From 9905bcf036db90378052c763fa087294e3f32b3d Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 24 May 2025 15:39:12 +0200 Subject: [PATCH 01/61] add submodul --- .gitmodules | 3 +++ modules/ModdingToolBase | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 modules/ModdingToolBase diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..87124a4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "modules/ModdingToolBase"] + path = modules/ModdingToolBase + url = https://github.com/AnakinRaW/ModdingToolBase diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase new file mode 160000 index 0000000..044d3cc --- /dev/null +++ b/modules/ModdingToolBase @@ -0,0 +1 @@ +Subproject commit 044d3ccad5f33e68c5f5737d273fbe1ed8fd6b88 From 7beeb3757ef4e5378064c7dc1b950839464ae8c2 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 24 May 2025 16:24:25 +0200 Subject: [PATCH 02/61] update deps --- .../PG.StarWarsGame.Engine.csproj | 14 ++++---------- .../PG.StarWarsGame.Files.ALO.csproj | 6 +----- .../PG.StarWarsGame.Files.XML.csproj | 8 ++++---- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index 6055649..8bb3a0a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -26,19 +26,13 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/PG.StarWarsGame.Files.ALO.csproj b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/PG.StarWarsGame.Files.ALO.csproj index 4fd3dd4..c653074 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/PG.StarWarsGame.Files.ALO.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/PG.StarWarsGame.Files.ALO.csproj @@ -16,11 +16,7 @@ snupkg - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj index 066e32d..71c3fed 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj @@ -18,12 +18,12 @@ - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - \ No newline at end of file From 47fd1b24e6d9f0ac1032e237ddcdee115fbc227d Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 24 May 2025 16:24:43 +0200 Subject: [PATCH 03/61] update deps --- src/ModVerify/ModVerify.csproj | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index fecceb2..c0a09df 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -27,16 +27,14 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - From e3995dd955fede92be1dd5f5213f002e60ee0235 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 24 May 2025 17:31:42 +0200 Subject: [PATCH 04/61] integrate moddingtoolbase code --- ModVerify.sln | 82 ++++++ modules/ModdingToolBase | 2 +- src/ModVerify.CliApp/ConsoleUtilities.cs | 94 ------- .../GameFinder/GameFinderService.cs | 6 +- .../ModSelectors/AutomaticModSelector.cs | 2 +- .../ModSelectors/ConsoleModSelector.cs | 3 +- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 41 +-- .../ModVerifyAppEnvironment.cs | 54 ++++ ...odVerifyApp.cs => ModVerifyApplication.cs} | 121 +++++++-- .../ModVerifyConsoleUtilities.cs | 22 ++ src/ModVerify.CliApp/ModVerifyConstants.cs | 13 + .../CommandLine/BaseModVerifyOptions.cs | 24 +- .../CommandLine/CreateBaselineVerbOption.cs | 2 +- .../Options/CommandLine/VerifyVerbOption.cs | 8 +- .../Options/GameInstallationsSettings.cs | 7 +- .../Options/ModVerifyAppSettings.cs | 2 + src/ModVerify.CliApp/Program.cs | 240 +++++++++--------- .../Properties/launchSettings.json | 2 +- src/ModVerify.CliApp/SettingsBuilder.cs | 34 +-- ...yUpdaterChecker.cs => ModVerifyUpdater.cs} | 4 +- 20 files changed, 463 insertions(+), 300 deletions(-) delete mode 100644 src/ModVerify.CliApp/ConsoleUtilities.cs create mode 100644 src/ModVerify.CliApp/ModVerifyAppEnvironment.cs rename src/ModVerify.CliApp/{ModVerifyApp.cs => ModVerifyApplication.cs} (54%) create mode 100644 src/ModVerify.CliApp/ModVerifyConsoleUtilities.cs create mode 100644 src/ModVerify.CliApp/ModVerifyConstants.cs rename src/ModVerify.CliApp/Updates/{ModVerifyUpdaterChecker.cs => ModVerifyUpdater.cs} (95%) diff --git a/ModVerify.sln b/ModVerify.sln index d09a64e..de26809 100644 --- a/ModVerify.sln +++ b/ModVerify.sln @@ -17,6 +17,34 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PG.StarWarsGame.Files.XML", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PG.StarWarsGame.Engine", "src\PetroglyphTools\PG.StarWarsGame.Engine\PG.StarWarsGame.Engine.csproj", "{DFD62F61-3455-44BE-BB7C-E954FF48534B}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AnakinApps", "AnakinApps", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ModdingToolBase", "ModdingToolBase", "{E6AFA947-48BE-416A-BEB1-C76E9D0843A8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApplicationBase", "modules\ModdingToolBase\src\AnakinApps\ApplicationBase\ApplicationBase.csproj", "{92A18973-0EB9-120E-7850-B37A24189DA8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApplicationBase.CommandLine", "modules\ModdingToolBase\src\AnakinApps\ApplicationBase.CommandLine\ApplicationBase.CommandLine.csproj", "{E06AF71E-5BC3-6837-4B6F-19A153B48327}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ApplicationBase.Shared", "modules\ModdingToolBase\src\AnakinApps\ApplicationBase.Shared\ApplicationBase.Shared.shproj", "{B297A13A-8E3A-436C-BA97-8B5F57827FFE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DeployTools", "DeployTools", "{749BC149-9DBD-46CA-B039-DAEA4C05FD97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApplicationManifestCreator", "modules\ModdingToolBase\src\AnakinApps\ApplicationManifestCreator\ApplicationManifestCreator.csproj", "{E8D2FB2D-8C9C-37D8-58AD-1A8F05E89999}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FtpUploader", "modules\ModdingToolBase\src\AnakinApps\FtpUploader\FtpUploader.csproj", "{4F70D564-DE7F-2101-EA21-B6B9398D13F2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UpdateFrameworks", "UpdateFrameworks", "{C535EAD3-87C0-4C85-BBF6-03A8B3A3E389}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppUpdaterFramework", "modules\ModdingToolBase\src\Updater\AppUpdaterFramework\AppUpdaterFramework.csproj", "{D7E1FA39-5484-B17A-2138-798858D5DA09}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppUpdaterFramework.Attributes", "modules\ModdingToolBase\src\Updater\AppUpdaterFramework.Attributes\AppUpdaterFramework.Attributes.csproj", "{7908FA18-0B16-B6E7-207F-077048A0CB63}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppUpdaterFramework.Manifest", "modules\ModdingToolBase\src\Updater\AppUpdaterFramework.Manifest\AppUpdaterFramework.Manifest.csproj", "{F1666AC0-2207-B91F-4369-7C8C38A4245D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExternalUpdater.App", "modules\ModdingToolBase\src\Updater\ExternalUpdater.App\ExternalUpdater.App.csproj", "{18C9D26C-6BBC-E3E0-25EF-D66762103B14}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExternalUpdater.Core", "modules\ModdingToolBase\src\Updater\ExternalUpdater.Core\ExternalUpdater.Core.csproj", "{D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -47,6 +75,42 @@ Global {DFD62F61-3455-44BE-BB7C-E954FF48534B}.Debug|Any CPU.Build.0 = Debug|Any CPU {DFD62F61-3455-44BE-BB7C-E954FF48534B}.Release|Any CPU.ActiveCfg = Release|Any CPU {DFD62F61-3455-44BE-BB7C-E954FF48534B}.Release|Any CPU.Build.0 = Release|Any CPU + {92A18973-0EB9-120E-7850-B37A24189DA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {92A18973-0EB9-120E-7850-B37A24189DA8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92A18973-0EB9-120E-7850-B37A24189DA8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {92A18973-0EB9-120E-7850-B37A24189DA8}.Release|Any CPU.Build.0 = Release|Any CPU + {E06AF71E-5BC3-6837-4B6F-19A153B48327}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E06AF71E-5BC3-6837-4B6F-19A153B48327}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E06AF71E-5BC3-6837-4B6F-19A153B48327}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E06AF71E-5BC3-6837-4B6F-19A153B48327}.Release|Any CPU.Build.0 = Release|Any CPU + {E8D2FB2D-8C9C-37D8-58AD-1A8F05E89999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8D2FB2D-8C9C-37D8-58AD-1A8F05E89999}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8D2FB2D-8C9C-37D8-58AD-1A8F05E89999}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8D2FB2D-8C9C-37D8-58AD-1A8F05E89999}.Release|Any CPU.Build.0 = Release|Any CPU + {4F70D564-DE7F-2101-EA21-B6B9398D13F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F70D564-DE7F-2101-EA21-B6B9398D13F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F70D564-DE7F-2101-EA21-B6B9398D13F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F70D564-DE7F-2101-EA21-B6B9398D13F2}.Release|Any CPU.Build.0 = Release|Any CPU + {D7E1FA39-5484-B17A-2138-798858D5DA09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7E1FA39-5484-B17A-2138-798858D5DA09}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7E1FA39-5484-B17A-2138-798858D5DA09}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7E1FA39-5484-B17A-2138-798858D5DA09}.Release|Any CPU.Build.0 = Release|Any CPU + {7908FA18-0B16-B6E7-207F-077048A0CB63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7908FA18-0B16-B6E7-207F-077048A0CB63}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7908FA18-0B16-B6E7-207F-077048A0CB63}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7908FA18-0B16-B6E7-207F-077048A0CB63}.Release|Any CPU.Build.0 = Release|Any CPU + {F1666AC0-2207-B91F-4369-7C8C38A4245D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1666AC0-2207-B91F-4369-7C8C38A4245D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1666AC0-2207-B91F-4369-7C8C38A4245D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1666AC0-2207-B91F-4369-7C8C38A4245D}.Release|Any CPU.Build.0 = Release|Any CPU + {18C9D26C-6BBC-E3E0-25EF-D66762103B14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18C9D26C-6BBC-E3E0-25EF-D66762103B14}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18C9D26C-6BBC-E3E0-25EF-D66762103B14}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18C9D26C-6BBC-E3E0-25EF-D66762103B14}.Release|Any CPU.Build.0 = Release|Any CPU + {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -56,8 +120,26 @@ Global {DF76A383-C94E-4D03-A07C-22D61ED37059} = {15F8B753-814A-406E-9147-EB048DADAC96} {418C68FA-531B-432E-8459-6433181C8AD3} = {15F8B753-814A-406E-9147-EB048DADAC96} {DFD62F61-3455-44BE-BB7C-E954FF48534B} = {15F8B753-814A-406E-9147-EB048DADAC96} + {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {E6AFA947-48BE-416A-BEB1-C76E9D0843A8} + {92A18973-0EB9-120E-7850-B37A24189DA8} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {E06AF71E-5BC3-6837-4B6F-19A153B48327} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {B297A13A-8E3A-436C-BA97-8B5F57827FFE} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {749BC149-9DBD-46CA-B039-DAEA4C05FD97} = {E6AFA947-48BE-416A-BEB1-C76E9D0843A8} + {E8D2FB2D-8C9C-37D8-58AD-1A8F05E89999} = {749BC149-9DBD-46CA-B039-DAEA4C05FD97} + {4F70D564-DE7F-2101-EA21-B6B9398D13F2} = {749BC149-9DBD-46CA-B039-DAEA4C05FD97} + {C535EAD3-87C0-4C85-BBF6-03A8B3A3E389} = {E6AFA947-48BE-416A-BEB1-C76E9D0843A8} + {D7E1FA39-5484-B17A-2138-798858D5DA09} = {C535EAD3-87C0-4C85-BBF6-03A8B3A3E389} + {7908FA18-0B16-B6E7-207F-077048A0CB63} = {C535EAD3-87C0-4C85-BBF6-03A8B3A3E389} + {F1666AC0-2207-B91F-4369-7C8C38A4245D} = {C535EAD3-87C0-4C85-BBF6-03A8B3A3E389} + {18C9D26C-6BBC-E3E0-25EF-D66762103B14} = {C535EAD3-87C0-4C85-BBF6-03A8B3A3E389} + {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030} = {C535EAD3-87C0-4C85-BBF6-03A8B3A3E389} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D74A22E2-91F1-4BC7-9630-3CF930B45408} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + modules\ModdingToolBase\src\AnakinApps\ApplicationBase.Shared\ApplicationBase.Shared.projitems*{4f70d564-de7f-2101-ea21-b6b9398d13f2}*SharedItemsImports = 5 + modules\ModdingToolBase\src\AnakinApps\ApplicationBase.Shared\ApplicationBase.Shared.projitems*{92a18973-0eb9-120e-7850-b37a24189da8}*SharedItemsImports = 5 + modules\ModdingToolBase\src\AnakinApps\ApplicationBase.Shared\ApplicationBase.Shared.projitems*{e8d2fb2d-8c9c-37d8-58ad-1a8f05e89999}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 044d3cc..661b95b 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 044d3ccad5f33e68c5f5737d273fbe1ed8fd6b88 +Subproject commit 661b95b1b21d766120af90be78c2bac7869bd1cf diff --git a/src/ModVerify.CliApp/ConsoleUtilities.cs b/src/ModVerify.CliApp/ConsoleUtilities.cs deleted file mode 100644 index db3e741..0000000 --- a/src/ModVerify.CliApp/ConsoleUtilities.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; - -namespace AET.ModVerifyTool; - -internal static class ConsoleUtilities -{ - public delegate bool ConsoleQuestionValueFactory(string input, out T value); - - public static void WriteHorizontalLine(char lineChar = '─', int length = 20) - { - var line = new string(lineChar, length); - Console.WriteLine(line); - } - - public static void WriteHeader() - { - Console.WriteLine("***********************************"); - Console.WriteLine("***********************************"); - Console.WriteLine(Figgle.FiggleFonts.Standard.Render("Mod Verify")); - Console.WriteLine("***********************************"); - Console.WriteLine("***********************************"); - Console.WriteLine(" by AnakinRaW"); - Console.WriteLine(); - Console.WriteLine(); - } - - public static void WriteApplicationFailure() - { - Console.WriteLine(); - WriteHorizontalLine('*'); - Console.ForegroundColor = ConsoleColor.DarkRed; - Console.WriteLine(" ModVerify Failure! "); - Console.ResetColor(); - WriteHorizontalLine('*'); - Console.WriteLine(); - Console.WriteLine("The application encountered an unexpected error and will terminate now!"); - Console.WriteLine(); - } - - public static T UserQuestionOnSameLine(string question, ConsoleQuestionValueFactory inputCorrect) - { - while (true) - { - var promptLeft = 0; - var promptTop = Console.CursorTop; - - Console.SetCursorPosition(promptLeft, promptTop); - Console.Write(question); - Console.SetCursorPosition(promptLeft + question.Length, promptTop); - - var input = ReadLineInline(); - - if (!inputCorrect(input, out var result)) - { - Console.SetCursorPosition(0, promptTop); - Console.Write(new string(' ', Console.WindowWidth - 1)); - continue; - } - - Console.WriteLine(); - return result; - } - } - - private static string ReadLineInline() - { - var input = ""; - while (true) - { - var key = Console.ReadKey(intercept: true); - - if (key.Key == ConsoleKey.Enter) - break; - - if (key.Key == ConsoleKey.Backspace) - { - if (input.Length > 0) - { - input = input[..^1]; - Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop); - Console.Write(' '); - Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop); - } - } - else if (!char.IsControl(key.KeyChar)) - { - input += key.KeyChar; - Console.Write(key.KeyChar); - } - } - - return input; - } -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs index 78fe408..3845156 100644 --- a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs +++ b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs @@ -97,7 +97,8 @@ private GameFinderResult FindGames(IList detectors) if (result.GameLocation is null) throw new GameNotFoundException("Unable to find game installation: Wrong install path?"); - _logger?.LogInformation($"Found game installation: {result.GameIdentity} at {result.GameLocation.FullName}"); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, + $"Found game installation: {result.GameIdentity} at {result.GameLocation.FullName}"); var game = _gameFactory.CreateGame(result, CultureInfo.InvariantCulture); @@ -118,7 +119,8 @@ private GameFinderResult FindGames(IList detectors) if (!TryDetectGame(GameType.Eaw, fallbackDetectors, out var fallbackResult) || fallbackResult.GameLocation is null) throw new GameNotFoundException("Unable to find fallback game installation: Wrong install path?"); - _logger?.LogInformation($"Found fallback game installation: {fallbackResult.GameIdentity} at {fallbackResult.GameLocation.FullName}"); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, + $"Found fallback game installation: {fallbackResult.GameIdentity} at {fallbackResult.GameLocation.FullName}"); fallbackGame = _gameFactory.CreateGame(fallbackResult, CultureInfo.InvariantCulture); diff --git a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs index 00fc4ae..26648a4 100644 --- a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs @@ -37,7 +37,7 @@ internal class AutomaticModSelector(IServiceProvider serviceProvider) : ModSelec } catch (GameNotFoundException) { - Logger?.LogError($"Unable to find games based of the given location '{settings.GamePath}'. Consider specifying all paths manually."); + Logger?.LogError(ModVerifyConstants.ConsoleEventId, $"Unable to find games based of the given location '{settings.GamePath}'. Consider specifying all paths manually."); targetObject = null!; return null; } diff --git a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs index 7a29368..306f9e2 100644 --- a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs @@ -3,6 +3,7 @@ using AET.Modinfo.Spec; using AET.ModVerifyTool.GameFinder; using AET.ModVerifyTool.Options; +using AnakinRaW.ApplicationBase; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; using PG.StarWarsGame.Infrastructure.Games; @@ -100,7 +101,7 @@ private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult fin if (!int.TryParse(input, out value)) return false; - return value <= list.Count; + return value <= list.Count && value >= 0; }); return list[selected]; } diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 3ca41b6..7cd3c57 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -1,7 +1,7 @@  - net9.0;net48 + net9.0;net481 Exe AET.ModVerifyTool ModVerify @@ -25,39 +25,35 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - @@ -65,6 +61,11 @@ + + + + + diff --git a/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs b/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs new file mode 100644 index 0000000..9fcbfeb --- /dev/null +++ b/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Reflection; +using AnakinRaW.ApplicationBase.Environment; +using AnakinRaW.AppUpdaterFramework.Configuration; +using AnakinRaW.CommonUtilities.DownloadManager.Configuration; + +namespace AET.ModVerifyTool; + +internal sealed class ModVerifyAppEnvironment(Assembly assembly, IFileSystem fileSystem) +#if NET + : ApplicationEnvironment(assembly, fileSystem) +#else + : UpdatableApplicationEnvironment(assembly, fileSystem) +#endif +{ + public override string ApplicationName => ModVerifyConstants.AppNameString; + + protected override string ApplicationLocalDirectoryName => ModVerifyConstants.ModVerifyToolPath; + +#if NETFRAMEWORK + + public override ICollection UpdateMirrors { get; } = new List + { + new($"https://republicatwar.com/downloads/{ModVerifyConstants.ModVerifyToolPath}") + }; + + public override string UpdateRegistryPath => $@"SOFTWARE\{ModVerifyConstants.ModVerifyToolPath}\Update"; + + protected override UpdateConfiguration CreateUpdateConfiguration() + { + return new UpdateConfiguration + { + DownloadLocation = FileSystem.Path.Combine(ApplicationLocalPath, "downloads"), + BackupLocation = FileSystem.Path.Combine(ApplicationLocalPath, "backups"), + BackupPolicy = BackupPolicy.Required, + DownloadConfiguration = new DownloadManagerConfiguration + { + AllowEmptyFileDownload = false, + DownloadRetryDelay = 500, + ValidationPolicy = ValidationPolicy.Required + }, + DownloadRetryCount = 3, + RestartConfiguration = new UpdateRestartConfiguration + { + SupportsRestart = true, + PassCurrentArgumentsForRestart = true + }, + ValidateInstallation = true + }; + } +#endif +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModVerifyApp.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs similarity index 54% rename from src/ModVerify.CliApp/ModVerifyApp.cs rename to src/ModVerify.CliApp/ModVerifyApplication.cs index 5cefc40..29a9560 100644 --- a/src/ModVerify.CliApp/ModVerifyApp.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -1,9 +1,12 @@ -using AET.ModVerify; -using AET.ModVerify.Reporting; -using AET.ModVerifyTool.ModSelectors; +using AET.ModVerifyTool.ModSelectors; using AET.ModVerifyTool.Options; +using AET.ModVerifyTool.Reporting; +using AET.ModVerifyTool.Updates; +using AnakinRaW.ApplicationBase; +using AnakinRaW.ApplicationBase.Utilities; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Serilog; using System; using System.Collections.Generic; using System.IO; @@ -11,25 +14,72 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using AET.ModVerify; using AET.ModVerify.Pipeline; -using AET.ModVerifyTool.Reporting; +using AET.ModVerify.Reporting; using PG.StarWarsGame.Engine; +using ILogger = Microsoft.Extensions.Logging.ILogger; namespace AET.ModVerifyTool; -internal class ModVerifyApp(ModVerifyAppSettings settings, IServiceProvider services) +internal sealed class ModVerifyApplication(ModVerifyAppSettings settings, IServiceProvider services) { - private readonly ILogger? _logger = services.GetService()?.CreateLogger(typeof(ModVerifyApp)); + private readonly ILogger? _logger = services.GetService()?.CreateLogger(typeof(ModVerifyApplication)); private readonly IFileSystem _fileSystem = services.GetRequiredService(); - public async Task RunApplication() + public async Task Run() + { + using (new UnhandledExceptionHandler(services)) + using (new UnobservedTaskExceptionHandler(services)) + return await RunCore().ConfigureAwait(false); + } + + private async Task RunCore() + { + _logger?.LogDebug($"Raw command line: {Environment.CommandLine}"); + + var interactive = false; + try + { + interactive = settings.Interactive; + + if (!settings.Offline) + await CheckForUpdate().ConfigureAwait(false); + + return await RunVerify().ConfigureAwait(false); + } + catch (Exception e) + { + _logger?.LogCritical(e, e.Message); + ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, e); + return e.HResult; + } + finally + { +#if NET + await Log.CloseAndFlushAsync(); +#else + Log.CloseAndFlush(); +#endif + if (interactive) + { + Console.WriteLine(); + ConsoleUtilities.WriteHorizontalLine('-'); + Console.WriteLine("Press any key to exit"); + Console.ReadLine(); + } + } + } + + + private async Task RunVerify() { var installData = new SettingsBasedModSelector(services) .CreateInstallationDataFromSettings(settings.GameInstallationsSettings); _logger?.LogDebug($"Verify install data: {installData}"); _logger?.LogTrace($"Verify settings: {settings}"); - + var allErrors = await Verify(installData).ConfigureAwait(false); try @@ -40,12 +90,12 @@ public async Task RunApplication() { return e.HResult; } - + if (!settings.CreateNewBaseline) return 0; await WriteBaseline(allErrors, settings.NewBaselinePath).ConfigureAwait(false); - _logger?.LogInformation("Baseline successfully created."); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Baseline successfully created."); return 0; } @@ -64,7 +114,7 @@ private async Task> Verify(VerifyInstalla try { - _logger?.LogInformation($"Creating Game Engine '{installInformation.EngineType}'"); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Creating Game Engine '{installInformation.EngineType}'"); gameEngine = await gameEngineService.InitializeAsync( installInformation.EngineType, installInformation.GameLocations, @@ -72,7 +122,7 @@ private async Task> Verify(VerifyInstalla initProgress, false, CancellationToken.None).ConfigureAwait(false); - _logger?.LogInformation("Game Engine created"); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Game Engine created"); } finally { @@ -84,7 +134,7 @@ private async Task> Verify(VerifyInstalla _logger?.LogError(e, $"Creating game engine failed: {e.Message}"); throw; } - + var progressReporter = new VerifyConsoleProgressReporter(installInformation.Name); using var verifyPipeline = new GameVerifyPipeline( @@ -99,7 +149,7 @@ private async Task> Verify(VerifyInstalla { try { - _logger?.LogInformation($"Verifying '{installInformation.Name}'..."); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Verifying '{installInformation.Name}'..."); await verifyPipeline.RunAsync().ConfigureAwait(false); progressReporter.Report(string.Empty, 1.0); } @@ -115,7 +165,7 @@ private async Task> Verify(VerifyInstalla } catch (OperationCanceledException) { - _logger?.LogWarning("Verification stopped due to enabled failFast setting."); + _logger?.LogWarning(ModVerifyConstants.ConsoleEventId, "Verification stopped due to enabled failFast setting."); } catch (Exception e) { @@ -123,14 +173,14 @@ private async Task> Verify(VerifyInstalla throw; } - _logger?.LogInformation("Finished verification"); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Finished verification"); return verifyPipeline.FilteredErrors; } private async Task ReportErrors(IReadOnlyCollection errors) { - _logger?.LogInformation("Reporting Errors..."); - + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Reporting Errors..."); + var reportBroker = new VerificationReportBroker(services); await reportBroker.ReportAsync(errors); @@ -138,13 +188,13 @@ private async Task ReportErrors(IReadOnlyCollection errors) if (errors.Any(x => x.Severity >= settings.AppThrowsOnMinimumSeverity)) throw new GameVerificationException(errors); } - + private async Task WriteBaseline(IEnumerable errors, string baselineFile) { var baseline = new VerificationBaseline(settings.GlobalReportSettings.MinimumReportSeverity, errors); var fullPath = _fileSystem.Path.GetFullPath(baselineFile); - _logger?.LogInformation($"Writing Baseline to '{fullPath}'"); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Writing Baseline to '{fullPath}'"); #if NET await @@ -152,4 +202,35 @@ private async Task WriteBaseline(IEnumerable errors, string b using var fs = _fileSystem.FileStream.New(fullPath, FileMode.Create, FileAccess.Write, FileShare.None); await baseline.ToJsonAsync(fs); } + + + private async Task CheckForUpdate() + { + var updateChecker = new ModVerifyUpdater(services); + + _logger?.LogDebug("Checking for available update"); + + try + { + var updateInfo = await updateChecker.CheckForUpdateAsync().ConfigureAwait(false); + if (updateInfo.IsUpdateAvailable) + { + ConsoleUtilities.WriteHorizontalLine(); + + Console.ForegroundColor = ConsoleColor.DarkGreen; + Console.WriteLine("New Update Available!"); + Console.ResetColor(); + + Console.WriteLine($"Version: {updateInfo.NewVersion}, Download here: {updateInfo.DownloadLink}"); + ConsoleUtilities.WriteHorizontalLine(); + Console.WriteLine(); + + } + } + catch (Exception e) + { + _logger?.LogWarning(ModVerifyConstants.ConsoleEventId, $"Unable to check for updates due to an internal error: {e.Message}"); + _logger?.LogTrace(e, "Checking for update failed: " + e.Message); + } + } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModVerifyConsoleUtilities.cs b/src/ModVerify.CliApp/ModVerifyConsoleUtilities.cs new file mode 100644 index 0000000..8bca1e8 --- /dev/null +++ b/src/ModVerify.CliApp/ModVerifyConsoleUtilities.cs @@ -0,0 +1,22 @@ +using System; +using AnakinRaW.ApplicationBase; + +namespace AET.ModVerifyTool; + +internal static class ModVerifyConsoleUtilities +{ + public static void WriteHeader() + { + const int lineLength = 50; + const string author = "by AnakinRaW"; + + ConsoleUtilities.WriteHorizontalLine('*', lineLength); + + Console.WriteLine(Figgle.FiggleFonts.Standard.Render(ModVerifyConstants.AppNameString)); + ConsoleUtilities.WriteHorizontalLine('*', lineLength); + + Console.WriteLine(new string(' ', lineLength - author.Length)+ author); + Console.WriteLine(); + Console.WriteLine(); + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModVerifyConstants.cs b/src/ModVerify.CliApp/ModVerifyConstants.cs new file mode 100644 index 0000000..9bd2507 --- /dev/null +++ b/src/ModVerify.CliApp/ModVerifyConstants.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.Logging; + +namespace AET.ModVerifyTool; + +internal static class ModVerifyConstants +{ + public const string AppNameString = "AET Mod Verify"; + public const string ModVerifyToolId = "AET.ModVerify"; + public const string ModVerifyToolPath = "ModVerify"; + public const int ConsoleEventIdValue = 1138; + + public static readonly EventId ConsoleEventId = new(ConsoleEventIdValue, "LogToConsole"); +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs b/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs index d239112..3863314 100644 --- a/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs +++ b/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs @@ -8,52 +8,52 @@ namespace AET.ModVerifyTool.Options.CommandLine; internal abstract class BaseModVerifyOptions { [Option('v', "verbose", Required = false, HelpText = "Sets output to verbose messages.")] - public bool Verbose { get; set; } + public bool Verbose { get; init; } [Option("offline", Default = false, HelpText = "When set, the application will work in offline mode and does not need an Internet connection.")] - public bool OfflineMode { get; set; } + public bool OfflineMode { get; init; } [Option("minSeverity", Required = false, Default = VerificationSeverity.Information, HelpText = "When set, only findings with at least the specified severity value are processed.")] - public VerificationSeverity MinimumSeverity { get; set; } + public VerificationSeverity MinimumSeverity { get; init; } [Option("suppressions", Required = false, HelpText = "Path to a JSON suppression file.")] - public string? Suppressions { get; set; } + public string? Suppressions { get; init; } [Option("path", SetName = "autoDetection", Required = false, Default = null, - HelpText = "Specifies the path to verify. The path may be a game or mod. The application will try to find all necessary submods or base games itself. " + + HelpText = "Specifies the path to verify. The path may be a game or mod. The application will try to find all necessary sub-mods or base games itself. " + "The argument cannot be combined with any of --mods, --game or --fallbackGame")] - public string? AutoPath { get; set; } + public string? AutoPath { get; init; } [Option("mods", SetName = "manualPaths", Required = false, Default = null, Separator = ';', HelpText = "The path of the mod to verify. To support submods, multiple paths can be separated using the ';' (semicolon) character. " + "Leave empty, if you want to verify a game. If you want to use the interactive mode, leave this, --game and --fallbackGame empty.")] - public IList? ModPaths { get; set; } + public IList? ModPaths { get; init; } [Option("game", SetName = "manualPaths", Required = false, Default = null, HelpText = "The path of the base game. For FoC mods this points to the FoC installation, for EaW mods this points to the EaW installation. " + "Leave empty, if you want to auto-detect games. If you want to use the interactive mode, leave this, --mods and --fallbackGame empty. " + "If this argument is set, you also need to set --mods (including sub mods) and --fallbackGame manually.")] - public string? GamePath { get; set; } + public string? GamePath { get; init; } [Option("fallbackGame", SetName = "manualPaths", Required = false, Default = null, HelpText = "The path of the fallback game. Usually this points to the EaW installation. This argument only recognized if --game is set.")] - public string? FallbackGamePath { get; set; } + public string? FallbackGamePath { get; init; } [Option("type", Required = false, Default = null, HelpText = "The game type of the mod that shall be verified. Skip this value to auto-determine the type. Valid values are 'Eaw' and 'Foc'. " + "This argument is required, if the first mod of '--mods' points to a directory outside of the common folder hierarchy (e.g, /MODS/MOD_NAME or /32470/WORKSHOP_ID")] - public GameEngineType? GameType { get; set; } + public GameEngineType? GameType { get; init; } [Option("additionalFallbackPaths", Required = false, Separator = ';', HelpText = "Additional fallback paths, which may contain assets that shall be included when doing the verification. Do not add EaW here. " + "Multiple paths can be separated using the ';' (semicolon) character.")] - public IList? AdditionalFallbackPath { get; set; } + public IList? AdditionalFallbackPath { get; init; } [Option("parallel", Default = false, HelpText = "When set, game verifiers will run in parallel. " + "While this may reduce analysis time, console output might be harder to read.")] - public bool Parallel { get; set; } + public bool Parallel { get; init; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Options/CommandLine/CreateBaselineVerbOption.cs b/src/ModVerify.CliApp/Options/CommandLine/CreateBaselineVerbOption.cs index 78132cc..e957da9 100644 --- a/src/ModVerify.CliApp/Options/CommandLine/CreateBaselineVerbOption.cs +++ b/src/ModVerify.CliApp/Options/CommandLine/CreateBaselineVerbOption.cs @@ -6,5 +6,5 @@ namespace AET.ModVerifyTool.Options.CommandLine; internal class CreateBaselineVerbOption : BaseModVerifyOptions { [Option('o', "outFile", Required = true, HelpText = "The file path of the new baseline file.")] - public string OutputFile { get; set; } + public required string OutputFile { get; init; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Options/CommandLine/VerifyVerbOption.cs b/src/ModVerify.CliApp/Options/CommandLine/VerifyVerbOption.cs index 517ceda..5827fd2 100644 --- a/src/ModVerify.CliApp/Options/CommandLine/VerifyVerbOption.cs +++ b/src/ModVerify.CliApp/Options/CommandLine/VerifyVerbOption.cs @@ -7,11 +7,11 @@ namespace AET.ModVerifyTool.Options.CommandLine; internal class VerifyVerbOption : BaseModVerifyOptions { [Option('o', "outDir", Required = false, HelpText = "Directory where result files shall be stored to.")] - public string? OutputDirectory { get; set; } + public string? OutputDirectory { get; init; } [Option("failFast", Required = false, Default = false, HelpText = "When set, the application will abort on the first failure. The option also recognized the 'MinimumFailureSeverity' setting.")] - public bool FailFast { get; set; } + public bool FailFast { get; init; } [Option("minFailSeverity", Required = false, Default = null, HelpText = "When set, the application return with an error, if any finding has at least the specified severity value.")] @@ -19,8 +19,8 @@ internal class VerifyVerbOption : BaseModVerifyOptions [Option("ignoreAsserts", Required = false, HelpText = "When this flag is present, the application will not report engine assertions.")] - public bool IgnoreAsserts { get; set; } + public bool IgnoreAsserts { get; init; } [Option("baseline", Required = false, HelpText = "Path to a JSON baseline file.")] - public string? Baseline { get; set; } + public string? Baseline { get; init; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Options/GameInstallationsSettings.cs b/src/ModVerify.CliApp/Options/GameInstallationsSettings.cs index 667e2cf..58223c2 100644 --- a/src/ModVerify.CliApp/Options/GameInstallationsSettings.cs +++ b/src/ModVerify.CliApp/Options/GameInstallationsSettings.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using PG.StarWarsGame.Engine; @@ -17,13 +16,13 @@ internal record GameInstallationsSettings public string? AutoPath { get; init; } - public IList ModPaths { get; init; } = Array.Empty(); + public IList ModPaths { get; init; } = []; public string? GamePath { get; init; } public string? FallbackGamePath { get; init; } - public IList AdditionalFallbackPaths { get; init; } = Array.Empty(); + public IList AdditionalFallbackPaths { get; init; } = []; public GameEngineType? EngineType { get; init; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs b/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs index 6a3b0bd..a1b0752 100644 --- a/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs +++ b/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs @@ -25,4 +25,6 @@ internal sealed class ModVerifyAppSettings public string? NewBaselinePath { get; init; } public bool Offline { get; init; } + + public bool VerboseMode { get; init; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 3ac92ba..6d23bec 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -6,8 +6,11 @@ using AET.ModVerify.Reporting.Settings; using AET.ModVerifyTool.Options; using AET.ModVerifyTool.Options.CommandLine; -using AET.ModVerifyTool.Updates; using AET.SteamAbstraction; +using AnakinRaW.ApplicationBase; +using AnakinRaW.ApplicationBase.Environment; +using AnakinRaW.ApplicationBase.Update; +using AnakinRaW.AppUpdaterFramework.Json; using AnakinRaW.CommonUtilities.Hashing; using AnakinRaW.CommonUtilities.Registry; using AnakinRaW.CommonUtilities.Registry.Windows; @@ -31,164 +34,142 @@ using Serilog.Filters; using Serilog.Sinks.SystemConsole.Themes; using System; +using System.Collections.Generic; using System.IO.Abstractions; +using System.Runtime.InteropServices; using System.Threading.Tasks; +using Serilog.Expressions; using Testably.Abstractions; using ILogger = Serilog.ILogger; namespace AET.ModVerifyTool; -internal class Program +internal class Program : SelfUpdateableAppLifecycle { private static readonly string EngineParserNamespace = typeof(XmlObjectParser<>).Namespace!; private static readonly string ParserNamespace = typeof(PetroglyphXmlFileParser<>).Namespace!; private static readonly string ModVerifyRootNameSpace = typeof(Program).Namespace!; + private static readonly CompiledExpression PrintToConsoleExpression = SerilogExpression.Compile($"EventId.Id = {ModVerifyConstants.ConsoleEventIdValue}"); + + private static ModVerifyAppSettings _modVerifySettings = null!; private static async Task Main(string[] args) { - ConsoleUtilities.WriteHeader(); - - var result = 0; - - Type[] programVerbs = - [ - typeof(VerifyVerbOption), - typeof(CreateBaselineVerbOption), - ]; - - var parseResult = Parser.Default.ParseArguments(args, programVerbs); - - await parseResult.WithParsedAsync(async o => - { - result = await Run((BaseModVerifyOptions)o); - }); - await parseResult.WithNotParsedAsync(e => - { - Console.WriteLine(HelpText.AutoBuild(parseResult).ToString()); - result = 0xA0; - return Task.CompletedTask; - }); - - return result; + ModVerifyConsoleUtilities.WriteHeader(); + return await new Program().StartAsync(args); } - private static async Task Run(BaseModVerifyOptions options) + protected override async Task InitializeAppAsync(IReadOnlyList args) { - var coreServiceCollection = CreateCoreServices(options.Verbose); - var coreServices = coreServiceCollection.BuildServiceProvider(); - var logger = coreServices.GetService()?.CreateLogger(typeof(Program)); - - logger?.LogDebug($"Raw command line: {Environment.CommandLine}"); + await base.InitializeAppAsync(args); - var interactive = false; try { - var settings = new SettingsBuilder(coreServices).BuildSettings(options); - interactive = settings.Interactive; - var services = CreateAppServices(coreServiceCollection, settings); - - if (!settings.Offline) - await CheckForUpdate(services, logger); - - var verifier = new ModVerifyApp(settings, services); - return await verifier.RunApplication().ConfigureAwait(false); + var settings = ParseSettings(args); + if (settings is null) + return 0xA0; + + _modVerifySettings = settings; + return 0; } catch (Exception e) { - ConsoleUtilities.WriteApplicationFailure(); - logger?.LogCritical(e, e.Message); + Logger?.LogCritical(e, $"Failed to create settings form commandline arguments: {e.Message}"); + ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, e); return e.HResult; } - finally + } + + protected override void CreateAppServices(IServiceCollection services, IReadOnlyList args) + { + base.CreateAppServices(services, args); + + services.AddLogging(ConfigureLogging); + + services.AddSingleton(sp => new HashingService(sp)); + + + if (IsUpdateableApplication) { #if NET - await Log.CloseAndFlushAsync(); -#else - Log.CloseAndFlush(); + throw new NotSupportedException(); #endif - if (interactive) - { - Console.WriteLine(); - ConsoleUtilities.WriteHorizontalLine('-'); - Console.WriteLine("Press any key to exit"); - Console.ReadLine(); - } + services.MakeAppUpdateable( + UpdatableApplicationEnvironment, + sp => new CosturaApplicationProductService(ApplicationEnvironment, sp), + sp => new JsonManifestLoader(sp)); } - } - private static async Task CheckForUpdate(IServiceProvider services, Microsoft.Extensions.Logging.ILogger? logger) - { - var updateChecker = new ModVerifyUpdaterChecker(services); + SteamAbstractionLayer.InitializeServices(services); + PetroglyphGameInfrastructure.InitializeServices(services); - logger?.LogDebug("Checking for available update"); + services.SupportMTD(); + services.SupportMEG(); + services.SupportALO(); + services.SupportXML(); + PetroglyphCommons.ContributeServices(services); - try + PetroglyphEngineServiceContribution.ContributeServices(services); + services.RegisterVerifierCache(); + + + SetupVerifyReporting(services, _modVerifySettings); + + if (_modVerifySettings.Offline) { - var updateInfo = await updateChecker.CheckForUpdateAsync().ConfigureAwait(false); - if (updateInfo.IsUpdateAvailable) - { - ConsoleUtilities.WriteHorizontalLine(); - - Console.ForegroundColor = ConsoleColor.DarkGreen; - Console.WriteLine("New Update Available!"); - Console.ResetColor(); - - Console.WriteLine($"Version: {updateInfo.NewVersion}, Download here: {updateInfo.DownloadLink}"); - ConsoleUtilities.WriteHorizontalLine(); - Console.WriteLine(); - - } + services.AddSingleton(sp => new OfflineModNameResolver(sp)); + services.AddSingleton(sp => new OfflineModGameTypeResolver(sp)); } - catch(Exception e) + else { - logger?.LogWarning($"Unable to check for updates due to an internal error: {e.Message}"); - logger?.LogTrace(e, "Checking for update failed: " + e.Message); + services.AddSingleton(sp => new OnlineModNameResolver(sp)); + services.AddSingleton(sp => new OnlineModGameTypeResolver(sp)); } } - private static IServiceCollection CreateCoreServices(bool verboseLogging) + private ModVerifyAppSettings? ParseSettings(IReadOnlyList args) { - var fileSystem = new RealFileSystem(); - var serviceCollection = new ServiceCollection(); + Type[] programVerbs = + [ + typeof(VerifyVerbOption), + typeof(CreateBaselineVerbOption), + ]; - serviceCollection.AddSingleton(new WindowsRegistry()); - serviceCollection.AddSingleton(fileSystem); + var parseResult = Parser.Default.ParseArguments(args, programVerbs); - serviceCollection.AddLogging(builder => ConfigureLogging(builder, fileSystem, verboseLogging)); + ModVerifyAppSettings? settings = null; - return serviceCollection; - } + parseResult.WithParsed(o => settings = BuildSettings(o)); - private static IServiceProvider CreateAppServices(IServiceCollection serviceCollection, ModVerifyAppSettings settings) - { - serviceCollection.AddSingleton(sp => new HashingService(sp)); + parseResult.WithNotParsed(_ => + { + Logger?.LogError("Unable to parse command line"); + Console.WriteLine(HelpText.AutoBuild(parseResult).ToString()); + }); - SteamAbstractionLayer.InitializeServices(serviceCollection); - PetroglyphGameInfrastructure.InitializeServices(serviceCollection); + return settings; + } - serviceCollection.SupportMTD(); - serviceCollection.SupportMEG(); - serviceCollection.SupportALO(); - serviceCollection.SupportXML(); - PetroglyphCommons.ContributeServices(serviceCollection); + protected override ApplicationEnvironment CreateAppEnvironment() + { + return new ModVerifyAppEnvironment(typeof(Program).Assembly, FileSystem); + } - PetroglyphEngineServiceContribution.ContributeServices(serviceCollection); - serviceCollection.RegisterVerifierCache(); + protected override IFileSystem CreateFileSystem() + { + return new RealFileSystem(); + } - SetupVerifyReporting(serviceCollection, settings); + protected override IRegistry CreateRegistry() + { + return !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? new InMemoryRegistry(InMemoryRegistryCreationFlags.WindowsLike) + : new WindowsRegistry(); + } - if (settings.Offline) - { - serviceCollection.AddSingleton(sp => new OfflineModNameResolver(sp)); - serviceCollection.AddSingleton(sp => new OfflineModGameTypeResolver(sp)); - } - else - { - serviceCollection.AddSingleton(sp => new OnlineModNameResolver(sp)); - serviceCollection.AddSingleton(sp => new OnlineModGameTypeResolver(sp)); - } - - return serviceCollection.BuildServiceProvider(); + protected override async Task RunAppAsync(string[] args, IServiceProvider appServiceProvider) + { + return await new ModVerifyApplication(_modVerifySettings, appServiceProvider).Run().ConfigureAwait(false); } private static void SetupVerifyReporting(IServiceCollection serviceCollection, ModVerifyAppSettings settings) @@ -215,7 +196,12 @@ private static void SetupVerifyReporting(IServiceCollection serviceCollection, M }); } - private static void ConfigureLogging(ILoggingBuilder loggingBuilder, IFileSystem fileSystem, bool verbose) + private ModVerifyAppSettings BuildSettings(BaseModVerifyOptions options) + { + return new SettingsBuilder(FileSystem, BootstrapLoggerFactory).BuildSettings(options); + } + + private void ConfigureLogging(ILoggingBuilder loggingBuilder) { loggingBuilder.ClearProviders(); @@ -226,13 +212,13 @@ private static void ConfigureLogging(ILoggingBuilder loggingBuilder, IFileSystem loggingBuilder.AddDebug(); #endif - if (verbose) + if (_modVerifySettings.VerboseMode) { logLevel = LogEventLevel.Verbose; loggingBuilder.AddDebug(); } - var fileLogger = SetupFileLogging(fileSystem, logLevel); + var fileLogger = SetupFileLogging(logLevel); loggingBuilder.AddSerilog(fileLogger); var cLogger = new LoggerConfiguration() @@ -240,22 +226,36 @@ private static void ConfigureLogging(ILoggingBuilder loggingBuilder, IFileSystem logLevel, theme: AnsiConsoleTheme.Code, outputTemplate: "[{Level:u3}] {Message:lj}{NewLine}{Exception}") + .MinimumLevel.Is(logLevel) .Filter.ByIncludingOnly(x => { - if (!x.Properties.TryGetValue("SourceContext", out var value)) + // Fatal errors are handled by a global exception handler + if (x.Level == LogEventLevel.Fatal) + return false; + + // Verbose should print everything we get + if (logLevel == LogEventLevel.Verbose) return true; - - var source = value.ToString().AsSpan().Trim('\"'); - return source.StartsWith(ModVerifyRootNameSpace.AsSpan()); + // Debug should print everything that has something to do with ModVerify + if (logLevel == LogEventLevel.Debug) + { + if (!x.Properties.TryGetValue("SourceContext", out var value)) + return false; + var source = value.ToString().AsSpan().Trim('\"'); + return source.StartsWith(ModVerifyRootNameSpace.AsSpan()); + } + + // In normal operation, we only print logs, which have the print-to-console EventId set. + return ExpressionResult.IsTrue(PrintToConsoleExpression(x)); }) .CreateLogger(); loggingBuilder.AddSerilog(cLogger); } - private static ILogger SetupFileLogging(IFileSystem fileSystem, LogEventLevel minLevel) + private ILogger SetupFileLogging(LogEventLevel minLevel) { - var logPath = fileSystem.Path.Combine(fileSystem.Path.GetTempPath(), "ModVerify_log.txt"); + var logPath = FileSystem.Path.Combine(ApplicationEnvironment.ApplicationLocalPath, "ModVerify_log.txt"); return new LoggerConfiguration() .Enrich.FromLogContext() diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index 1e01ece..283181a 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Interactive Verify": { "commandName": "Project", - "commandLineArgs": "verify -o verifyResults --minFailSeverity Information -v --baseline focBaseline.json --offline" + "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --baseline focBaseline.json --offline" }, "Interactive Baseline": { "commandName": "Project", diff --git a/src/ModVerify.CliApp/SettingsBuilder.cs b/src/ModVerify.CliApp/SettingsBuilder.cs index d2ba861..6386f9e 100644 --- a/src/ModVerify.CliApp/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/SettingsBuilder.cs @@ -2,7 +2,6 @@ using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Settings; using AET.ModVerifyTool.Options; -using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.IO; @@ -13,11 +12,9 @@ namespace AET.ModVerifyTool; -internal sealed class SettingsBuilder(IServiceProvider services) +internal sealed class SettingsBuilder(IFileSystem fileSystem, ILoggerFactory? loggerFactory) { - private readonly IFileSystem _fileSystem = services.GetRequiredService(); - private readonly ILogger? _logger = - services.GetRequiredService()?.CreateLogger(typeof(SettingsBuilder)); + private readonly ILogger? _logger = loggerFactory?.CreateLogger(typeof(SettingsBuilder)); public ModVerifyAppSettings BuildSettings(BaseModVerifyOptions options) { @@ -37,7 +34,7 @@ private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) var outDir = verifyOptions.OutputDirectory; if (!string.IsNullOrEmpty(outDir)) - output = _fileSystem.Path.GetFullPath(_fileSystem.Path.Combine(Environment.CurrentDirectory, outDir!)); + output = fileSystem.Path.GetFullPath(fileSystem.Path.Combine(Environment.CurrentDirectory, outDir!)); return new ModVerifyAppSettings { @@ -56,7 +53,8 @@ private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) GameInstallationsSettings = BuildInstallationSettings(verifyOptions), GlobalReportSettings = BuilderGlobalReportSettings(verifyOptions), ReportOutput = output, - Offline = verifyOptions.OfflineMode + Offline = verifyOptions.OfflineMode, + VerboseMode = verifyOptions.Verbose }; VerificationSeverity? GetVerifierMinimumThrowSeverity() @@ -66,8 +64,9 @@ private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) { if (minFailSeverity == null) { - _logger?.LogWarning($"Verification is configured to fail fast but 'minFailSeverity' is not specified. " + - $"Using severity '{VerificationSeverity.Information}'."); + _logger?.LogWarning(ModVerifyConstants.ConsoleEventId, + $"Verification is configured to fail fast but 'minFailSeverity' is not specified. " + + $"Using severity '{VerificationSeverity.Information}'."); minFailSeverity = VerificationSeverity.Information; } @@ -100,7 +99,8 @@ private ModVerifyAppSettings BuildFromCreateBaselineVerb(CreateBaselineVerbOptio GlobalReportSettings = BuilderGlobalReportSettings(baselineVerb), NewBaselinePath = baselineVerb.OutputFile, ReportOutput = null, - Offline = baselineVerb.OfflineMode + Offline = baselineVerb.OfflineMode, + VerboseMode = baselineVerb.Verbose }; } @@ -119,7 +119,7 @@ VerificationBaseline CreateBaseline() if (options is not VerifyVerbOption verifyOptions || string.IsNullOrEmpty(verifyOptions.Baseline)) return VerificationBaseline.Empty; - using var fs = _fileSystem.FileStream.New(verifyOptions.Baseline!, FileMode.Open, FileAccess.Read); + using var fs = fileSystem.FileStream.New(verifyOptions.Baseline!, FileMode.Open, FileAccess.Read); try { @@ -138,7 +138,7 @@ SuppressionList CreateSuppressions() { if (options.Suppressions is null) return SuppressionList.Empty; - using var fs = _fileSystem.FileStream.New(options.Suppressions, FileMode.Open, FileAccess.Read); + using var fs = fileSystem.FileStream.New(options.Suppressions, FileMode.Open, FileAccess.Read); return SuppressionList.FromJson(fs); } } @@ -151,7 +151,7 @@ private GameInstallationsSettings BuildInstallationSettings(BaseModVerifyOptions foreach (var mod in options.ModPaths) { if (!string.IsNullOrEmpty(mod)) - modPaths.Add(_fileSystem.Path.GetFullPath(mod)); + modPaths.Add(fileSystem.Path.GetFullPath(mod)); } } @@ -161,22 +161,22 @@ private GameInstallationsSettings BuildInstallationSettings(BaseModVerifyOptions foreach (var fallback in options.AdditionalFallbackPath) { if (!string.IsNullOrEmpty(fallback)) - fallbackPaths.Add(_fileSystem.Path.GetFullPath(fallback)); + fallbackPaths.Add(fileSystem.Path.GetFullPath(fallback)); } } var gamePath = options.GamePath; if (!string.IsNullOrEmpty(gamePath)) - gamePath = _fileSystem.Path.GetFullPath(gamePath); + gamePath = fileSystem.Path.GetFullPath(gamePath!); string? fallbackGamePath = null; if (!string.IsNullOrEmpty(gamePath) && !string.IsNullOrEmpty(options.FallbackGamePath)) - fallbackGamePath = _fileSystem.Path.GetFullPath(options.FallbackGamePath); + fallbackGamePath = fileSystem.Path.GetFullPath(options.FallbackGamePath!); var autoPath = options.AutoPath; if (!string.IsNullOrEmpty(autoPath)) - autoPath = _fileSystem.Path.GetFullPath(autoPath); + autoPath = fileSystem.Path.GetFullPath(autoPath!); return new GameInstallationsSettings { diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdaterChecker.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs similarity index 95% rename from src/ModVerify.CliApp/Updates/ModVerifyUpdaterChecker.cs rename to src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index d0ab59b..87b54ec 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdaterChecker.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -10,11 +10,11 @@ namespace AET.ModVerifyTool.Updates; -internal sealed class ModVerifyUpdaterChecker +internal sealed class ModVerifyUpdater { private readonly ILogger? _logger; - public ModVerifyUpdaterChecker(IServiceProvider serviceProvider) + public ModVerifyUpdater(IServiceProvider serviceProvider) { _logger = serviceProvider.GetService()?.CreateLogger(GetType()); } From c0a257c59899906f7a828dbc1b0e0226332940e7 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 6 Jun 2025 11:43:49 +0200 Subject: [PATCH 05/61] implement and test argument parser --- Directory.Build.props | 2 +- ModVerify.sln | 6 + src/ModVerify.CliApp/ExtensionMethods.cs | 17 -- .../{ => GameFinder}/GameNotFoundException.cs | 2 +- .../ModSelectors/AutomaticModSelector.cs | 3 +- .../ModSelectors/ConsoleModSelector.cs | 3 +- .../ModSelectors/IModSelector.cs | 2 +- .../ModSelectors/ManualModSelector.cs | 2 +- .../ModSelectors/ModSelectorBase.cs | 2 +- .../ModSelectors/ModSelectorFactory.cs | 2 +- .../ModSelectors/SettingsBasedModSelector.cs | 7 +- .../VerifyInstallationData.cs} | 4 +- .../ModVerifyAppEnvironment.cs | 8 +- src/ModVerify.CliApp/ModVerifyApplication.cs | 53 +---- .../ModVerifyConsoleUtilities.cs | 22 -- src/ModVerify.CliApp/Program.cs | 207 +++++++++-------- .../Properties/AssemblyAttributes.cs | 3 + .../CommandLine/BaseModVerifyOptions.cs | 2 +- .../CommandLine/CreateBaselineVerbOption.cs | 4 +- .../CommandLine/ModVerifyOptionsParser.cs | 109 +++++++++ .../CommandLine/VerifyVerbOption.cs | 6 +- .../GameInstallationsSettings.cs | 4 +- .../ModVerifyAppSettings.cs | 2 +- .../Settings/ModVerifySettingsContainer.cs | 12 + .../{ => Settings}/SettingsBuilder.cs | 13 +- .../Updates/ModVerifyUpdateMode.cs | 8 + .../Updates/ModVerifyUpdater.cs | 59 ++++- .../Utilities/ExtensionMethods.cs | 29 +++ .../Utilities/ModVerifyConsoleUtilities.cs | 28 +++ .../PG.StarWarsGame.Files.ChunkFiles.csproj | 1 + .../PG.StarWarsGame.Files.XML.csproj | 1 + .../ModVerify.CliApp.Test.csproj | 32 +++ .../ModVerifyOptionsParserTest.cs | 211 ++++++++++++++++++ 33 files changed, 655 insertions(+), 211 deletions(-) delete mode 100644 src/ModVerify.CliApp/ExtensionMethods.cs rename src/ModVerify.CliApp/{ => GameFinder}/GameNotFoundException.cs (76%) rename src/ModVerify.CliApp/{VerifyInstallationInformation.cs => ModSelectors/VerifyInstallationData.cs} (90%) delete mode 100644 src/ModVerify.CliApp/ModVerifyConsoleUtilities.cs create mode 100644 src/ModVerify.CliApp/Properties/AssemblyAttributes.cs rename src/ModVerify.CliApp/{Options => Settings}/CommandLine/BaseModVerifyOptions.cs (98%) rename src/ModVerify.CliApp/{Options => Settings}/CommandLine/CreateBaselineVerbOption.cs (71%) create mode 100644 src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs rename src/ModVerify.CliApp/{Options => Settings}/CommandLine/VerifyVerbOption.cs (84%) rename src/ModVerify.CliApp/{Options => Settings}/GameInstallationsSettings.cs (90%) rename src/ModVerify.CliApp/{Options => Settings}/ModVerifyAppSettings.cs (95%) create mode 100644 src/ModVerify.CliApp/Settings/ModVerifySettingsContainer.cs rename src/ModVerify.CliApp/{ => Settings}/SettingsBuilder.cs (97%) create mode 100644 src/ModVerify.CliApp/Updates/ModVerifyUpdateMode.cs create mode 100644 src/ModVerify.CliApp/Utilities/ExtensionMethods.cs create mode 100644 src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs create mode 100644 test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj create mode 100644 test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs diff --git a/Directory.Build.props b/Directory.Build.props index 49ca26f..1dbc929 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -24,7 +24,7 @@ - latest + preview disable enable True diff --git a/ModVerify.sln b/ModVerify.sln index de26809..ce8d0b9 100644 --- a/ModVerify.sln +++ b/ModVerify.sln @@ -45,6 +45,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExternalUpdater.App", "modu EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExternalUpdater.Core", "modules\ModdingToolBase\src\Updater\ExternalUpdater.Core\ExternalUpdater.Core.csproj", "{D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModVerify.CliApp.Test", "test\ModVerify.CliApp.Test\ModVerify.CliApp.Test.csproj", "{EBFFD896-7432-4199-A7A3-311839E67278}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -111,6 +113,10 @@ Global {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}.Debug|Any CPU.Build.0 = Debug|Any CPU {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}.Release|Any CPU.ActiveCfg = Release|Any CPU {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}.Release|Any CPU.Build.0 = Release|Any CPU + {EBFFD896-7432-4199-A7A3-311839E67278}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EBFFD896-7432-4199-A7A3-311839E67278}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBFFD896-7432-4199-A7A3-311839E67278}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EBFFD896-7432-4199-A7A3-311839E67278}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/ModVerify.CliApp/ExtensionMethods.cs b/src/ModVerify.CliApp/ExtensionMethods.cs deleted file mode 100644 index b7e20c8..0000000 --- a/src/ModVerify.CliApp/ExtensionMethods.cs +++ /dev/null @@ -1,17 +0,0 @@ -using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Infrastructure.Games; - -namespace AET.ModVerifyTool; - -internal static class ExtensionMethods -{ - public static GameEngineType ToEngineType(this GameType type) - { - return type == GameType.Foc ? GameEngineType.Foc : GameEngineType.Eaw; - } - - public static GameType FromEngineType(this GameEngineType type) - { - return type == GameEngineType.Foc ? GameType.Foc : GameType.Eaw; - } -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/GameNotFoundException.cs b/src/ModVerify.CliApp/GameFinder/GameNotFoundException.cs similarity index 76% rename from src/ModVerify.CliApp/GameNotFoundException.cs rename to src/ModVerify.CliApp/GameFinder/GameNotFoundException.cs index 21cdc81..76af676 100644 --- a/src/ModVerify.CliApp/GameNotFoundException.cs +++ b/src/ModVerify.CliApp/GameFinder/GameNotFoundException.cs @@ -1,5 +1,5 @@ using PG.StarWarsGame.Infrastructure.Games; -namespace AET.ModVerifyTool; +namespace AET.ModVerifyTool.GameFinder; internal class GameNotFoundException(string message) : GameException(message); \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs index 26648a4..11f4207 100644 --- a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs @@ -3,7 +3,8 @@ using System.IO.Abstractions; using System.Linq; using AET.ModVerifyTool.GameFinder; -using AET.ModVerifyTool.Options; +using AET.ModVerifyTool.Settings; +using AET.ModVerifyTool.Utilities; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; diff --git a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs index 306f9e2..ee9c16c 100644 --- a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs @@ -2,7 +2,8 @@ using System.Collections.Generic; using AET.Modinfo.Spec; using AET.ModVerifyTool.GameFinder; -using AET.ModVerifyTool.Options; +using AET.ModVerifyTool.Settings; +using AET.ModVerifyTool.Utilities; using AnakinRaW.ApplicationBase; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; diff --git a/src/ModVerify.CliApp/ModSelectors/IModSelector.cs b/src/ModVerify.CliApp/ModSelectors/IModSelector.cs index f0b30a0..b92ac47 100644 --- a/src/ModVerify.CliApp/ModSelectors/IModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/IModSelector.cs @@ -1,4 +1,4 @@ -using AET.ModVerifyTool.Options; +using AET.ModVerifyTool.Settings; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; diff --git a/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs index d5913c6..fb6e77c 100644 --- a/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs @@ -1,5 +1,5 @@ using System; -using AET.ModVerifyTool.Options; +using AET.ModVerifyTool.Settings; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; diff --git a/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs b/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs index 0eec285..6dbcbe0 100644 --- a/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs +++ b/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using AET.ModVerifyTool.GameFinder; -using AET.ModVerifyTool.Options; +using AET.ModVerifyTool.Settings; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; diff --git a/src/ModVerify.CliApp/ModSelectors/ModSelectorFactory.cs b/src/ModVerify.CliApp/ModSelectors/ModSelectorFactory.cs index 8335a86..80eaaff 100644 --- a/src/ModVerify.CliApp/ModSelectors/ModSelectorFactory.cs +++ b/src/ModVerify.CliApp/ModSelectors/ModSelectorFactory.cs @@ -1,5 +1,5 @@ using System; -using AET.ModVerifyTool.Options; +using AET.ModVerifyTool.Settings; namespace AET.ModVerifyTool.ModSelectors; diff --git a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs index 12f7861..3f3e4b0 100644 --- a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs @@ -1,6 +1,7 @@ using System; using System.Linq; -using AET.ModVerifyTool.Options; +using AET.ModVerifyTool.GameFinder; +using AET.ModVerifyTool.Settings; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; @@ -8,7 +9,7 @@ namespace AET.ModVerifyTool.ModSelectors; internal class SettingsBasedModSelector(IServiceProvider serviceProvider) { - public VerifyInstallationInformation CreateInstallationDataFromSettings(GameInstallationsSettings settings) + public VerifyInstallationData CreateInstallationDataFromSettings(GameInstallationsSettings settings) { var gameLocations = new ModSelectorFactory(serviceProvider) .CreateSelector(settings) @@ -20,7 +21,7 @@ public VerifyInstallationInformation CreateInstallationDataFromSettings(GameInst if (engineType is null) throw new InvalidOperationException("Engine type not specified."); - return new VerifyInstallationInformation + return new VerifyInstallationData { EngineType = engineType.Value, GameLocations = gameLocations, diff --git a/src/ModVerify.CliApp/VerifyInstallationInformation.cs b/src/ModVerify.CliApp/ModSelectors/VerifyInstallationData.cs similarity index 90% rename from src/ModVerify.CliApp/VerifyInstallationInformation.cs rename to src/ModVerify.CliApp/ModSelectors/VerifyInstallationData.cs index 66816ab..3558503 100644 --- a/src/ModVerify.CliApp/VerifyInstallationInformation.cs +++ b/src/ModVerify.CliApp/ModSelectors/VerifyInstallationData.cs @@ -1,9 +1,9 @@ using System.Text; using PG.StarWarsGame.Engine; -namespace AET.ModVerifyTool; +namespace AET.ModVerifyTool.ModSelectors; -internal sealed class VerifyInstallationInformation +internal sealed class VerifyInstallationData { public required string Name { get; init; } diff --git a/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs b/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs index 9fcbfeb..de79246 100644 --- a/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs +++ b/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs @@ -1,10 +1,12 @@ -using System; -using System.Collections.Generic; -using System.IO.Abstractions; +using System.IO.Abstractions; using System.Reflection; using AnakinRaW.ApplicationBase.Environment; +#if !NET +using System; +using System.Collections.Generic; using AnakinRaW.AppUpdaterFramework.Configuration; using AnakinRaW.CommonUtilities.DownloadManager.Configuration; +#endif namespace AET.ModVerifyTool; diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index 29a9560..cbdaf9b 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -1,7 +1,5 @@ using AET.ModVerifyTool.ModSelectors; -using AET.ModVerifyTool.Options; using AET.ModVerifyTool.Reporting; -using AET.ModVerifyTool.Updates; using AnakinRaW.ApplicationBase; using AnakinRaW.ApplicationBase.Utilities; using Microsoft.Extensions.DependencyInjection; @@ -17,6 +15,7 @@ using AET.ModVerify; using AET.ModVerify.Pipeline; using AET.ModVerify.Reporting; +using AET.ModVerifyTool.Settings; using PG.StarWarsGame.Engine; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -38,14 +37,9 @@ private async Task RunCore() { _logger?.LogDebug($"Raw command line: {Environment.CommandLine}"); - var interactive = false; + var interactive = settings.Interactive; try { - interactive = settings.Interactive; - - if (!settings.Offline) - await CheckForUpdate().ConfigureAwait(false); - return await RunVerify().ConfigureAwait(false); } catch (Exception e) @@ -100,7 +94,7 @@ private async Task RunVerify() return 0; } - private async Task> Verify(VerifyInstallationInformation installInformation) + private async Task> Verify(VerifyInstallationData installData) { var gameEngineService = services.GetRequiredService(); var engineErrorReporter = new ConcurrentGameEngineErrorReporter(); @@ -114,10 +108,10 @@ private async Task> Verify(VerifyInstalla try { - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Creating Game Engine '{installInformation.EngineType}'"); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Creating Game Engine '{installData.EngineType}'"); gameEngine = await gameEngineService.InitializeAsync( - installInformation.EngineType, - installInformation.GameLocations, + installData.EngineType, + installData.GameLocations, engineErrorReporter, initProgress, false, @@ -135,7 +129,7 @@ private async Task> Verify(VerifyInstalla throw; } - var progressReporter = new VerifyConsoleProgressReporter(installInformation.Name); + var progressReporter = new VerifyConsoleProgressReporter(installData.Name); using var verifyPipeline = new GameVerifyPipeline( gameEngine, @@ -149,7 +143,7 @@ private async Task> Verify(VerifyInstalla { try { - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Verifying '{installInformation.Name}'..."); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Verifying '{installData.Name}'..."); await verifyPipeline.RunAsync().ConfigureAwait(false); progressReporter.Report(string.Empty, 1.0); } @@ -202,35 +196,4 @@ private async Task WriteBaseline(IEnumerable errors, string b using var fs = _fileSystem.FileStream.New(fullPath, FileMode.Create, FileAccess.Write, FileShare.None); await baseline.ToJsonAsync(fs); } - - - private async Task CheckForUpdate() - { - var updateChecker = new ModVerifyUpdater(services); - - _logger?.LogDebug("Checking for available update"); - - try - { - var updateInfo = await updateChecker.CheckForUpdateAsync().ConfigureAwait(false); - if (updateInfo.IsUpdateAvailable) - { - ConsoleUtilities.WriteHorizontalLine(); - - Console.ForegroundColor = ConsoleColor.DarkGreen; - Console.WriteLine("New Update Available!"); - Console.ResetColor(); - - Console.WriteLine($"Version: {updateInfo.NewVersion}, Download here: {updateInfo.DownloadLink}"); - ConsoleUtilities.WriteHorizontalLine(); - Console.WriteLine(); - - } - } - catch (Exception e) - { - _logger?.LogWarning(ModVerifyConstants.ConsoleEventId, $"Unable to check for updates due to an internal error: {e.Message}"); - _logger?.LogTrace(e, "Checking for update failed: " + e.Message); - } - } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModVerifyConsoleUtilities.cs b/src/ModVerify.CliApp/ModVerifyConsoleUtilities.cs deleted file mode 100644 index 8bca1e8..0000000 --- a/src/ModVerify.CliApp/ModVerifyConsoleUtilities.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using AnakinRaW.ApplicationBase; - -namespace AET.ModVerifyTool; - -internal static class ModVerifyConsoleUtilities -{ - public static void WriteHeader() - { - const int lineLength = 50; - const string author = "by AnakinRaW"; - - ConsoleUtilities.WriteHorizontalLine('*', lineLength); - - Console.WriteLine(Figgle.FiggleFonts.Standard.Render(ModVerifyConstants.AppNameString)); - ConsoleUtilities.WriteHorizontalLine('*', lineLength); - - Console.WriteLine(new string(' ', lineLength - author.Length)+ author); - Console.WriteLine(); - Console.WriteLine(); - } -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 6d23bec..9db0f4c 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -4,18 +4,16 @@ using AET.ModVerify.Reporting.Reporters.JSON; using AET.ModVerify.Reporting.Reporters.Text; using AET.ModVerify.Reporting.Settings; -using AET.ModVerifyTool.Options; -using AET.ModVerifyTool.Options.CommandLine; +using AET.ModVerifyTool.Updates; using AET.SteamAbstraction; using AnakinRaW.ApplicationBase; using AnakinRaW.ApplicationBase.Environment; using AnakinRaW.ApplicationBase.Update; +using AnakinRaW.ApplicationBase.Update.Options; using AnakinRaW.AppUpdaterFramework.Json; using AnakinRaW.CommonUtilities.Hashing; using AnakinRaW.CommonUtilities.Registry; using AnakinRaW.CommonUtilities.Registry.Windows; -using CommandLine; -using CommandLine.Text; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.Commons; @@ -31,19 +29,32 @@ using PG.StarWarsGame.Infrastructure.Services.Name; using Serilog; using Serilog.Events; +using Serilog.Expressions; using Serilog.Filters; using Serilog.Sinks.SystemConsole.Themes; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO.Abstractions; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Serilog.Expressions; +using AET.ModVerifyTool.Settings; +using AET.ModVerifyTool.Settings.CommandLine; +using AET.ModVerifyTool.Utilities; using Testably.Abstractions; using ILogger = Serilog.ILogger; namespace AET.ModVerifyTool; +internal class MainClass +{ + // Fody/Costura application with .NET Core apparently don't work well when the class containing the Main method are derived by a type in an embedded assembly. + private static Task Main(string[] args) + { + return new Program().StartAsync(args); + } +} + internal class Program : SelfUpdateableAppLifecycle { private static readonly string EngineParserNamespace = typeof(XmlObjectParser<>).Namespace!; @@ -51,25 +62,20 @@ internal class Program : SelfUpdateableAppLifecycle private static readonly string ModVerifyRootNameSpace = typeof(Program).Namespace!; private static readonly CompiledExpression PrintToConsoleExpression = SerilogExpression.Compile($"EventId.Id = {ModVerifyConstants.ConsoleEventIdValue}"); - private static ModVerifyAppSettings _modVerifySettings = null!; - - private static async Task Main(string[] args) - { - ModVerifyConsoleUtilities.WriteHeader(); - return await new Program().StartAsync(args); - } + private static ModVerifySettingsContainer _settingsContainer = null!; protected override async Task InitializeAppAsync(IReadOnlyList args) { + ModVerifyConsoleUtilities.WriteHeader(ApplicationEnvironment.AssemblyInfo.InformationalVersion); + await base.InitializeAppAsync(args); try { - var settings = ParseSettings(args); - if (settings is null) + var settings = new ModVerifyOptionsParser(ApplicationEnvironment, FileSystem, BootstrapLoggerFactory).Parse(args); + if (!settings.HasSettings) return 0xA0; - - _modVerifySettings = settings; + _settingsContainer = settings; return 0; } catch (Exception e) @@ -84,6 +90,8 @@ protected override void CreateAppServices(IServiceCollection services, IReadOnly { base.CreateAppServices(services, args); + services.AddSingleton((ApplicationEnvironment as ModVerifyAppEnvironment)!); + services.AddLogging(ConfigureLogging); services.AddSingleton(sp => new HashingService(sp)); @@ -100,6 +108,9 @@ protected override void CreateAppServices(IServiceCollection services, IReadOnly sp => new JsonManifestLoader(sp)); } + if (_settingsContainer.ModVerifyAppSettings is null) + return; + SteamAbstractionLayer.InitializeServices(services); PetroglyphGameInfrastructure.InitializeServices(services); @@ -113,9 +124,9 @@ protected override void CreateAppServices(IServiceCollection services, IReadOnly services.RegisterVerifierCache(); - SetupVerifyReporting(services, _modVerifySettings); + SetupVerifyReporting(services); - if (_modVerifySettings.Offline) + if (_settingsContainer.ModVerifyAppSettings.Offline) { services.AddSingleton(sp => new OfflineModNameResolver(sp)); services.AddSingleton(sp => new OfflineModGameTypeResolver(sp)); @@ -127,29 +138,6 @@ protected override void CreateAppServices(IServiceCollection services, IReadOnly } } - private ModVerifyAppSettings? ParseSettings(IReadOnlyList args) - { - Type[] programVerbs = - [ - typeof(VerifyVerbOption), - typeof(CreateBaselineVerbOption), - ]; - - var parseResult = Parser.Default.ParseArguments(args, programVerbs); - - ModVerifyAppSettings? settings = null; - - parseResult.WithParsed(o => settings = BuildSettings(o)); - - parseResult.WithNotParsed(_ => - { - Logger?.LogError("Unable to parse command line"); - Console.WriteLine(HelpText.AutoBuild(parseResult).ToString()); - }); - - return settings; - } - protected override ApplicationEnvironment CreateAppEnvironment() { return new ModVerifyAppEnvironment(typeof(Program).Assembly, FileSystem); @@ -169,11 +157,17 @@ protected override IRegistry CreateRegistry() protected override async Task RunAppAsync(string[] args, IServiceProvider appServiceProvider) { - return await new ModVerifyApplication(_modVerifySettings, appServiceProvider).Run().ConfigureAwait(false); + var result = await HandleUpdate(appServiceProvider); + if (result != 0 || _settingsContainer.ModVerifyAppSettings is null) + return result; + return await new ModVerifyApplication(_settingsContainer.ModVerifyAppSettings, appServiceProvider).Run().ConfigureAwait(false); } - private static void SetupVerifyReporting(IServiceCollection serviceCollection, ModVerifyAppSettings settings) + private static void SetupVerifyReporting(IServiceCollection serviceCollection) { + var settings = _settingsContainer.ModVerifyAppSettings; + Debug.Assert(settings is not null); + var printOnlySummary = settings.CreateNewBaseline; serviceCollection.RegisterConsoleReporter(new VerifyReportSettings { @@ -196,11 +190,6 @@ private static void SetupVerifyReporting(IServiceCollection serviceCollection, M }); } - private ModVerifyAppSettings BuildSettings(BaseModVerifyOptions options) - { - return new SettingsBuilder(FileSystem, BootstrapLoggerFactory).BuildSettings(options); - } - private void ConfigureLogging(ILoggingBuilder loggingBuilder) { loggingBuilder.ClearProviders(); @@ -212,67 +201,97 @@ private void ConfigureLogging(ILoggingBuilder loggingBuilder) loggingBuilder.AddDebug(); #endif - if (_modVerifySettings.VerboseMode) + if (_settingsContainer.ModVerifyAppSettings?.VerboseMode == true || _settingsContainer.UpdateOptions?.Verbose == true) { logLevel = LogEventLevel.Verbose; loggingBuilder.AddDebug(); } - var fileLogger = SetupFileLogging(logLevel); + var fileLogger = SetupFileLogging(); loggingBuilder.AddSerilog(fileLogger); - var cLogger = new LoggerConfiguration() - .WriteTo.Console( - logLevel, - theme: AnsiConsoleTheme.Code, - outputTemplate: "[{Level:u3}] {Message:lj}{NewLine}{Exception}") - .MinimumLevel.Is(logLevel) - .Filter.ByIncludingOnly(x => - { - // Fatal errors are handled by a global exception handler - if (x.Level == LogEventLevel.Fatal) - return false; + var consoleLogger = SetupConsoleLogging(); + loggingBuilder.AddSerilog(consoleLogger); - // Verbose should print everything we get - if (logLevel == LogEventLevel.Verbose) - return true; + return; - // Debug should print everything that has something to do with ModVerify - if (logLevel == LogEventLevel.Debug) + ILogger SetupConsoleLogging() + { + return new LoggerConfiguration() + .WriteTo.Console( + logLevel, + theme: AnsiConsoleTheme.Code, + outputTemplate: "[{Level:u3}] {Message:lj}{NewLine}{Exception}") + .MinimumLevel.Is(logLevel) + .Filter.ByIncludingOnly(x => { - if (!x.Properties.TryGetValue("SourceContext", out var value)) + // Fatal errors are handled by a global exception handler + if (x.Level == LogEventLevel.Fatal) return false; - var source = value.ToString().AsSpan().Trim('\"'); - return source.StartsWith(ModVerifyRootNameSpace.AsSpan()); - } - - // In normal operation, we only print logs, which have the print-to-console EventId set. - return ExpressionResult.IsTrue(PrintToConsoleExpression(x)); - }) - .CreateLogger(); - loggingBuilder.AddSerilog(cLogger); - } - private ILogger SetupFileLogging(LogEventLevel minLevel) - { - var logPath = FileSystem.Path.Combine(ApplicationEnvironment.ApplicationLocalPath, "ModVerify_log.txt"); + // Verbose should print everything we get + if (logLevel == LogEventLevel.Verbose) + return true; + + // Debug should print everything that has something to do with ModVerify + if (logLevel == LogEventLevel.Debug) + { + if (!x.Properties.TryGetValue("SourceContext", out var value)) + return false; + var source = value.ToString().AsSpan().Trim('\"'); + return source.StartsWith(ModVerifyRootNameSpace.AsSpan()); + } + + // In normal operation, we only print logs, which have the print-to-console EventId set. + return ExpressionResult.IsTrue(PrintToConsoleExpression(x)); + }) + .CreateLogger(); + } - return new LoggerConfiguration() - .Enrich.FromLogContext() - .MinimumLevel.Is(minLevel) - .Filter.ByExcluding(IsXmlParserLogging) - .WriteTo.Async(c => - { - c.RollingFile( - logPath, - outputTemplate: - "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {Message}{NewLine}{Exception}"); - }) - .CreateLogger(); + ILogger SetupFileLogging() + { + var logPath = FileSystem.Path.Combine(ApplicationEnvironment.ApplicationLocalPath, "ModVerify_log.txt"); + + return new LoggerConfiguration() + .Enrich.FromLogContext() + .MinimumLevel.Is(logLevel) + .Filter.ByExcluding(IsXmlParserLogging) + .WriteTo.Async(c => + { + c.RollingFile( + logPath, + outputTemplate: + "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {Message}{NewLine}{Exception}"); + }) + .CreateLogger(); + } + + static bool IsXmlParserLogging(LogEvent logEvent) + { + return Matching.FromSource(ParserNamespace)(logEvent) || Matching.FromSource(EngineParserNamespace)(logEvent); + } } - private static bool IsXmlParserLogging(LogEvent logEvent) + private async Task HandleUpdate(IServiceProvider serviceProvider) { - return Matching.FromSource(ParserNamespace)(logEvent) || Matching.FromSource(EngineParserNamespace)(logEvent); + var updateOptions = _settingsContainer.UpdateOptions ?? new ApplicationUpdateOptions(); + ModVerifyUpdateMode updateMode; + + if (_settingsContainer.ModVerifyAppSettings is not null) + { + if (_settingsContainer.ModVerifyAppSettings.Offline) + { + Logger?.LogTrace("Running in offline mode. There will be nothing to update."); + return 0; + } + + updateMode = _settingsContainer.ModVerifyAppSettings.Interactive + ? ModVerifyUpdateMode.InteractiveUpdate + : ModVerifyUpdateMode.CheckOnly; + } + else + updateMode = ModVerifyUpdateMode.AutoUpdate; + + return await new ModVerifyUpdater(updateOptions, serviceProvider).RunUpdateProcedure(updateMode).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Properties/AssemblyAttributes.cs b/src/ModVerify.CliApp/Properties/AssemblyAttributes.cs new file mode 100644 index 0000000..af943d8 --- /dev/null +++ b/src/ModVerify.CliApp/Properties/AssemblyAttributes.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly:InternalsVisibleTo("ModVerify.CliApp.Test")] \ No newline at end of file diff --git a/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs b/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs similarity index 98% rename from src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs rename to src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs index 3863314..1fa7c7c 100644 --- a/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs @@ -3,7 +3,7 @@ using CommandLine; using PG.StarWarsGame.Engine; -namespace AET.ModVerifyTool.Options.CommandLine; +namespace AET.ModVerifyTool.Settings.CommandLine; internal abstract class BaseModVerifyOptions { diff --git a/src/ModVerify.CliApp/Options/CommandLine/CreateBaselineVerbOption.cs b/src/ModVerify.CliApp/Settings/CommandLine/CreateBaselineVerbOption.cs similarity index 71% rename from src/ModVerify.CliApp/Options/CommandLine/CreateBaselineVerbOption.cs rename to src/ModVerify.CliApp/Settings/CommandLine/CreateBaselineVerbOption.cs index e957da9..1c9d518 100644 --- a/src/ModVerify.CliApp/Options/CommandLine/CreateBaselineVerbOption.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/CreateBaselineVerbOption.cs @@ -1,9 +1,9 @@ using CommandLine; -namespace AET.ModVerifyTool.Options.CommandLine; +namespace AET.ModVerifyTool.Settings.CommandLine; [Verb("createBaseline", HelpText = "Verifies the specified game and creates a new baseline file at the specified location.")] -internal class CreateBaselineVerbOption : BaseModVerifyOptions +internal sealed class CreateBaselineVerbOption : BaseModVerifyOptions { [Option('o', "outFile", Required = true, HelpText = "The file path of the new baseline file.")] public required string OutputFile { get; init; } diff --git a/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs b/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs new file mode 100644 index 0000000..4b65866 --- /dev/null +++ b/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO.Abstractions; +using System.Linq; +using System.Threading; +using AET.ModVerifyTool.Utilities; +using AnakinRaW.ApplicationBase.Environment; +using AnakinRaW.ApplicationBase.Update.Options; +using AnakinRaW.ExternalUpdater; +using CommandLine; +using CommandLine.Text; +using Microsoft.Extensions.Logging; + +namespace AET.ModVerifyTool.Settings.CommandLine; + +internal sealed class ModVerifyOptionsParser +{ + private readonly ApplicationEnvironment _applicationEnvironment; + private readonly IFileSystem _fileSystem; + private readonly ILogger? _logger; + private readonly ILoggerFactory? _loggerFactory; + + [field: AllowNull, MaybeNull] + private Type[] AvailableVerbTypes => LazyInitializer.EnsureInitialized(ref field, GetAvailableVerbTypes)!; + + public ModVerifyOptionsParser( + ApplicationEnvironment applicationEnvironment, + IFileSystem fileSystem, + ILoggerFactory? loggerFactory) + { + _applicationEnvironment = applicationEnvironment; + _fileSystem = fileSystem; + _loggerFactory = loggerFactory; + _logger = loggerFactory?.CreateLogger(GetType()); + } + + public ModVerifySettingsContainer Parse(IReadOnlyList args) + { + // If the application is updatable (.NET Framework) we need to remove potential arguments from the external updater + // in order to keep strict parsing rules enabled for better user error messages. + if (_applicationEnvironment.IsUpdatable()) + args = StripExternalUpdateResults(args); + + return ParseArguments(args); + } + + private ModVerifySettingsContainer ParseArguments(IReadOnlyList args) + { + // Empty arguments means that we are "interactive" mode (user simply double-clicked the executable) + if (args.Count == 0) + { + return new ModVerifySettingsContainer + { + ModVerifyAppSettings = BuildSettings(new VerifyVerbOption()), + UpdateOptions = null + }; + } + + var parseResult = Parser.Default.ParseArguments(args, AvailableVerbTypes); + + ModVerifyAppSettings? settings = null; + ApplicationUpdateOptions? updateOptions = null; + + parseResult.WithParsed(o => settings = BuildSettings(o)); + parseResult.WithParsed(o => updateOptions = o); + + parseResult.WithNotParsed(_ => + { + _logger?.LogError("Unable to parse command line"); + Console.WriteLine(HelpText.AutoBuild(parseResult).ToString()); + }); + + return new ModVerifySettingsContainer + { + ModVerifyAppSettings = settings, + UpdateOptions = updateOptions, + }; + } + + public static IReadOnlyList StripExternalUpdateResults(IReadOnlyList args) + { + // Parser.Default.FormatCommandLine(ResultOption) as used in ProcessTool.cs either returns + // two argument segments or none (if Result == UpdaterNotRun) + if (args.Count < 2) + return args; + + // The external updater promises to append the result to the arguments. + // Thus, it's sufficient to check the second last segment whether it matches. + var secondLast = args[^2]; + + return secondLast == ExternalUpdaterResultOptions.RawOptionString + ? [..args.Take(args.Count - 2)] + : args; + } + + + private ModVerifyAppSettings BuildSettings(BaseModVerifyOptions options) + { + return new SettingsBuilder(_fileSystem, _loggerFactory).BuildSettings(options); + } + + private Type[] GetAvailableVerbTypes() + { + return _applicationEnvironment.IsUpdatable() + ? [typeof(VerifyVerbOption), typeof(CreateBaselineVerbOption), typeof(ApplicationUpdateOptions)] + : [typeof(VerifyVerbOption), typeof(CreateBaselineVerbOption)]; + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Options/CommandLine/VerifyVerbOption.cs b/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs similarity index 84% rename from src/ModVerify.CliApp/Options/CommandLine/VerifyVerbOption.cs rename to src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs index 5827fd2..7260b43 100644 --- a/src/ModVerify.CliApp/Options/CommandLine/VerifyVerbOption.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs @@ -1,10 +1,10 @@ using AET.ModVerify.Reporting; using CommandLine; -namespace AET.ModVerifyTool.Options.CommandLine; +namespace AET.ModVerifyTool.Settings.CommandLine; -[Verb("verify", true, HelpText = "Verifies the specified game and reports the findings.")] -internal class VerifyVerbOption : BaseModVerifyOptions +[Verb("verify", HelpText = "Verifies the specified game and reports the findings.")] +internal sealed class VerifyVerbOption : BaseModVerifyOptions { [Option('o', "outDir", Required = false, HelpText = "Directory where result files shall be stored to.")] public string? OutputDirectory { get; init; } diff --git a/src/ModVerify.CliApp/Options/GameInstallationsSettings.cs b/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs similarity index 90% rename from src/ModVerify.CliApp/Options/GameInstallationsSettings.cs rename to src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs index 58223c2..a012449 100644 --- a/src/ModVerify.CliApp/Options/GameInstallationsSettings.cs +++ b/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs @@ -2,9 +2,9 @@ using System.Diagnostics.CodeAnalysis; using PG.StarWarsGame.Engine; -namespace AET.ModVerifyTool.Options; +namespace AET.ModVerifyTool.Settings; -internal record GameInstallationsSettings +internal sealed record GameInstallationsSettings { public bool Interactive => string.IsNullOrEmpty(AutoPath) && ModPaths.Count == 0 && string.IsNullOrEmpty(GamePath); diff --git a/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs b/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs similarity index 95% rename from src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs rename to src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs index a1b0752..8c204b8 100644 --- a/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs +++ b/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs @@ -3,7 +3,7 @@ using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Settings; -namespace AET.ModVerifyTool.Options; +namespace AET.ModVerifyTool.Settings; internal sealed class ModVerifyAppSettings { diff --git a/src/ModVerify.CliApp/Settings/ModVerifySettingsContainer.cs b/src/ModVerify.CliApp/Settings/ModVerifySettingsContainer.cs new file mode 100644 index 0000000..317114f --- /dev/null +++ b/src/ModVerify.CliApp/Settings/ModVerifySettingsContainer.cs @@ -0,0 +1,12 @@ +using AnakinRaW.ApplicationBase.Update.Options; + +namespace AET.ModVerifyTool.Settings; + +internal sealed class ModVerifySettingsContainer +{ + public ModVerifyAppSettings? ModVerifyAppSettings { get; init; } + + public ApplicationUpdateOptions? UpdateOptions { get; init; } + + public bool HasSettings => ModVerifyAppSettings is not null || UpdateOptions is not null; +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/SettingsBuilder.cs b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs similarity index 97% rename from src/ModVerify.CliApp/SettingsBuilder.cs rename to src/ModVerify.CliApp/Settings/SettingsBuilder.cs index 6386f9e..b2a747d 100644 --- a/src/ModVerify.CliApp/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs @@ -1,16 +1,15 @@ -using AET.ModVerify.Reporting; -using AET.ModVerify.Reporting.Settings; -using AET.ModVerify.Settings; -using AET.ModVerifyTool.Options; -using System; +using System; using System.Collections.Generic; using System.IO; using System.IO.Abstractions; using AET.ModVerify.Pipeline; -using AET.ModVerifyTool.Options.CommandLine; +using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting.Settings; +using AET.ModVerify.Settings; +using AET.ModVerifyTool.Settings.CommandLine; using Microsoft.Extensions.Logging; -namespace AET.ModVerifyTool; +namespace AET.ModVerifyTool.Settings; internal sealed class SettingsBuilder(IFileSystem fileSystem, ILoggerFactory? loggerFactory) { diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdateMode.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdateMode.cs new file mode 100644 index 0000000..12f3757 --- /dev/null +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdateMode.cs @@ -0,0 +1,8 @@ +namespace AET.ModVerifyTool.Updates; + +public enum ModVerifyUpdateMode +{ + CheckOnly, + InteractiveUpdate, + AutoUpdate, +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index 87b54ec..3199d7a 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -6,19 +6,76 @@ using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; +using AnakinRaW.ApplicationBase.Update.Options; using Semver; namespace AET.ModVerifyTool.Updates; internal sealed class ModVerifyUpdater { + private readonly IServiceProvider _serviceProvider; private readonly ILogger? _logger; + private readonly ModVerifyAppEnvironment _appEnvironment; - public ModVerifyUpdater(IServiceProvider serviceProvider) + public ModVerifyUpdater(ApplicationUpdateOptions updateOptions, IServiceProvider serviceProvider) { + _serviceProvider = serviceProvider; _logger = serviceProvider.GetService()?.CreateLogger(GetType()); + _appEnvironment = serviceProvider.GetRequiredService(); } + + + public async Task RunUpdateProcedure(ModVerifyUpdateMode mode) + { + //if (_settings.Offline) + //{ + // _logger?.LogTrace("App is running in offline mode. Nothing to do here."); + // return; + //} + + //if (!IsUpdatable(out var updateEnv)) + //{ + // await CheckUpdateGithub(); + // return; + //} + + + + _logger?.LogDebug("Checking for available update"); + + //try + //{ + // var updateInfo = await updateChecker.CheckForUpdateAsync().ConfigureAwait(false); + // if (updateInfo.IsUpdateAvailable) + // { + // ConsoleUtilities.WriteHorizontalLine(); + + // Console.ForegroundColor = ConsoleColor.DarkGreen; + // Console.WriteLine("New Update Available!"); + // Console.ResetColor(); + + // Console.WriteLine($"Version: {updateInfo.NewVersion}, Download here: {updateInfo.DownloadLink}"); + // ConsoleUtilities.WriteHorizontalLine(); + // Console.WriteLine(); + + // } + //} + //catch (Exception e) + //{ + // _logger?.LogWarning(ModVerifyConstants.ConsoleEventId, $"Unable to check for updates due to an internal error: {e.Message}"); + // _logger?.LogTrace(e, "Checking for update failed: " + e.Message); + //} + + return 0; + } + + private async Task CheckUpdateGithub() + { + + } + + public async Task CheckForUpdateAsync() { var githubReleases = await DownloadReleaseList().ConfigureAwait(false); diff --git a/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs new file mode 100644 index 0000000..5749de6 --- /dev/null +++ b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs @@ -0,0 +1,29 @@ +using AnakinRaW.ApplicationBase.Environment; +using PG.StarWarsGame.Engine; +using PG.StarWarsGame.Infrastructure.Games; + +namespace AET.ModVerifyTool.Utilities; + +internal static class ExtensionMethods +{ + public static GameEngineType ToEngineType(this GameType type) + { + return type == GameType.Foc ? GameEngineType.Foc : GameEngineType.Eaw; + } + + public static GameType FromEngineType(this GameEngineType type) + { + return type == GameEngineType.Foc ? GameType.Foc : GameType.Eaw; + } + + public static bool IsUpdatable(this ApplicationEnvironment modVerifyEnvironment) + { + return modVerifyEnvironment.IsUpdatable(out _); + } + + public static bool IsUpdatable(this ApplicationEnvironment applicationEnvironment, out UpdatableApplicationEnvironment? updatableEnvironment) + { + updatableEnvironment = applicationEnvironment as UpdatableApplicationEnvironment; + return updatableEnvironment is not null; + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs b/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs new file mode 100644 index 0000000..e5dea7d --- /dev/null +++ b/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs @@ -0,0 +1,28 @@ +using System; +using AnakinRaW.ApplicationBase; + +namespace AET.ModVerifyTool.Utilities; + +internal static class ModVerifyConsoleUtilities +{ + public static void WriteHeader(string? version = null) + { + const int lineLength = 73; + const string author = "by AnakinRaW"; + + ConsoleUtilities.WriteHorizontalLine('*', lineLength); + Console.WriteLine(Figgle.FiggleFonts.Standard.Render(ModVerifyConstants.AppNameString)); + if (!string.IsNullOrEmpty(version)) + { + Console.ForegroundColor = ConsoleColor.DarkGray; + ConsoleUtilities.WriteLineRight($"Version: {version}", lineLength); + Console.ResetColor(); + Console.WriteLine(); + } + ConsoleUtilities.WriteHorizontalLine('*', lineLength); + + ConsoleUtilities.WriteLineRight(author, lineLength); + Console.WriteLine(); + Console.WriteLine(); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj index d6a7939..88b3cb9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj @@ -14,6 +14,7 @@ true snupkg + preview diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj index 71c3fed..f161cd5 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj @@ -15,6 +15,7 @@ true snupkg true + preview diff --git a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj new file mode 100644 index 0000000..657d4f9 --- /dev/null +++ b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj @@ -0,0 +1,32 @@ + + + + net9.0 + false + preview + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs new file mode 100644 index 0000000..400a300 --- /dev/null +++ b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Reflection; +using AET.ModVerifyTool.Settings.CommandLine; +using AnakinRaW.ApplicationBase.Environment; +using AnakinRaW.AppUpdaterFramework.Configuration; +using Testably.Abstractions; + +namespace ModVerify.CliApp.Test; + +public class ModVerifyOptionsParserTest_Updateable : ModVerifyOptionsParserTestBase +{ + protected override bool IsUpdatable => true; + + protected override ApplicationEnvironment CreateEnvironment() + { + return new UpdatableEnv(GetType().Assembly, FileSystem); + } + + [Fact] + public void Parse_UpdateAppArg() + { + const string argString = "updateApplication --updateBranch test --updateManifestUrl https://examlple.com"; + + var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + + Assert.True(settings.HasSettings); + Assert.Null(settings.ModVerifyAppSettings); + Assert.NotNull(settings.UpdateOptions); + Assert.Equal("test", settings.UpdateOptions.BranchName); + Assert.Equal("https://examlple.com", settings.UpdateOptions.ManifestUrl); + } +} + +public class ModVerifyOptionsParserTest_NotUpdateable : ModVerifyOptionsParserTestBase +{ + protected override bool IsUpdatable => false; + + protected override ApplicationEnvironment CreateEnvironment() + { + return new TestEnv(GetType().Assembly, FileSystem); + } + + [Theory] + [InlineData("verify --externalUpdaterResult UpdateSuccess")] + [InlineData("createBaseline --externalUpdaterResult UpdateSuccess")] + [InlineData("verify --junkOption")] + [InlineData("createBaseline --junkOption")] + [InlineData("updateApplication")] + public void Parse_InvalidArgs_NotUpdateable(string argString) + { + var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + + Assert.False(settings.HasSettings); + Assert.Null(settings.ModVerifyAppSettings); + Assert.Null(settings.UpdateOptions); + } +} + + +public abstract class ModVerifyOptionsParserTestBase +{ + private protected readonly ModVerifyOptionsParser Parser; + protected readonly IFileSystem FileSystem = new RealFileSystem(); + + protected abstract ApplicationEnvironment CreateEnvironment(); + + protected abstract bool IsUpdatable { get; } + + protected ModVerifyOptionsParserTestBase() + { + Parser = new ModVerifyOptionsParser(CreateEnvironment(), FileSystem, null); + } + + [Fact] + public void Parse_NoArgs_IsVerify_IsInteractive() + { + var settings = Parser.Parse([]); + + Assert.True(settings.HasSettings); + Assert.NotNull(settings.ModVerifyAppSettings); + + Assert.False(settings.ModVerifyAppSettings.CreateNewBaseline); + Assert.True(settings.ModVerifyAppSettings.Interactive); + + Assert.Null(settings.UpdateOptions); + } + + [Theory] + [InlineData("verify", false)] + [InlineData("verify -v", false)] + [InlineData("createBaseline -o out.json", true)] + [InlineData("createBaseline -v -o out.json", true)] + public void Parse_Interactive(string argString, bool createBaseLine) + { + var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + + Assert.True(settings.HasSettings); + Assert.NotNull(settings.ModVerifyAppSettings); + + Assert.True(settings.ModVerifyAppSettings.Interactive); + Assert.Equal(createBaseLine, settings.ModVerifyAppSettings.CreateNewBaseline); + Assert.Null(settings.UpdateOptions); + } + + [Theory] + [InlineData("verify --path myMod", false)] + [InlineData("verify -v --game myGame", false)] + [InlineData("createBaseline -o out.json --path myMod", true)] + [InlineData("createBaseline -v -o out.json --game myGame", true)] + public void Parse_NotInteractive(string argString, bool createBaseLine) + { + var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + + Assert.True(settings.HasSettings); + Assert.NotNull(settings.ModVerifyAppSettings); + + Assert.False(settings.ModVerifyAppSettings.Interactive); + Assert.Equal(createBaseLine, settings.ModVerifyAppSettings.CreateNewBaseline); + Assert.Null(settings.UpdateOptions); + } + + [Theory] + [InlineData("verify --path myMod --game myGame")] + [InlineData("verify --game myMod --path myMod")] + [InlineData("verify --mod myMod --path myMod")] + [InlineData("verify --fallbackGame myGame --path myMod")] + public void Parse_InvalidPathConfig(string argString) + { + var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + + Assert.False(settings.HasSettings); + Assert.Null(settings.ModVerifyAppSettings); + Assert.Null(settings.UpdateOptions); + } + + [Theory] + [InlineData("")] + [InlineData("junkVerb")] + [InlineData("junkVerb verify")] + [InlineData("junkVerb verify --v")] + [InlineData("junkVerb --v")] + [InlineData("verify --junkOption")] + [InlineData("verify -v --junkOption")] + [InlineData("updateApplication --junkOption")] + [InlineData("--junkOption")] + [InlineData("junkVerb --junkOption")] + [InlineData("junkVerb --externalUpdaterResult UpdateSuccess")] + [InlineData("-v")] + public void Parse_InvalidArgs(string argString) + { + var settings = Parser.Parse(argString.Split(' ')); + + Assert.False(settings.HasSettings); + Assert.Null(settings.ModVerifyAppSettings); + Assert.Null(settings.UpdateOptions); + } + + [Fact] + public void Parse_UpdatePerformed_RestartedFromNoArgs() + { + // This only happens when we run without args, performed an auto-update and restarted the application automatically. + const string argString = "--externalUpdaterResult UpdateSuccess"; + + var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + + if (!IsUpdatable) + Assert.False(settings.HasSettings); + else + { + Assert.True(settings.HasSettings); + Assert.NotNull(settings.ModVerifyAppSettings); + + Assert.False(settings.ModVerifyAppSettings.CreateNewBaseline); + Assert.True(settings.ModVerifyAppSettings.Interactive); + + Assert.Null(settings.UpdateOptions); + } + } + + [Theory] + [InlineData("createBaseline")] + [InlineData("createBaseline -v")] + public void Parse_CreateBaseline_MissingRequired_Fails(string argString) + { + var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + + Assert.False(settings.HasSettings); + Assert.Null(settings.ModVerifyAppSettings); + Assert.Null(settings.UpdateOptions); + } +} + +internal class TestEnv(Assembly assembly, IFileSystem fileSystem) : ApplicationEnvironment(assembly, fileSystem) +{ + public override string ApplicationName => "TestEnv"; + protected override string ApplicationLocalDirectoryName => ApplicationName; +} + +internal class UpdatableEnv(Assembly assembly, IFileSystem fileSystem) : UpdatableApplicationEnvironment(assembly, fileSystem) +{ + public override string ApplicationName => "TestUpdateEnv"; + protected override string ApplicationLocalDirectoryName => ApplicationName; + public override ICollection UpdateMirrors => []; + public override string UpdateRegistryPath => ApplicationName; + protected override UpdateConfiguration CreateUpdateConfiguration() + { + return UpdateConfiguration.Default; + } +} \ No newline at end of file From 09c1c03900aaf0b2b2b3e0dae6e6e659eea341e2 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 6 Jun 2025 11:50:03 +0200 Subject: [PATCH 06/61] update namespace --- .../GameFinder/GameFinderResult.cs | 2 +- .../GameFinder/GameFinderService.cs | 2 +- .../GameFinder/GameNotFoundException.cs | 2 +- .../ModSelectors/AutomaticModSelector.cs | 8 +++---- .../ModSelectors/ConsoleModSelector.cs | 8 +++---- .../ModSelectors/IModSelector.cs | 4 ++-- .../ModSelectors/ManualModSelector.cs | 4 ++-- .../ModSelectors/ModSelectorBase.cs | 6 ++--- .../ModSelectors/ModSelectorFactory.cs | 4 ++-- .../ModSelectors/SettingsBasedModSelector.cs | 6 ++--- .../ModSelectors/VerifyInstallationData.cs | 2 +- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 2 +- .../ModVerifyAppEnvironment.cs | 2 +- src/ModVerify.CliApp/ModVerifyApplication.cs | 21 ++++++++--------- src/ModVerify.CliApp/ModVerifyConstants.cs | 2 +- src/ModVerify.CliApp/Program.cs | 23 +++++++++---------- .../EngineInitializeProgressReporter.cs | 2 +- .../VerifyConsoleProgressReporter.cs | 10 ++++---- .../CommandLine/BaseModVerifyOptions.cs | 2 +- .../CommandLine/CreateBaselineVerbOption.cs | 2 +- .../CommandLine/ModVerifyOptionsParser.cs | 4 ++-- .../Settings/CommandLine/VerifyVerbOption.cs | 2 +- .../Settings/GameInstallationsSettings.cs | 2 +- .../Settings/ModVerifyAppSettings.cs | 2 +- .../Settings/ModVerifySettingsContainer.cs | 2 +- .../Settings/SettingsBuilder.cs | 4 ++-- .../Updates/GithubReleaseEntry.cs | 2 +- .../Updates/GithubReleaseList.cs | 2 +- .../Updates/ModVerifyUpdateMode.cs | 2 +- .../Updates/ModVerifyUpdater.cs | 8 +++---- .../Updates/ModVerifyUpdaterInformation.cs | 2 +- src/ModVerify.CliApp/Updates/UpdateInfo.cs | 2 +- .../Utilities/ExtensionMethods.cs | 2 +- .../Utilities/ModVerifyConsoleUtilities.cs | 2 +- .../ModVerifyOptionsParserTest.cs | 2 +- 35 files changed, 76 insertions(+), 78 deletions(-) diff --git a/src/ModVerify.CliApp/GameFinder/GameFinderResult.cs b/src/ModVerify.CliApp/GameFinder/GameFinderResult.cs index 3135955..beb3964 100644 --- a/src/ModVerify.CliApp/GameFinder/GameFinderResult.cs +++ b/src/ModVerify.CliApp/GameFinder/GameFinderResult.cs @@ -1,5 +1,5 @@ using PG.StarWarsGame.Infrastructure.Games; -namespace AET.ModVerifyTool.GameFinder; +namespace AET.ModVerify.App.GameFinder; internal record GameFinderResult(IGame Game, IGame? FallbackGame); \ No newline at end of file diff --git a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs index 3845156..82a0ecd 100644 --- a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs +++ b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs @@ -10,7 +10,7 @@ using PG.StarWarsGame.Infrastructure.Services; using PG.StarWarsGame.Infrastructure.Services.Detection; -namespace AET.ModVerifyTool.GameFinder; +namespace AET.ModVerify.App.GameFinder; internal class GameFinderService { diff --git a/src/ModVerify.CliApp/GameFinder/GameNotFoundException.cs b/src/ModVerify.CliApp/GameFinder/GameNotFoundException.cs index 76af676..37b0386 100644 --- a/src/ModVerify.CliApp/GameFinder/GameNotFoundException.cs +++ b/src/ModVerify.CliApp/GameFinder/GameNotFoundException.cs @@ -1,5 +1,5 @@ using PG.StarWarsGame.Infrastructure.Games; -namespace AET.ModVerifyTool.GameFinder; +namespace AET.ModVerify.App.GameFinder; internal class GameNotFoundException(string message) : GameException(message); \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs index 11f4207..1137263 100644 --- a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs @@ -2,9 +2,9 @@ using System.Globalization; using System.IO.Abstractions; using System.Linq; -using AET.ModVerifyTool.GameFinder; -using AET.ModVerifyTool.Settings; -using AET.ModVerifyTool.Utilities; +using AET.ModVerify.App.GameFinder; +using AET.ModVerify.App.Settings; +using AET.ModVerify.App.Utilities; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; @@ -14,7 +14,7 @@ using PG.StarWarsGame.Infrastructure.Services; using PG.StarWarsGame.Infrastructure.Services.Detection; -namespace AET.ModVerifyTool.ModSelectors; +namespace AET.ModVerify.App.ModSelectors; internal class AutomaticModSelector(IServiceProvider serviceProvider) : ModSelectorBase(serviceProvider) { diff --git a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs index ee9c16c..c776d6d 100644 --- a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs @@ -1,16 +1,16 @@ using System; using System.Collections.Generic; using AET.Modinfo.Spec; -using AET.ModVerifyTool.GameFinder; -using AET.ModVerifyTool.Settings; -using AET.ModVerifyTool.Utilities; +using AET.ModVerify.App.GameFinder; +using AET.ModVerify.App.Settings; +using AET.ModVerify.App.Utilities; using AnakinRaW.ApplicationBase; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; using PG.StarWarsGame.Infrastructure.Games; using PG.StarWarsGame.Infrastructure.Mods; -namespace AET.ModVerifyTool.ModSelectors; +namespace AET.ModVerify.App.ModSelectors; internal class ConsoleModSelector(IServiceProvider serviceProvider) : ModSelectorBase(serviceProvider) { diff --git a/src/ModVerify.CliApp/ModSelectors/IModSelector.cs b/src/ModVerify.CliApp/ModSelectors/IModSelector.cs index b92ac47..a04858c 100644 --- a/src/ModVerify.CliApp/ModSelectors/IModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/IModSelector.cs @@ -1,8 +1,8 @@ -using AET.ModVerifyTool.Settings; +using AET.ModVerify.App.Settings; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; -namespace AET.ModVerifyTool.ModSelectors; +namespace AET.ModVerify.App.ModSelectors; internal interface IModSelector { diff --git a/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs index fb6e77c..34cf39d 100644 --- a/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs @@ -1,9 +1,9 @@ using System; -using AET.ModVerifyTool.Settings; +using AET.ModVerify.App.Settings; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; -namespace AET.ModVerifyTool.ModSelectors; +namespace AET.ModVerify.App.ModSelectors; internal class ManualModSelector(IServiceProvider serviceProvider) : ModSelectorBase(serviceProvider) { diff --git a/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs b/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs index 6dbcbe0..8dd1d90 100644 --- a/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs +++ b/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using AET.ModVerifyTool.GameFinder; -using AET.ModVerifyTool.Settings; +using AET.ModVerify.App.GameFinder; +using AET.ModVerify.App.Settings; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; @@ -10,7 +10,7 @@ using PG.StarWarsGame.Infrastructure.Mods; using PG.StarWarsGame.Infrastructure.Services.Dependencies; -namespace AET.ModVerifyTool.ModSelectors; +namespace AET.ModVerify.App.ModSelectors; internal abstract class ModSelectorBase : IModSelector { diff --git a/src/ModVerify.CliApp/ModSelectors/ModSelectorFactory.cs b/src/ModVerify.CliApp/ModSelectors/ModSelectorFactory.cs index 80eaaff..07ef263 100644 --- a/src/ModVerify.CliApp/ModSelectors/ModSelectorFactory.cs +++ b/src/ModVerify.CliApp/ModSelectors/ModSelectorFactory.cs @@ -1,7 +1,7 @@ using System; -using AET.ModVerifyTool.Settings; +using AET.ModVerify.App.Settings; -namespace AET.ModVerifyTool.ModSelectors; +namespace AET.ModVerify.App.ModSelectors; internal class ModSelectorFactory(IServiceProvider serviceProvider) { diff --git a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs index 3f3e4b0..221bb9e 100644 --- a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs @@ -1,11 +1,11 @@ using System; using System.Linq; -using AET.ModVerifyTool.GameFinder; -using AET.ModVerifyTool.Settings; +using AET.ModVerify.App.GameFinder; +using AET.ModVerify.App.Settings; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; -namespace AET.ModVerifyTool.ModSelectors; +namespace AET.ModVerify.App.ModSelectors; internal class SettingsBasedModSelector(IServiceProvider serviceProvider) { diff --git a/src/ModVerify.CliApp/ModSelectors/VerifyInstallationData.cs b/src/ModVerify.CliApp/ModSelectors/VerifyInstallationData.cs index 3558503..1a1fcd2 100644 --- a/src/ModVerify.CliApp/ModSelectors/VerifyInstallationData.cs +++ b/src/ModVerify.CliApp/ModSelectors/VerifyInstallationData.cs @@ -1,7 +1,7 @@ using System.Text; using PG.StarWarsGame.Engine; -namespace AET.ModVerifyTool.ModSelectors; +namespace AET.ModVerify.App.ModSelectors; internal sealed class VerifyInstallationData { diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 7cd3c57..96033b6 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -3,7 +3,7 @@ net9.0;net481 Exe - AET.ModVerifyTool + AET.ModVerify.App ModVerify $(RepoRootPath)aet.ico AlamoEngineTools.ModVerify.CliApp diff --git a/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs b/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs index de79246..ff1758a 100644 --- a/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs +++ b/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs @@ -8,7 +8,7 @@ using AnakinRaW.CommonUtilities.DownloadManager.Configuration; #endif -namespace AET.ModVerifyTool; +namespace AET.ModVerify.App; internal sealed class ModVerifyAppEnvironment(Assembly assembly, IFileSystem fileSystem) #if NET diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index cbdaf9b..2cb32af 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -1,25 +1,24 @@ -using AET.ModVerifyTool.ModSelectors; -using AET.ModVerifyTool.Reporting; -using AnakinRaW.ApplicationBase; -using AnakinRaW.ApplicationBase.Utilities; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Serilog; -using System; +using System; using System.Collections.Generic; using System.IO; using System.IO.Abstractions; using System.Linq; using System.Threading; using System.Threading.Tasks; -using AET.ModVerify; +using AET.ModVerify.App.ModSelectors; +using AET.ModVerify.App.Reporting; +using AET.ModVerify.App.Settings; using AET.ModVerify.Pipeline; using AET.ModVerify.Reporting; -using AET.ModVerifyTool.Settings; +using AnakinRaW.ApplicationBase; +using AnakinRaW.ApplicationBase.Utilities; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; +using Serilog; using ILogger = Microsoft.Extensions.Logging.ILogger; -namespace AET.ModVerifyTool; +namespace AET.ModVerify.App; internal sealed class ModVerifyApplication(ModVerifyAppSettings settings, IServiceProvider services) { diff --git a/src/ModVerify.CliApp/ModVerifyConstants.cs b/src/ModVerify.CliApp/ModVerifyConstants.cs index 9bd2507..6b60f06 100644 --- a/src/ModVerify.CliApp/ModVerifyConstants.cs +++ b/src/ModVerify.CliApp/ModVerifyConstants.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Logging; -namespace AET.ModVerifyTool; +namespace AET.ModVerify.App; internal static class ModVerifyConstants { diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 9db0f4c..5b8f896 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -1,10 +1,18 @@ -using AET.ModVerify; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO.Abstractions; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using AET.ModVerify.App.Settings; +using AET.ModVerify.App.Settings.CommandLine; +using AET.ModVerify.App.Updates; +using AET.ModVerify.App.Utilities; using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Reporters; using AET.ModVerify.Reporting.Reporters.JSON; using AET.ModVerify.Reporting.Reporters.Text; using AET.ModVerify.Reporting.Settings; -using AET.ModVerifyTool.Updates; using AET.SteamAbstraction; using AnakinRaW.ApplicationBase; using AnakinRaW.ApplicationBase.Environment; @@ -32,19 +40,10 @@ using Serilog.Expressions; using Serilog.Filters; using Serilog.Sinks.SystemConsole.Themes; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO.Abstractions; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using AET.ModVerifyTool.Settings; -using AET.ModVerifyTool.Settings.CommandLine; -using AET.ModVerifyTool.Utilities; using Testably.Abstractions; using ILogger = Serilog.ILogger; -namespace AET.ModVerifyTool; +namespace AET.ModVerify.App; internal class MainClass { diff --git a/src/ModVerify.CliApp/Reporting/EngineInitializeProgressReporter.cs b/src/ModVerify.CliApp/Reporting/EngineInitializeProgressReporter.cs index 69413c0..b994e97 100644 --- a/src/ModVerify.CliApp/Reporting/EngineInitializeProgressReporter.cs +++ b/src/ModVerify.CliApp/Reporting/EngineInitializeProgressReporter.cs @@ -1,6 +1,6 @@ using System; -namespace AET.ModVerifyTool.Reporting; +namespace AET.ModVerify.App.Reporting; internal sealed class EngineInitializeProgressReporter : IDisposable { diff --git a/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs b/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs index 37d3ca2..4700457 100644 --- a/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs +++ b/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs @@ -1,11 +1,11 @@ -using AnakinRaW.CommonUtilities; -using AnakinRaW.CommonUtilities.SimplePipeline.Progress; -using ShellProgressBar; -using System; +using System; using System.Threading; using AET.ModVerify.Pipeline.Progress; +using AnakinRaW.CommonUtilities; +using AnakinRaW.CommonUtilities.SimplePipeline.Progress; +using ShellProgressBar; -namespace AET.ModVerifyTool.Reporting; +namespace AET.ModVerify.App.Reporting; public sealed class VerifyConsoleProgressReporter(string toVerifyName) : DisposableObject, IVerifyProgressReporter { diff --git a/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs b/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs index 1fa7c7c..0e5e203 100644 --- a/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs @@ -3,7 +3,7 @@ using CommandLine; using PG.StarWarsGame.Engine; -namespace AET.ModVerifyTool.Settings.CommandLine; +namespace AET.ModVerify.App.Settings.CommandLine; internal abstract class BaseModVerifyOptions { diff --git a/src/ModVerify.CliApp/Settings/CommandLine/CreateBaselineVerbOption.cs b/src/ModVerify.CliApp/Settings/CommandLine/CreateBaselineVerbOption.cs index 1c9d518..dc60a73 100644 --- a/src/ModVerify.CliApp/Settings/CommandLine/CreateBaselineVerbOption.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/CreateBaselineVerbOption.cs @@ -1,6 +1,6 @@ using CommandLine; -namespace AET.ModVerifyTool.Settings.CommandLine; +namespace AET.ModVerify.App.Settings.CommandLine; [Verb("createBaseline", HelpText = "Verifies the specified game and creates a new baseline file at the specified location.")] internal sealed class CreateBaselineVerbOption : BaseModVerifyOptions diff --git a/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs b/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs index 4b65866..7862e52 100644 --- a/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs @@ -4,7 +4,7 @@ using System.IO.Abstractions; using System.Linq; using System.Threading; -using AET.ModVerifyTool.Utilities; +using AET.ModVerify.App.Utilities; using AnakinRaW.ApplicationBase.Environment; using AnakinRaW.ApplicationBase.Update.Options; using AnakinRaW.ExternalUpdater; @@ -12,7 +12,7 @@ using CommandLine.Text; using Microsoft.Extensions.Logging; -namespace AET.ModVerifyTool.Settings.CommandLine; +namespace AET.ModVerify.App.Settings.CommandLine; internal sealed class ModVerifyOptionsParser { diff --git a/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs b/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs index 7260b43..3fc5dc4 100644 --- a/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs @@ -1,7 +1,7 @@ using AET.ModVerify.Reporting; using CommandLine; -namespace AET.ModVerifyTool.Settings.CommandLine; +namespace AET.ModVerify.App.Settings.CommandLine; [Verb("verify", HelpText = "Verifies the specified game and reports the findings.")] internal sealed class VerifyVerbOption : BaseModVerifyOptions diff --git a/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs b/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs index a012449..00bc4f0 100644 --- a/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs +++ b/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs @@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using PG.StarWarsGame.Engine; -namespace AET.ModVerifyTool.Settings; +namespace AET.ModVerify.App.Settings; internal sealed record GameInstallationsSettings { diff --git a/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs b/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs index 8c204b8..a837e42 100644 --- a/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs +++ b/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs @@ -3,7 +3,7 @@ using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Settings; -namespace AET.ModVerifyTool.Settings; +namespace AET.ModVerify.App.Settings; internal sealed class ModVerifyAppSettings { diff --git a/src/ModVerify.CliApp/Settings/ModVerifySettingsContainer.cs b/src/ModVerify.CliApp/Settings/ModVerifySettingsContainer.cs index 317114f..e8ccf66 100644 --- a/src/ModVerify.CliApp/Settings/ModVerifySettingsContainer.cs +++ b/src/ModVerify.CliApp/Settings/ModVerifySettingsContainer.cs @@ -1,6 +1,6 @@ using AnakinRaW.ApplicationBase.Update.Options; -namespace AET.ModVerifyTool.Settings; +namespace AET.ModVerify.App.Settings; internal sealed class ModVerifySettingsContainer { diff --git a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs index b2a747d..ba10c62 100644 --- a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs @@ -2,14 +2,14 @@ using System.Collections.Generic; using System.IO; using System.IO.Abstractions; +using AET.ModVerify.App.Settings.CommandLine; using AET.ModVerify.Pipeline; using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Settings; -using AET.ModVerifyTool.Settings.CommandLine; using Microsoft.Extensions.Logging; -namespace AET.ModVerifyTool.Settings; +namespace AET.ModVerify.App.Settings; internal sealed class SettingsBuilder(IFileSystem fileSystem, ILoggerFactory? loggerFactory) { diff --git a/src/ModVerify.CliApp/Updates/GithubReleaseEntry.cs b/src/ModVerify.CliApp/Updates/GithubReleaseEntry.cs index 968e6a9..60b508d 100644 --- a/src/ModVerify.CliApp/Updates/GithubReleaseEntry.cs +++ b/src/ModVerify.CliApp/Updates/GithubReleaseEntry.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace AET.ModVerifyTool.Updates; +namespace AET.ModVerify.App.Updates; [JsonUnmappedMemberHandling(JsonUnmappedMemberHandling.Skip)] [method: JsonConstructor] diff --git a/src/ModVerify.CliApp/Updates/GithubReleaseList.cs b/src/ModVerify.CliApp/Updates/GithubReleaseList.cs index a54edfb..a6f0055 100644 --- a/src/ModVerify.CliApp/Updates/GithubReleaseList.cs +++ b/src/ModVerify.CliApp/Updates/GithubReleaseList.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -namespace AET.ModVerifyTool.Updates; +namespace AET.ModVerify.App.Updates; internal sealed class GithubReleaseList : List; \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdateMode.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdateMode.cs index 12f3757..882a6a2 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdateMode.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdateMode.cs @@ -1,4 +1,4 @@ -namespace AET.ModVerifyTool.Updates; +namespace AET.ModVerify.App.Updates; public enum ModVerifyUpdateMode { diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index 3199d7a..5c2b090 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -1,15 +1,15 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; +using System; using System.IO; using System.Linq; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; using AnakinRaW.ApplicationBase.Update.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Semver; -namespace AET.ModVerifyTool.Updates; +namespace AET.ModVerify.App.Updates; internal sealed class ModVerifyUpdater { diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdaterInformation.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdaterInformation.cs index 2fa1022..b16083a 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdaterInformation.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdaterInformation.cs @@ -1,7 +1,7 @@ using System.Diagnostics; using Semver; -namespace AET.ModVerifyTool.Updates; +namespace AET.ModVerify.App.Updates; internal static class ModVerifyUpdaterInformation { diff --git a/src/ModVerify.CliApp/Updates/UpdateInfo.cs b/src/ModVerify.CliApp/Updates/UpdateInfo.cs index b6a6505..c4af83f 100644 --- a/src/ModVerify.CliApp/Updates/UpdateInfo.cs +++ b/src/ModVerify.CliApp/Updates/UpdateInfo.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -namespace AET.ModVerifyTool.Updates; +namespace AET.ModVerify.App.Updates; internal readonly struct UpdateInfo { diff --git a/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs index 5749de6..b55c163 100644 --- a/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs +++ b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs @@ -2,7 +2,7 @@ using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure.Games; -namespace AET.ModVerifyTool.Utilities; +namespace AET.ModVerify.App.Utilities; internal static class ExtensionMethods { diff --git a/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs b/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs index e5dea7d..e99fb5f 100644 --- a/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs +++ b/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs @@ -1,7 +1,7 @@ using System; using AnakinRaW.ApplicationBase; -namespace AET.ModVerifyTool.Utilities; +namespace AET.ModVerify.App.Utilities; internal static class ModVerifyConsoleUtilities { diff --git a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs index 400a300..c8951c1 100644 --- a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs +++ b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.IO.Abstractions; using System.Reflection; -using AET.ModVerifyTool.Settings.CommandLine; +using AET.ModVerify.App.Settings.CommandLine; using AnakinRaW.ApplicationBase.Environment; using AnakinRaW.AppUpdaterFramework.Configuration; using Testably.Abstractions; From cf08182c4044b166d3400ebc56e20b33084329ed Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 9 Jun 2025 09:02:46 +0200 Subject: [PATCH 07/61] move option parsing from boostrap to app code --- src/ModVerify.CliApp/Program.cs | 57 ++++++----- .../CommandLine/ModVerifyOptionsContainer.cs | 12 +++ .../CommandLine/ModVerifyOptionsParser.cs | 32 ++---- .../Settings/CommandLine/VerifyVerbOption.cs | 2 + .../Settings/ModVerifyAppSettings.cs | 6 -- .../Settings/ModVerifySettingsContainer.cs | 12 --- .../Settings/SettingsBuilder.cs | 97 +++++++++++-------- .../Utilities/ExtensionMethods.cs | 10 +- .../ModVerifyOptionsParserTest.cs | 54 +++++------ 9 files changed, 149 insertions(+), 133 deletions(-) create mode 100644 src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsContainer.cs delete mode 100644 src/ModVerify.CliApp/Settings/ModVerifySettingsContainer.cs diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 5b8f896..bcca074 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -61,7 +61,7 @@ internal class Program : SelfUpdateableAppLifecycle private static readonly string ModVerifyRootNameSpace = typeof(Program).Namespace!; private static readonly CompiledExpression PrintToConsoleExpression = SerilogExpression.Compile($"EventId.Id = {ModVerifyConstants.ConsoleEventIdValue}"); - private static ModVerifySettingsContainer _settingsContainer = null!; + private static ModVerifyOptionsContainer _optionsContainer = null!; protected override async Task InitializeAppAsync(IReadOnlyList args) { @@ -71,10 +71,10 @@ protected override async Task InitializeAppAsync(IReadOnlyList args try { - var settings = new ModVerifyOptionsParser(ApplicationEnvironment, FileSystem, BootstrapLoggerFactory).Parse(args); - if (!settings.HasSettings) + var settings = new ModVerifyOptionsParser(ApplicationEnvironment, BootstrapLoggerFactory).Parse(args); + if (!settings.HasOptions) return 0xA0; - _settingsContainer = settings; + _optionsContainer = settings; return 0; } catch (Exception e) @@ -107,7 +107,7 @@ protected override void CreateAppServices(IServiceCollection services, IReadOnly sp => new JsonManifestLoader(sp)); } - if (_settingsContainer.ModVerifyAppSettings is null) + if (_optionsContainer.ModVerifyOptions is null) return; SteamAbstractionLayer.InitializeServices(services); @@ -125,7 +125,7 @@ protected override void CreateAppServices(IServiceCollection services, IReadOnly SetupVerifyReporting(services); - if (_settingsContainer.ModVerifyAppSettings.Offline) + if (_optionsContainer.ModVerifyOptions.OfflineMode) { services.AddSingleton(sp => new OfflineModNameResolver(sp)); services.AddSingleton(sp => new OfflineModGameTypeResolver(sp)); @@ -157,35 +157,48 @@ protected override IRegistry CreateRegistry() protected override async Task RunAppAsync(string[] args, IServiceProvider appServiceProvider) { var result = await HandleUpdate(appServiceProvider); - if (result != 0 || _settingsContainer.ModVerifyAppSettings is null) + if (result != 0 || _optionsContainer.ModVerifyOptions is null) return result; - return await new ModVerifyApplication(_settingsContainer.ModVerifyAppSettings, appServiceProvider).Run().ConfigureAwait(false); + + var modVerifySettings = new SettingsBuilder(appServiceProvider).BuildSettings(_optionsContainer.ModVerifyOptions); + + return await new ModVerifyApplication(modVerifySettings, appServiceProvider).Run().ConfigureAwait(false); } - private static void SetupVerifyReporting(IServiceCollection serviceCollection) + private void SetupVerifyReporting(IServiceCollection serviceCollection) { - var settings = _settingsContainer.ModVerifyAppSettings; - Debug.Assert(settings is not null); + var options = _optionsContainer.ModVerifyOptions; + Debug.Assert(options is not null); + + + var verifyVerb = options as VerifyVerbOption; - var printOnlySummary = settings.CreateNewBaseline; + // Console should be in minimal summary mode if we are not in verify mode. + var printOnlySummary = verifyVerb is null; + serviceCollection.RegisterConsoleReporter(new VerifyReportSettings { MinimumReportSeverity = VerificationSeverity.Error }, printOnlySummary); - if (string.IsNullOrEmpty(settings.ReportOutput)) + if (verifyVerb == null) return; + var outputDirectory = Environment.CurrentDirectory; + + if (!string.IsNullOrEmpty(verifyVerb.OutputDirectory)) + outputDirectory = FileSystem.Path.GetFullPath(FileSystem.Path.Combine(Environment.CurrentDirectory, verifyVerb.OutputDirectory!)); + serviceCollection.RegisterJsonReporter(new JsonReporterSettings { - OutputDirectory = settings.ReportOutput!, - MinimumReportSeverity = settings.GlobalReportSettings.MinimumReportSeverity + OutputDirectory = outputDirectory!, + MinimumReportSeverity = options.MinimumSeverity }); serviceCollection.RegisterTextFileReporter(new TextFileReporterSettings { - OutputDirectory = settings.ReportOutput!, - MinimumReportSeverity = settings.GlobalReportSettings.MinimumReportSeverity + OutputDirectory = outputDirectory!, + MinimumReportSeverity = options.MinimumSeverity }); } @@ -200,7 +213,7 @@ private void ConfigureLogging(ILoggingBuilder loggingBuilder) loggingBuilder.AddDebug(); #endif - if (_settingsContainer.ModVerifyAppSettings?.VerboseMode == true || _settingsContainer.UpdateOptions?.Verbose == true) + if (_optionsContainer.ModVerifyOptions?.Verbose == true || _optionsContainer.UpdateOptions?.Verbose == true) { logLevel = LogEventLevel.Verbose; loggingBuilder.AddDebug(); @@ -273,18 +286,18 @@ static bool IsXmlParserLogging(LogEvent logEvent) private async Task HandleUpdate(IServiceProvider serviceProvider) { - var updateOptions = _settingsContainer.UpdateOptions ?? new ApplicationUpdateOptions(); + var updateOptions = _optionsContainer.UpdateOptions ?? new ApplicationUpdateOptions(); ModVerifyUpdateMode updateMode; - if (_settingsContainer.ModVerifyAppSettings is not null) + if (_optionsContainer.ModVerifyOptions is not null) { - if (_settingsContainer.ModVerifyAppSettings.Offline) + if (_optionsContainer.ModVerifyOptions.OfflineMode) { Logger?.LogTrace("Running in offline mode. There will be nothing to update."); return 0; } - updateMode = _settingsContainer.ModVerifyAppSettings.Interactive + updateMode = _optionsContainer.ModVerifyOptions.LaunchedWithoutArguments() ? ModVerifyUpdateMode.InteractiveUpdate : ModVerifyUpdateMode.CheckOnly; } diff --git a/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsContainer.cs b/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsContainer.cs new file mode 100644 index 0000000..c9aab20 --- /dev/null +++ b/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsContainer.cs @@ -0,0 +1,12 @@ +using AnakinRaW.ApplicationBase.Update.Options; + +namespace AET.ModVerify.App.Settings.CommandLine; + +internal sealed class ModVerifyOptionsContainer +{ + public BaseModVerifyOptions? ModVerifyOptions { get; init; } + + public ApplicationUpdateOptions? UpdateOptions { get; init; } + + public bool HasOptions => ModVerifyOptions is not null || UpdateOptions is not null; +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs b/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs index 7862e52..1d01d4d 100644 --- a/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.IO.Abstractions; using System.Linq; using System.Threading; using AET.ModVerify.App.Utilities; @@ -17,25 +16,18 @@ namespace AET.ModVerify.App.Settings.CommandLine; internal sealed class ModVerifyOptionsParser { private readonly ApplicationEnvironment _applicationEnvironment; - private readonly IFileSystem _fileSystem; private readonly ILogger? _logger; - private readonly ILoggerFactory? _loggerFactory; [field: AllowNull, MaybeNull] private Type[] AvailableVerbTypes => LazyInitializer.EnsureInitialized(ref field, GetAvailableVerbTypes)!; - public ModVerifyOptionsParser( - ApplicationEnvironment applicationEnvironment, - IFileSystem fileSystem, - ILoggerFactory? loggerFactory) + public ModVerifyOptionsParser(ApplicationEnvironment applicationEnvironment, ILoggerFactory? loggerFactory) { _applicationEnvironment = applicationEnvironment; - _fileSystem = fileSystem; - _loggerFactory = loggerFactory; _logger = loggerFactory?.CreateLogger(GetType()); } - public ModVerifySettingsContainer Parse(IReadOnlyList args) + public ModVerifyOptionsContainer Parse(IReadOnlyList args) { // If the application is updatable (.NET Framework) we need to remove potential arguments from the external updater // in order to keep strict parsing rules enabled for better user error messages. @@ -45,24 +37,24 @@ public ModVerifySettingsContainer Parse(IReadOnlyList args) return ParseArguments(args); } - private ModVerifySettingsContainer ParseArguments(IReadOnlyList args) + private ModVerifyOptionsContainer ParseArguments(IReadOnlyList args) { // Empty arguments means that we are "interactive" mode (user simply double-clicked the executable) if (args.Count == 0) { - return new ModVerifySettingsContainer + return new ModVerifyOptionsContainer { - ModVerifyAppSettings = BuildSettings(new VerifyVerbOption()), + ModVerifyOptions = new VerifyVerbOption(), UpdateOptions = null }; } var parseResult = Parser.Default.ParseArguments(args, AvailableVerbTypes); - ModVerifyAppSettings? settings = null; + BaseModVerifyOptions? modVerifyOptions = null; ApplicationUpdateOptions? updateOptions = null; - parseResult.WithParsed(o => settings = BuildSettings(o)); + parseResult.WithParsed(o => modVerifyOptions = o); parseResult.WithParsed(o => updateOptions = o); parseResult.WithNotParsed(_ => @@ -71,9 +63,9 @@ private ModVerifySettingsContainer ParseArguments(IReadOnlyList args) Console.WriteLine(HelpText.AutoBuild(parseResult).ToString()); }); - return new ModVerifySettingsContainer + return new ModVerifyOptionsContainer { - ModVerifyAppSettings = settings, + ModVerifyOptions = modVerifyOptions, UpdateOptions = updateOptions, }; } @@ -94,12 +86,6 @@ public static IReadOnlyList StripExternalUpdateResults(IReadOnlyList !string.IsNullOrEmpty(NewBaselinePath); public string? NewBaselinePath { get; init; } - - public bool Offline { get; init; } - - public bool VerboseMode { get; init; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Settings/ModVerifySettingsContainer.cs b/src/ModVerify.CliApp/Settings/ModVerifySettingsContainer.cs deleted file mode 100644 index e8ccf66..0000000 --- a/src/ModVerify.CliApp/Settings/ModVerifySettingsContainer.cs +++ /dev/null @@ -1,12 +0,0 @@ -using AnakinRaW.ApplicationBase.Update.Options; - -namespace AET.ModVerify.App.Settings; - -internal sealed class ModVerifySettingsContainer -{ - public ModVerifyAppSettings? ModVerifyAppSettings { get; init; } - - public ApplicationUpdateOptions? UpdateOptions { get; init; } - - public bool HasSettings => ModVerifyAppSettings is not null || UpdateOptions is not null; -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs index ba10c62..d1a01e2 100644 --- a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs @@ -3,17 +3,20 @@ using System.IO; using System.IO.Abstractions; using AET.ModVerify.App.Settings.CommandLine; +using AET.ModVerify.App.Utilities; using AET.ModVerify.Pipeline; using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Settings; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace AET.ModVerify.App.Settings; -internal sealed class SettingsBuilder(IFileSystem fileSystem, ILoggerFactory? loggerFactory) +internal sealed class SettingsBuilder(IServiceProvider serviceProvider) { - private readonly ILogger? _logger = loggerFactory?.CreateLogger(typeof(SettingsBuilder)); + private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(SettingsBuilder)); + private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); public ModVerifyAppSettings BuildSettings(BaseModVerifyOptions options) { @@ -29,12 +32,6 @@ public ModVerifyAppSettings BuildSettings(BaseModVerifyOptions options) private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) { - var output = Environment.CurrentDirectory; - var outDir = verifyOptions.OutputDirectory; - - if (!string.IsNullOrEmpty(outDir)) - output = fileSystem.Path.GetFullPath(fileSystem.Path.Combine(Environment.CurrentDirectory, outDir!)); - return new ModVerifyAppSettings { VerifyPipelineSettings = new VerifyPipelineSettings @@ -51,9 +48,6 @@ private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) AppThrowsOnMinimumSeverity = verifyOptions.MinimumFailureSeverity, GameInstallationsSettings = BuildInstallationSettings(verifyOptions), GlobalReportSettings = BuilderGlobalReportSettings(verifyOptions), - ReportOutput = output, - Offline = verifyOptions.OfflineMode, - VerboseMode = verifyOptions.Verbose }; VerificationSeverity? GetVerifierMinimumThrowSeverity() @@ -97,47 +91,25 @@ private ModVerifyAppSettings BuildFromCreateBaselineVerb(CreateBaselineVerbOptio GameInstallationsSettings = BuildInstallationSettings(baselineVerb), GlobalReportSettings = BuilderGlobalReportSettings(baselineVerb), NewBaselinePath = baselineVerb.OutputFile, - ReportOutput = null, - Offline = baselineVerb.OfflineMode, - VerboseMode = baselineVerb.Verbose }; } private GlobalVerifyReportSettings BuilderGlobalReportSettings(BaseModVerifyOptions options) { + var baseline = new BaselineFactory(serviceProvider).CreateBaseline(options); + return new GlobalVerifyReportSettings { - Baseline = CreateBaseline(), + Baseline = baseline, Suppressions = CreateSuppressions(), MinimumReportSeverity = options.MinimumSeverity, }; - VerificationBaseline CreateBaseline() - { - // It does not make sense to create a baseline on another baseline. - if (options is not VerifyVerbOption verifyOptions || string.IsNullOrEmpty(verifyOptions.Baseline)) - return VerificationBaseline.Empty; - - using var fs = fileSystem.FileStream.New(verifyOptions.Baseline!, FileMode.Open, FileAccess.Read); - - try - { - return VerificationBaseline.FromJson(fs); - } - catch (IncompatibleBaselineException) - { - Console.WriteLine($"The baseline '{verifyOptions.Baseline}' is not compatible with with version of ModVerify." + - $"{Environment.NewLine}Please generate a new baseline file or download the latest version." + - $"{Environment.NewLine}"); - throw; - } - } - SuppressionList CreateSuppressions() { if (options.Suppressions is null) return SuppressionList.Empty; - using var fs = fileSystem.FileStream.New(options.Suppressions, FileMode.Open, FileAccess.Read); + using var fs = _fileSystem.FileStream.New(options.Suppressions, FileMode.Open, FileAccess.Read); return SuppressionList.FromJson(fs); } } @@ -150,7 +122,7 @@ private GameInstallationsSettings BuildInstallationSettings(BaseModVerifyOptions foreach (var mod in options.ModPaths) { if (!string.IsNullOrEmpty(mod)) - modPaths.Add(fileSystem.Path.GetFullPath(mod)); + modPaths.Add(_fileSystem.Path.GetFullPath(mod)); } } @@ -160,22 +132,22 @@ private GameInstallationsSettings BuildInstallationSettings(BaseModVerifyOptions foreach (var fallback in options.AdditionalFallbackPath) { if (!string.IsNullOrEmpty(fallback)) - fallbackPaths.Add(fileSystem.Path.GetFullPath(fallback)); + fallbackPaths.Add(_fileSystem.Path.GetFullPath(fallback)); } } var gamePath = options.GamePath; if (!string.IsNullOrEmpty(gamePath)) - gamePath = fileSystem.Path.GetFullPath(gamePath!); + gamePath = _fileSystem.Path.GetFullPath(gamePath!); string? fallbackGamePath = null; if (!string.IsNullOrEmpty(gamePath) && !string.IsNullOrEmpty(options.FallbackGamePath)) - fallbackGamePath = fileSystem.Path.GetFullPath(options.FallbackGamePath!); + fallbackGamePath = _fileSystem.Path.GetFullPath(options.FallbackGamePath!); var autoPath = options.AutoPath; if (!string.IsNullOrEmpty(autoPath)) - autoPath = fileSystem.Path.GetFullPath(autoPath!); + autoPath = _fileSystem.Path.GetFullPath(autoPath!); return new GameInstallationsSettings { @@ -187,4 +159,45 @@ private GameInstallationsSettings BuildInstallationSettings(BaseModVerifyOptions EngineType = options.GameType }; } +} + +internal sealed class BaselineFactory(IServiceProvider serviceProvider) +{ + private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(BaselineFactory)); + private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); + + public VerificationBaseline CreateBaseline(BaseModVerifyOptions options) + { + if (options.LaunchedWithoutArguments()) + return SearchBaselineInKnownDirectories(); + + // It does not make sense to create a baseline on another baseline. + if (options is not VerifyVerbOption verifyOptions || string.IsNullOrEmpty(verifyOptions.Baseline)) + return VerificationBaseline.Empty; + + return CreateBaselineFromFilePath(verifyOptions.Baseline); + } + + private VerificationBaseline SearchBaselineInKnownDirectories() + { + _logger?.LogTrace("Searching for nearby baseline files."); + // TODO + return VerificationBaseline.Empty; + } + + private VerificationBaseline CreateBaselineFromFilePath(string baselineFile) + { + using var fs = _fileSystem.FileStream.New(baselineFile, FileMode.Open, FileAccess.Read); + try + { + return VerificationBaseline.FromJson(fs); + } + catch (IncompatibleBaselineException) + { + Console.WriteLine($"The baseline '{baselineFile}' is not compatible with this version of ModVerify." + + $"{Environment.NewLine}Please generate a new baseline file or download the latest version." + + $"{Environment.NewLine}"); + throw; + } + } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs index b55c163..325fa7e 100644 --- a/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs +++ b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs @@ -1,4 +1,5 @@ -using AnakinRaW.ApplicationBase.Environment; +using AET.ModVerify.App.Settings.CommandLine; +using AnakinRaW.ApplicationBase.Environment; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure.Games; @@ -26,4 +27,11 @@ public static bool IsUpdatable(this ApplicationEnvironment applicationEnvironmen updatableEnvironment = applicationEnvironment as UpdatableApplicationEnvironment; return updatableEnvironment is not null; } + + public static bool LaunchedWithoutArguments(this BaseModVerifyOptions options) + { + if (options is VerifyVerbOption verifyOptions) + return verifyOptions.IsRunningWithoutArguments; + return false; + } } \ No newline at end of file diff --git a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs index c8951c1..3f54ebd 100644 --- a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs +++ b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs @@ -25,8 +25,8 @@ public void Parse_UpdateAppArg() var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); - Assert.True(settings.HasSettings); - Assert.Null(settings.ModVerifyAppSettings); + Assert.True(settings.HasOptions); + Assert.Null(settings.ModVerifyOptions); Assert.NotNull(settings.UpdateOptions); Assert.Equal("test", settings.UpdateOptions.BranchName); Assert.Equal("https://examlple.com", settings.UpdateOptions.ManifestUrl); @@ -52,8 +52,8 @@ public void Parse_InvalidArgs_NotUpdateable(string argString) { var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); - Assert.False(settings.HasSettings); - Assert.Null(settings.ModVerifyAppSettings); + Assert.False(settings.HasOptions); + Assert.Null(settings.ModVerifyOptions); Assert.Null(settings.UpdateOptions); } } @@ -78,11 +78,11 @@ public void Parse_NoArgs_IsVerify_IsInteractive() { var settings = Parser.Parse([]); - Assert.True(settings.HasSettings); - Assert.NotNull(settings.ModVerifyAppSettings); + Assert.True(settings.HasOptions); + Assert.NotNull(settings.ModVerifyOptions); - Assert.False(settings.ModVerifyAppSettings.CreateNewBaseline); - Assert.True(settings.ModVerifyAppSettings.Interactive); + Assert.False(settings.ModVerifyOptions.CreateNewBaseline); + Assert.True(settings.ModVerifyOptions.Interactive); Assert.Null(settings.UpdateOptions); } @@ -96,11 +96,11 @@ public void Parse_Interactive(string argString, bool createBaseLine) { var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); - Assert.True(settings.HasSettings); - Assert.NotNull(settings.ModVerifyAppSettings); + Assert.True(settings.HasOptions); + Assert.NotNull(settings.ModVerifyOptions); - Assert.True(settings.ModVerifyAppSettings.Interactive); - Assert.Equal(createBaseLine, settings.ModVerifyAppSettings.CreateNewBaseline); + Assert.True(settings.ModVerifyOptions.Interactive); + Assert.Equal(createBaseLine, settings.ModVerifyOptions.CreateNewBaseline); Assert.Null(settings.UpdateOptions); } @@ -113,11 +113,11 @@ public void Parse_NotInteractive(string argString, bool createBaseLine) { var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); - Assert.True(settings.HasSettings); - Assert.NotNull(settings.ModVerifyAppSettings); + Assert.True(settings.HasOptions); + Assert.NotNull(settings.ModVerifyOptions); - Assert.False(settings.ModVerifyAppSettings.Interactive); - Assert.Equal(createBaseLine, settings.ModVerifyAppSettings.CreateNewBaseline); + Assert.False(settings.ModVerifyOptions.Interactive); + Assert.Equal(createBaseLine, settings.ModVerifyOptions.CreateNewBaseline); Assert.Null(settings.UpdateOptions); } @@ -130,8 +130,8 @@ public void Parse_InvalidPathConfig(string argString) { var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); - Assert.False(settings.HasSettings); - Assert.Null(settings.ModVerifyAppSettings); + Assert.False(settings.HasOptions); + Assert.Null(settings.ModVerifyOptions); Assert.Null(settings.UpdateOptions); } @@ -152,8 +152,8 @@ public void Parse_InvalidArgs(string argString) { var settings = Parser.Parse(argString.Split(' ')); - Assert.False(settings.HasSettings); - Assert.Null(settings.ModVerifyAppSettings); + Assert.False(settings.HasOptions); + Assert.Null(settings.ModVerifyOptions); Assert.Null(settings.UpdateOptions); } @@ -166,14 +166,14 @@ public void Parse_UpdatePerformed_RestartedFromNoArgs() var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); if (!IsUpdatable) - Assert.False(settings.HasSettings); + Assert.False(settings.HasOptions); else { - Assert.True(settings.HasSettings); - Assert.NotNull(settings.ModVerifyAppSettings); + Assert.True(settings.HasOptions); + Assert.NotNull(settings.ModVerifyOptions); - Assert.False(settings.ModVerifyAppSettings.CreateNewBaseline); - Assert.True(settings.ModVerifyAppSettings.Interactive); + Assert.False(settings.ModVerifyOptions.CreateNewBaseline); + Assert.True(settings.ModVerifyOptions.Interactive); Assert.Null(settings.UpdateOptions); } @@ -186,8 +186,8 @@ public void Parse_CreateBaseline_MissingRequired_Fails(string argString) { var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); - Assert.False(settings.HasSettings); - Assert.Null(settings.ModVerifyAppSettings); + Assert.False(settings.HasOptions); + Assert.Null(settings.ModVerifyOptions); Assert.Null(settings.UpdateOptions); } } From ddad69398f7b41b8bedf48d626679dbf33091e4f Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 9 Jun 2025 09:23:31 +0200 Subject: [PATCH 08/61] update deps --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 5 +++-- .../Utilities/ModVerifyConsoleUtilities.cs | 10 ++++++---- .../PG.StarWarsGame.Engine.csproj | 2 +- .../ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj | 4 ++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 96033b6..7357e5c 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -24,8 +24,9 @@ - - + + + diff --git a/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs b/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs index e99fb5f..6e6664a 100644 --- a/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs +++ b/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs @@ -1,9 +1,11 @@ -using System; -using AnakinRaW.ApplicationBase; +using AnakinRaW.ApplicationBase; +using Figgle; +using System; namespace AET.ModVerify.App.Utilities; -internal static class ModVerifyConsoleUtilities +[GenerateFiggleText("HeaderText", "standard", ModVerifyConstants.AppNameString)] +internal static partial class ModVerifyConsoleUtilities { public static void WriteHeader(string? version = null) { @@ -11,7 +13,7 @@ public static void WriteHeader(string? version = null) const string author = "by AnakinRaW"; ConsoleUtilities.WriteHorizontalLine('*', lineLength); - Console.WriteLine(Figgle.FiggleFonts.Standard.Render(ModVerifyConstants.AppNameString)); + Console.WriteLine(HeaderText); if (!string.IsNullOrEmpty(version)) { Console.ForegroundColor = ConsoleColor.DarkGray; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index 8bb3a0a..5fa40bc 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -26,7 +26,7 @@ - + diff --git a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj index 657d4f9..1698d94 100644 --- a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj +++ b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj @@ -11,11 +11,11 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive From e8c4c5a43cfd26fe4d5b4d2c7d8aea98d9cc902a Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 9 Jun 2025 09:23:53 +0200 Subject: [PATCH 09/61] update subs --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 661b95b..d95bdce 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 661b95b1b21d766120af90be78c2bac7869bd1cf +Subproject commit d95bdce7cf7024c113028643f969fbb55833f564 From 27f9a1f31597a5b5bea2932e472f5303db6ec9ca Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 9 Jun 2025 09:25:39 +0200 Subject: [PATCH 10/61] update sub --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index d95bdce..746a05e 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit d95bdce7cf7024c113028643f969fbb55833f564 +Subproject commit 746a05eb17524637fc156886f16b967bb46f8864 From 023e3b634d51dad506996c60df2c86e1e04501b8 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 1 Jul 2025 09:07:59 +0200 Subject: [PATCH 11/61] update deps --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 14 +++++++------- .../PG.StarWarsGame.Engine.csproj | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 7357e5c..c3a69f0 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -24,16 +24,16 @@ - - + + - - - - - + + + + + diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index 5fa40bc..47f5955 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -26,7 +26,7 @@ - + From 79467ac19e5ad78181ec79cbf1a3088ece86c62e Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 1 Jul 2025 09:08:30 +0200 Subject: [PATCH 12/61] add settings build exception handling --- src/ModVerify.CliApp/Program.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index bcca074..c7cc6ec 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -160,7 +160,18 @@ protected override async Task RunAppAsync(string[] args, IServiceProvider a if (result != 0 || _optionsContainer.ModVerifyOptions is null) return result; - var modVerifySettings = new SettingsBuilder(appServiceProvider).BuildSettings(_optionsContainer.ModVerifyOptions); + ModVerifyAppSettings modVerifySettings; + + try + { + modVerifySettings = new SettingsBuilder(appServiceProvider).BuildSettings(_optionsContainer.ModVerifyOptions); + } + catch (Exception e) + { + Logger?.LogCritical(e, $"Failed to create settings form commandline arguments: {e.Message}"); + ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, e); + return e.HResult; + } return await new ModVerifyApplication(modVerifySettings, appServiceProvider).Run().ConfigureAwait(false); } From 748bc4277d37fd42d329074c4e42a9a24190fc0f Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 1 Jul 2025 09:09:23 +0200 Subject: [PATCH 13/61] add json schema for baseline --- .../Settings/SettingsBuilder.cs | 2 +- src/ModVerify/ModVerify.csproj | 11 +- .../IncompatibleBaselineException.cs | 8 -- .../Reporting/InvalidBaselineException.cs | 14 ++ .../Reporting/Json/JsonBaselineParser.cs | 41 ++++++ .../Reporting/Json/JsonBaselineSchema.cs | 123 ++++++++++++++++++ .../Reporting/VerificationBaseline.cs | 12 +- .../Resources/Schemas/2.0/baseline.json | 70 ++++++++++ 8 files changed, 262 insertions(+), 19 deletions(-) delete mode 100644 src/ModVerify/Reporting/IncompatibleBaselineException.cs create mode 100644 src/ModVerify/Reporting/InvalidBaselineException.cs create mode 100644 src/ModVerify/Reporting/Json/JsonBaselineParser.cs create mode 100644 src/ModVerify/Reporting/Json/JsonBaselineSchema.cs create mode 100644 src/ModVerify/Resources/Schemas/2.0/baseline.json diff --git a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs index d1a01e2..571d177 100644 --- a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs @@ -192,7 +192,7 @@ private VerificationBaseline CreateBaselineFromFilePath(string baselineFile) { return VerificationBaseline.FromJson(fs); } - catch (IncompatibleBaselineException) + catch (InvalidBaselineException) { Console.WriteLine($"The baseline '{baselineFile}' is not compatible with this version of ModVerify." + $"{Environment.NewLine}Please generate a new baseline file or download the latest version." + diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index c0a09df..3d7a664 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -24,10 +24,19 @@ snupkg + + + + + + + + - + + diff --git a/src/ModVerify/Reporting/IncompatibleBaselineException.cs b/src/ModVerify/Reporting/IncompatibleBaselineException.cs deleted file mode 100644 index c9a9eb1..0000000 --- a/src/ModVerify/Reporting/IncompatibleBaselineException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace AET.ModVerify.Reporting; - -public sealed class IncompatibleBaselineException : Exception -{ - public override string Message => "The specified baseline is not compatible to this version of the application."; -} \ No newline at end of file diff --git a/src/ModVerify/Reporting/InvalidBaselineException.cs b/src/ModVerify/Reporting/InvalidBaselineException.cs new file mode 100644 index 0000000..37ab9c8 --- /dev/null +++ b/src/ModVerify/Reporting/InvalidBaselineException.cs @@ -0,0 +1,14 @@ +using System; + +namespace AET.ModVerify.Reporting; + +public sealed class InvalidBaselineException : Exception +{ + public InvalidBaselineException(string message) : base(message) + { + } + + public InvalidBaselineException(string? message, Exception? inner) : base(message, inner) + { + } +} \ No newline at end of file diff --git a/src/ModVerify/Reporting/Json/JsonBaselineParser.cs b/src/ModVerify/Reporting/Json/JsonBaselineParser.cs new file mode 100644 index 0000000..9441524 --- /dev/null +++ b/src/ModVerify/Reporting/Json/JsonBaselineParser.cs @@ -0,0 +1,41 @@ +using System; +using System.IO; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace AET.ModVerify.Reporting.Json; + +public static class JsonBaselineParser +{ + public static VerificationBaseline Parse(Stream dataStream) + { + if (dataStream == null) + throw new ArgumentNullException(nameof(dataStream)); + try + { + var jsonNode = JsonNode.Parse(dataStream); + var jsonBaseline = ParseCore(jsonNode); + + if (jsonBaseline is null) + throw new InvalidBaselineException($"Unable to parse input from stream to {nameof(VerificationBaseline)}. Unknown Error!"); + + if (jsonBaseline.Version != VerificationBaseline.LatestVersion) + throw new InvalidBaselineException("The parsed baseline version does not match the version used by this version of ModVerify."); + + return new VerificationBaseline(jsonBaseline); + } + catch (JsonException cause) + { + throw new InvalidBaselineException(cause.Message, cause); + } + } + + private static JsonVerificationBaseline? ParseCore(JsonNode? jsonData) + { + if (jsonData is null) + return null; + + JsonBaselineSchema.Evaluate(jsonData); + return jsonData.Deserialize(); + } +} \ No newline at end of file diff --git a/src/ModVerify/Reporting/Json/JsonBaselineSchema.cs b/src/ModVerify/Reporting/Json/JsonBaselineSchema.cs new file mode 100644 index 0000000..7c8b02a --- /dev/null +++ b/src/ModVerify/Reporting/Json/JsonBaselineSchema.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Text.Json.Nodes; +using Json.Schema; + +namespace AET.ModVerify.Reporting.Json; + +public static class JsonBaselineSchema +{ + private static readonly JsonSchema Schema; + private static readonly EvaluationOptions EvaluationOptions; + + static JsonBaselineSchema() + { + var evalvOptions = new EvaluationOptions + { + EvaluateAs = SpecVersion.Draft202012, + OutputFormat = OutputFormat.Hierarchical, + AllowReferencesIntoUnknownKeywords = false + }; + + Schema = GetCurrentSchema(); + EvaluationOptions = evalvOptions; + } + + /// + /// Evaluates a JSON node against the ModVerify Baseline JSON schema. + /// + /// The JSON node to evaluate. + /// is not valid against the baseline JSON schema. + /// is . + public static void Evaluate(JsonNode json) + { + if (json == null) + throw new ArgumentNullException(nameof(json)); + var result = Schema.Evaluate(json, EvaluationOptions); + ThrowOnValidationError(result); + } + + private static void ThrowOnValidationError(EvaluationResults result) + { + if (!result.IsValid) + { + var error = GetFirstError(result); + var errorMessage = "Baseline JSON not valid"; + + if (error is null) + errorMessage += ": Unknown Error"; + else + errorMessage += $": {error}"; + + throw new InvalidBaselineException(errorMessage); + } + } + + private static KeyValuePair? GetFirstError(EvaluationResults result) + { + if (result.HasErrors) + return result.Errors!.First(); + foreach (var child in result.Details) + { + var error = GetFirstError(child); + if (error is not null) + return error; + } + return null; + } + + private static JsonSchema GetCurrentSchema() + { + using var resourceStream = typeof(JsonBaselineSchema) + .Assembly.GetManifestResourceStream($"AET.ModVerify.Resources.Schemas.{GetVersionedPath()}.baseline.json"); + + Debug.Assert(resourceStream is not null); + var schema = JsonSchema.FromStream(resourceStream!).GetAwaiter().GetResult(); + + var id = schema.GetId(); + if (id is null || !UriContainsVersion(id, VerificationBaseline.LatestVersionString)) + throw new InvalidOperationException("Internal error: The embedded schema version does not match the expected baseline version!"); + + return schema; + } + + private static bool UriContainsVersion(Uri id, string latestVersionString) + { + foreach (var segment in id.Segments) + { + var trimmed = segment.AsSpan().TrimEnd('/'); + if (trimmed.Equals(latestVersionString, StringComparison.OrdinalIgnoreCase)) + return true; + } + return false; + } + + private static string GetVersionedPath() + { + var version = VerificationBaseline.LatestVersion; + var sb = new StringBuilder(); + + AddVersionSegment(version.Major, ref sb); + AddVersionSegment(version.Minor, ref sb); + AddVersionSegment(version.Build, ref sb); + AddVersionSegment(version.Revision, ref sb); + + // Remove the trailing dot + sb.Length -= 1; + + return sb.ToString(); + + static void AddVersionSegment(int segment, ref StringBuilder sb) + { + if (segment >= 0) + { + sb.Append('_'); + sb.Append(segment); + sb.Append("."); + } + } + } +} \ No newline at end of file diff --git a/src/ModVerify/Reporting/VerificationBaseline.cs b/src/ModVerify/Reporting/VerificationBaseline.cs index 55a8973..c37539b 100644 --- a/src/ModVerify/Reporting/VerificationBaseline.cs +++ b/src/ModVerify/Reporting/VerificationBaseline.cs @@ -11,7 +11,8 @@ namespace AET.ModVerify.Reporting; public sealed class VerificationBaseline : IReadOnlyCollection { - private static readonly Version LatestVersion = new(2, 0); + public static readonly Version LatestVersion = new(2, 0); + public static readonly string LatestVersionString = LatestVersion.ToString(2); public static readonly VerificationBaseline Empty = new(VerificationSeverity.Information, []); @@ -60,14 +61,7 @@ public Task ToJsonAsync(Stream stream) public static VerificationBaseline FromJson(Stream stream) { - var baselineJson = JsonSerializer.Deserialize(stream, JsonSerializerOptions.Default); - if (baselineJson is null) - throw new InvalidOperationException("Unable to deserialize baseline."); - - if (baselineJson.Version is null || baselineJson.Version != LatestVersion) - throw new IncompatibleBaselineException(); - - return new VerificationBaseline(baselineJson); + return JsonBaselineParser.Parse(stream); } /// diff --git a/src/ModVerify/Resources/Schemas/2.0/baseline.json b/src/ModVerify/Resources/Schemas/2.0/baseline.json new file mode 100644 index 0000000..2520a58 --- /dev/null +++ b/src/ModVerify/Resources/Schemas/2.0/baseline.json @@ -0,0 +1,70 @@ +{ + "$id": "https://AlamoEngine-Tools.github.io/schemas/mod-verify/2.0/baseline", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Represents a baseline for AET ModVerify", + "type": "object", + "$defs": { + "severity": { + "enum": [ "Information", "Warning", "Error", "Critical" ] + }, + "error": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "message": { + "type": "string" + }, + "asset": { + "type": "string" + }, + "severity": { + "$ref": "#/$defs/severity" + }, + "verifiers": { + "type": "array", + "items": { + "type": "string" + } + }, + "context": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "message", + "asset", + "severity", + "verifiers", + "context" + ], + "additionalProperties": false + } + }, + "properties": { + "version": { + "const": "2.0" + }, + "minSeverity": { + "$ref": "#/$defs/severity" + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/$defs/error" + }, + "additionalItems": false + } + }, + "required": [ + "version", + "minSeverity", + "errors" + ], + "additionalProperties": false +} \ No newline at end of file From 4ab8e65c0d17a256b792b44c152d6c9bc95d56fd Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 1 Jul 2025 09:09:35 +0200 Subject: [PATCH 14/61] fix tests --- .../ModVerifyOptionsParserTest.cs | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs index 3f54ebd..e00d6d2 100644 --- a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs +++ b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs @@ -70,7 +70,7 @@ public abstract class ModVerifyOptionsParserTestBase protected ModVerifyOptionsParserTestBase() { - Parser = new ModVerifyOptionsParser(CreateEnvironment(), FileSystem, null); + Parser = new ModVerifyOptionsParser(CreateEnvironment(), null); } [Fact] @@ -79,11 +79,7 @@ public void Parse_NoArgs_IsVerify_IsInteractive() var settings = Parser.Parse([]); Assert.True(settings.HasOptions); - Assert.NotNull(settings.ModVerifyOptions); - - Assert.False(settings.ModVerifyOptions.CreateNewBaseline); - Assert.True(settings.ModVerifyOptions.Interactive); - + Assert.IsType(settings.ModVerifyOptions); Assert.Null(settings.UpdateOptions); } @@ -97,10 +93,10 @@ public void Parse_Interactive(string argString, bool createBaseLine) var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); Assert.True(settings.HasOptions); - Assert.NotNull(settings.ModVerifyOptions); - - Assert.True(settings.ModVerifyOptions.Interactive); - Assert.Equal(createBaseLine, settings.ModVerifyOptions.CreateNewBaseline); + if (createBaseLine) + Assert.IsType(settings.ModVerifyOptions); + else + Assert.IsType(settings.ModVerifyOptions); Assert.Null(settings.UpdateOptions); } @@ -116,8 +112,11 @@ public void Parse_NotInteractive(string argString, bool createBaseLine) Assert.True(settings.HasOptions); Assert.NotNull(settings.ModVerifyOptions); - Assert.False(settings.ModVerifyOptions.Interactive); - Assert.Equal(createBaseLine, settings.ModVerifyOptions.CreateNewBaseline); + if (createBaseLine) + Assert.IsType(settings.ModVerifyOptions); + else + Assert.IsType(settings.ModVerifyOptions); + Assert.Null(settings.UpdateOptions); } @@ -169,12 +168,8 @@ public void Parse_UpdatePerformed_RestartedFromNoArgs() Assert.False(settings.HasOptions); else { - Assert.True(settings.HasOptions); - Assert.NotNull(settings.ModVerifyOptions); - - Assert.False(settings.ModVerifyOptions.CreateNewBaseline); - Assert.True(settings.ModVerifyOptions.Interactive); - + Assert.True(settings.HasOptions); + Assert.IsType(settings.ModVerifyOptions); Assert.Null(settings.UpdateOptions); } } From 6987b6e885ee96dbf08ad084576bc337e109bbdc Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 1 Jul 2025 09:09:41 +0200 Subject: [PATCH 15/61] update subs --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 746a05e..8a37dc1 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 746a05eb17524637fc156886f16b967bb46f8864 +Subproject commit 8a37dc10507230ad6b90fcb786f34c97a7d2ab55 From 35c94ea7c1090e2e2a24434457dcc7828b8a1f96 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 14 Jul 2025 15:18:43 +0200 Subject: [PATCH 16/61] start feature: searching for local baselines --- src/ModVerify.CliApp/ModVerifyApplication.cs | 62 +++++++-- .../Properties/launchSettings.json | 2 +- .../Reporting/BaselineFactory.cs | 70 ++++++++++ .../Reporting/BaselineSelector.cs | 129 ++++++++++++++++++ .../CommandLine/ModVerifyOptionsParser.cs | 2 +- .../Settings/CommandLine/VerifyVerbOption.cs | 13 +- .../Settings/ModVerifyAppSettings.cs | 3 +- .../Settings/ModVerifyReportSettings.cs | 14 ++ .../Settings/SettingsBuilder.cs | 67 ++------- src/ModVerify/Pipeline/GameVerifyPipeline.cs | 1 + .../PG.StarWarsGame.Engine/GameLocations.cs | 9 ++ test/ModVerify.CliApp.Test/CommonTestBase.cs | 29 ++++ .../EmbeddedBaselineTest.cs | 47 +++++++ .../ModVerifyOptionsParserTest.cs | 48 +++---- .../TestData/NoVerifierProvider.cs | 16 +++ .../ModVerify.CliApp.Test/TestData/TestEnv.cs | 11 ++ .../TestData/UpdatableEnv.cs | 20 +++ 17 files changed, 440 insertions(+), 103 deletions(-) create mode 100644 src/ModVerify.CliApp/Reporting/BaselineFactory.cs create mode 100644 src/ModVerify.CliApp/Reporting/BaselineSelector.cs create mode 100644 src/ModVerify.CliApp/Settings/ModVerifyReportSettings.cs create mode 100644 test/ModVerify.CliApp.Test/CommonTestBase.cs create mode 100644 test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs create mode 100644 test/ModVerify.CliApp.Test/TestData/NoVerifierProvider.cs create mode 100644 test/ModVerify.CliApp.Test/TestData/TestEnv.cs create mode 100644 test/ModVerify.CliApp.Test/TestData/UpdatableEnv.cs diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index 2cb32af..bb013e5 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -1,21 +1,22 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Abstractions; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using AET.ModVerify.App.ModSelectors; +using AET.ModVerify.App.ModSelectors; using AET.ModVerify.App.Reporting; using AET.ModVerify.App.Settings; using AET.ModVerify.Pipeline; using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting.Settings; using AnakinRaW.ApplicationBase; using AnakinRaW.ApplicationBase.Utilities; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; using Serilog; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Abstractions; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace AET.ModVerify.App; @@ -70,10 +71,13 @@ private async Task RunVerify() var installData = new SettingsBasedModSelector(services) .CreateInstallationDataFromSettings(settings.GameInstallationsSettings); + var reportSettings = CreateGlobalReportSettings(installData); + _logger?.LogDebug($"Verify install data: {installData}"); _logger?.LogTrace($"Verify settings: {settings}"); - var allErrors = await Verify(installData).ConfigureAwait(false); + var allErrors = await Verify(installData, reportSettings) + .ConfigureAwait(false); try { @@ -87,13 +91,15 @@ private async Task RunVerify() if (!settings.CreateNewBaseline) return 0; - await WriteBaseline(allErrors, settings.NewBaselinePath).ConfigureAwait(false); + await WriteBaseline(reportSettings, allErrors, settings.NewBaselinePath).ConfigureAwait(false); _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Baseline successfully created."); return 0; } - private async Task> Verify(VerifyInstallationData installData) + private async Task> Verify( + VerifyInstallationData installData, + GlobalVerifyReportSettings reportSettings) { var gameEngineService = services.GetRequiredService(); var engineErrorReporter = new ConcurrentGameEngineErrorReporter(); @@ -134,7 +140,7 @@ private async Task> Verify(VerifyInstalla gameEngine, engineErrorReporter, settings.VerifyPipelineSettings, - settings.GlobalReportSettings, + reportSettings, progressReporter, services); @@ -182,9 +188,12 @@ private async Task ReportErrors(IReadOnlyCollection errors) throw new GameVerificationException(errors); } - private async Task WriteBaseline(IEnumerable errors, string baselineFile) + private async Task WriteBaseline( + GlobalVerifyReportSettings reportSettings, + IEnumerable errors, + string baselineFile) { - var baseline = new VerificationBaseline(settings.GlobalReportSettings.MinimumReportSeverity, errors); + var baseline = new VerificationBaseline(reportSettings.MinimumReportSeverity, errors); var fullPath = _fileSystem.Path.GetFullPath(baselineFile); _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Writing Baseline to '{fullPath}'"); @@ -195,4 +204,29 @@ private async Task WriteBaseline(IEnumerable errors, string b using var fs = _fileSystem.FileStream.New(fullPath, FileMode.Create, FileAccess.Write, FileShare.None); await baseline.ToJsonAsync(fs); } + + private GlobalVerifyReportSettings CreateGlobalReportSettings(VerifyInstallationData installData) + { + var baselineSelector = new BaselineSelector(settings, services); + var baseline = baselineSelector.SelectBaseline(installData); + + var suppressionsFile = settings.ReportSettings.SuppressionsPath; + SuppressionList suppressions; + + if (string.IsNullOrEmpty(suppressionsFile)) + suppressions = SuppressionList.Empty; + else + { + using var fs = _fileSystem.File.OpenRead(suppressionsFile); + suppressions = SuppressionList.FromJson(fs); + } + + + return new GlobalVerifyReportSettings + { + Baseline = baseline, + Suppressions = suppressions, + MinimumReportSeverity = settings.ReportSettings.MinimumReportSeverity, + }; + } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index 283181a..db7fe89 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Interactive Verify": { "commandName": "Project", - "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --baseline focBaseline.json --offline" + "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --offline" }, "Interactive Baseline": { "commandName": "Project", diff --git a/src/ModVerify.CliApp/Reporting/BaselineFactory.cs b/src/ModVerify.CliApp/Reporting/BaselineFactory.cs new file mode 100644 index 0000000..ebab6d1 --- /dev/null +++ b/src/ModVerify.CliApp/Reporting/BaselineFactory.cs @@ -0,0 +1,70 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.IO.Abstractions; +using AET.ModVerify.Reporting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace AET.ModVerify.App.Reporting; + +internal sealed class BaselineFactory(IServiceProvider serviceProvider) +{ + private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(BaselineFactory)); + private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); + + public bool TryCreateBaseline( + string directory, + out VerificationBaseline baseline, + [NotNullWhen(true)] out string? path) + { + baseline = VerificationBaseline.Empty; + path = null; + + if (!_fileSystem.Directory.Exists(directory)) + return false; + + _logger?.LogDebug(ModVerifyConstants.ConsoleEventId, $"Searching for baseline file at '{directory}'"); + + var jsonFiles = _fileSystem.Directory.EnumerateFiles( + directory, + "*.json" +#if NET || NETSTANDARD2_1_OR_GREATER + , new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = false + } +#endif + ); + + foreach (var jsonFile in jsonFiles) + { + try + { + baseline = CreateBaselineFromFilePath(jsonFile); + path = jsonFile; + return true; + } + catch (InvalidBaselineException) + { + // TODO: Log + // Ignore this exception + } + } + + path = null; + return false; + } + + public VerificationBaseline CreateBaseline(string filePath) + { + return CreateBaselineFromFilePath(filePath); + } + + private VerificationBaseline CreateBaselineFromFilePath(string baselineFile) + { + using var fs = _fileSystem.FileStream.New(baselineFile, FileMode.Open, FileAccess.Read); + return VerificationBaseline.FromJson(fs); + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs new file mode 100644 index 0000000..816d182 --- /dev/null +++ b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs @@ -0,0 +1,129 @@ +using System; +using System.Diagnostics; +using AET.ModVerify.App.ModSelectors; +using AET.ModVerify.App.Settings; +using AET.ModVerify.Reporting; +using AnakinRaW.ApplicationBase; +using AnakinRaW.ApplicationBase.Environment; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.StarWarsGame.Engine; + +namespace AET.ModVerify.App.Reporting; + +internal sealed class BaselineSelector(ModVerifyAppSettings settings, IServiceProvider services) +{ + private readonly ILogger? _logger = services.GetService()?.CreateLogger(typeof(ModVerifyApplication)); + private readonly ApplicationEnvironment _applicationEnvironment = services.GetRequiredService(); + private readonly BaselineFactory _baselineFactory = new(services); + + public VerificationBaseline SelectBaseline(VerifyInstallationData installationData) + { + var baselinePath = settings.ReportSettings.BaselinePath; + if (!string.IsNullOrEmpty(baselinePath)) + { + try + { + return _baselineFactory.CreateBaseline(baselinePath!); + } + catch (InvalidBaselineException) + { + using (ConsoleUtilities.HorizontalLineSeparatedBlock('*')) + { + Console.WriteLine($"The baseline '{baselinePath}' is not compatible with this version of ModVerify." + + $"{Environment.NewLine}Please generate a new baseline file or download the latest version." + + $"{Environment.NewLine}"); + } + + // For now, we bubble up this exception because we except users + // to correctly specify their baselines through command line arguments. + throw; + } + } + + if (!settings.ReportSettings.SearchBaselineLocally) + { + _logger?.LogDebug(ModVerifyConstants.ConsoleEventId, "No baseline path specified and local search is not enabled. Using empty baseline."); + return VerificationBaseline.Empty; + } + + if (settings.Interactive) + return FindBaselineInteractive(installationData); + + // If the application is not interactive, we only use a baseline file present in the directory of the verification target. + return FindBaselineNonInteractive(installationData); + + } + + private VerificationBaseline FindBaselineInteractive(VerifyInstallationData installationData) + { + // The application is in interactive mode. We apply the following lookup: + // 1. Use a baseline found in the directory of the verification target. + // 2. Use a baseline found in the directory ModVerify executable. + // 3. If the verification target is a mod, ask the user to apply the default game's baseline. + // In any case ask the use if they want to use the located baseline file, or they wish to continue using none/empty. + + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Searching for local baseline files..."); + + if (!_baselineFactory.TryCreateBaseline(installationData.GameLocations.TargetPath, out var baseline, + out var baselinePath)) + { + if (!_baselineFactory.TryCreateBaseline(".", out baseline, out baselinePath)) + { + // It does not make sense to load the game's default baselines if the user wants to verify the game, + // as the verification result would always be empty (at least in a non-development scenario) + if (installationData.GameLocations.ModPaths.Count == 0) + return TryGetDefaultBaseline(installationData.EngineType); + + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "No local baseline file found."); + return VerificationBaseline.Empty; + } + } + + Debug.Assert(baselinePath is not null); + + return ConsoleUtilities.UserYesNoQuestion($"ModVerify found the baseline file '{baselinePath}'. Do you want to use it?") + ? baseline + : VerificationBaseline.Empty; + } + + private VerificationBaseline TryGetDefaultBaseline(GameEngineType engineType) + { + if (engineType == GameEngineType.Eaw) + { + // TODO: EAW currently not implemented + return VerificationBaseline.Empty; + } + + if (!ConsoleUtilities.UserYesNoQuestion($"Do you want to load the default baseline for game engine '{engineType}'?")) + return VerificationBaseline.Empty; + + try + { + return VerificationBaseline.Empty; + } + catch (InvalidBaselineException) + { + throw new InvalidOperationException("Invalid baseline packed along ModVerify App. Please reach out to the creators. Thanks!"); + } + } + + internal VerificationBaseline LoadEmbeddedBaseline(GameEngineType engineType) + { + var resourcePath = $"{_applicationEnvironment.AssemblyInfo.Assembly.GetName().Name}.Resources.Baselines.{engineType}"; + using var baselineStream = typeof(BaselineSelector).Assembly.GetManifestResourceStream(resourcePath)!; + return VerificationBaseline.FromJson(baselineStream); + } + + private VerificationBaseline FindBaselineNonInteractive(VerifyInstallationData installationData) + { + var targetPath = installationData.GameLocations.TargetPath; + if (_baselineFactory.TryCreateBaseline(targetPath, out var baseline, out var path)) + { + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Automatically applying local baseline file '{path}'."); + return baseline; + } + _logger?.LogTrace($"No baseline file found in taget path '{targetPath}'."); + return VerificationBaseline.Empty; + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs b/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs index 1d01d4d..63a29d8 100644 --- a/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/ModVerifyOptionsParser.cs @@ -44,7 +44,7 @@ private ModVerifyOptionsContainer ParseArguments(IReadOnlyList args) { return new ModVerifyOptionsContainer { - ModVerifyOptions = new VerifyVerbOption(), + ModVerifyOptions = VerifyVerbOption.WithoutArguments, UpdateOptions = null }; } diff --git a/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs b/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs index 9047826..97f1536 100644 --- a/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs @@ -6,6 +6,12 @@ namespace AET.ModVerify.App.Settings.CommandLine; [Verb("verify", HelpText = "Verifies the specified game and reports the findings.")] internal sealed class VerifyVerbOption : BaseModVerifyOptions { + internal static readonly VerifyVerbOption WithoutArguments = new() + { + IsRunningWithoutArguments = true, + SearchBaselineLocally = true, + }; + [Option('o', "outDir", Required = false, HelpText = "Directory where result files shall be stored to.")] public string? OutputDirectory { get; init; } @@ -21,8 +27,13 @@ internal sealed class VerifyVerbOption : BaseModVerifyOptions HelpText = "When this flag is present, the application will not report engine assertions.")] public bool IgnoreAsserts { get; init; } - [Option("baseline", Required = false, HelpText = "Path to a JSON baseline file.")] + [Option("baseline", SetName = "baselineSelection", Required = false, + HelpText = "Path to a JSON baseline file. Cannot be used together with --searchBaseline.")] public string? Baseline { get; init; } + [Option("searchBaseline", SetName = "baselineSelection", Required = false, + HelpText = "When set, the application will search for baseline files and use them for verification. Cannot be used together with --baseline")] + public bool SearchBaselineLocally { get; init; } + public bool IsRunningWithoutArguments { get; init; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs b/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs index 09631df..3376354 100644 --- a/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs +++ b/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs @@ -1,6 +1,5 @@ using System.Diagnostics.CodeAnalysis; using AET.ModVerify.Reporting; -using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Settings; namespace AET.ModVerify.App.Settings; @@ -11,7 +10,7 @@ internal sealed class ModVerifyAppSettings public required VerifyPipelineSettings VerifyPipelineSettings { get; init; } - public required GlobalVerifyReportSettings GlobalReportSettings { get; init; } + public required ModVerifyReportSettings ReportSettings { get; init; } public required GameInstallationsSettings GameInstallationsSettings { get; init; } diff --git a/src/ModVerify.CliApp/Settings/ModVerifyReportSettings.cs b/src/ModVerify.CliApp/Settings/ModVerifyReportSettings.cs new file mode 100644 index 0000000..482b844 --- /dev/null +++ b/src/ModVerify.CliApp/Settings/ModVerifyReportSettings.cs @@ -0,0 +1,14 @@ +using AET.ModVerify.Reporting; + +namespace AET.ModVerify.App.Settings; + +internal sealed class ModVerifyReportSettings +{ + public VerificationSeverity MinimumReportSeverity { get; init; } + + public string? SuppressionsPath { get; init; } + + public string? BaselinePath { get; init; } + + public bool SearchBaselineLocally { get; init; } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs index 571d177..caf0353 100644 --- a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; -using System.IO; using System.IO.Abstractions; using AET.ModVerify.App.Settings.CommandLine; using AET.ModVerify.App.Utilities; using AET.ModVerify.Pipeline; using AET.ModVerify.Reporting; -using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Settings; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -47,7 +45,7 @@ private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) }, AppThrowsOnMinimumSeverity = verifyOptions.MinimumFailureSeverity, GameInstallationsSettings = BuildInstallationSettings(verifyOptions), - GlobalReportSettings = BuilderGlobalReportSettings(verifyOptions), + ReportSettings = BuildReportSettings(verifyOptions), }; VerificationSeverity? GetVerifierMinimumThrowSeverity() @@ -89,28 +87,28 @@ private ModVerifyAppSettings BuildFromCreateBaselineVerb(CreateBaselineVerbOptio }, AppThrowsOnMinimumSeverity = null, GameInstallationsSettings = BuildInstallationSettings(baselineVerb), - GlobalReportSettings = BuilderGlobalReportSettings(baselineVerb), + ReportSettings = BuildReportSettings(baselineVerb), NewBaselinePath = baselineVerb.OutputFile, }; } - private GlobalVerifyReportSettings BuilderGlobalReportSettings(BaseModVerifyOptions options) + private static ModVerifyReportSettings BuildReportSettings(BaseModVerifyOptions options) { - var baseline = new BaselineFactory(serviceProvider).CreateBaseline(options); + var baselinePath = (options as VerifyVerbOption)?.Baseline; - return new GlobalVerifyReportSettings + return new ModVerifyReportSettings { - Baseline = baseline, - Suppressions = CreateSuppressions(), + BaselinePath = baselinePath, MinimumReportSeverity = options.MinimumSeverity, + SearchBaselineLocally = SearchLocally(options), + SuppressionsPath = options.Suppressions }; - SuppressionList CreateSuppressions() + static bool SearchLocally(BaseModVerifyOptions o) { - if (options.Suppressions is null) - return SuppressionList.Empty; - using var fs = _fileSystem.FileStream.New(options.Suppressions, FileMode.Open, FileAccess.Read); - return SuppressionList.FromJson(fs); + if (o is not VerifyVerbOption v) + return false; + return v.SearchBaselineLocally || v.LaunchedWithoutArguments(); } } @@ -159,45 +157,4 @@ private GameInstallationsSettings BuildInstallationSettings(BaseModVerifyOptions EngineType = options.GameType }; } -} - -internal sealed class BaselineFactory(IServiceProvider serviceProvider) -{ - private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(BaselineFactory)); - private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); - - public VerificationBaseline CreateBaseline(BaseModVerifyOptions options) - { - if (options.LaunchedWithoutArguments()) - return SearchBaselineInKnownDirectories(); - - // It does not make sense to create a baseline on another baseline. - if (options is not VerifyVerbOption verifyOptions || string.IsNullOrEmpty(verifyOptions.Baseline)) - return VerificationBaseline.Empty; - - return CreateBaselineFromFilePath(verifyOptions.Baseline); - } - - private VerificationBaseline SearchBaselineInKnownDirectories() - { - _logger?.LogTrace("Searching for nearby baseline files."); - // TODO - return VerificationBaseline.Empty; - } - - private VerificationBaseline CreateBaselineFromFilePath(string baselineFile) - { - using var fs = _fileSystem.FileStream.New(baselineFile, FileMode.Open, FileAccess.Read); - try - { - return VerificationBaseline.FromJson(fs); - } - catch (InvalidBaselineException) - { - Console.WriteLine($"The baseline '{baselineFile}' is not compatible with this version of ModVerify." + - $"{Environment.NewLine}Please generate a new baseline file or download the latest version." + - $"{Environment.NewLine}"); - throw; - } - } } \ No newline at end of file diff --git a/src/ModVerify/Pipeline/GameVerifyPipeline.cs b/src/ModVerify/Pipeline/GameVerifyPipeline.cs index b7ac00d..810651a 100644 --- a/src/ModVerify/Pipeline/GameVerifyPipeline.cs +++ b/src/ModVerify/Pipeline/GameVerifyPipeline.cs @@ -103,6 +103,7 @@ protected override void OnError(object sender, StepRunnerErrorEventArgs e) { if (FailFast && e.Exception is GameVerificationException v) { + // TODO: Apply globalMinSeverity if (v.Errors.All(error => _reportSettings.Baseline.Contains(error) || _reportSettings.Suppressions.Suppresses(error))) return; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs index 18539a7..3b77732 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs @@ -7,6 +7,11 @@ namespace PG.StarWarsGame.Engine; public sealed class GameLocations { + /// + /// Gets the path that represents the topmost playable target. This is typically the actual mod selected by the user. + /// + public string TargetPath { get; } + public IReadOnlyList ModPaths { get; } public string GamePath { get; } @@ -38,5 +43,9 @@ public GameLocations(IList modPaths, string gamePath, IList fall ModPaths = modPaths.ToList(); GamePath = gamePath; FallbackPaths = fallbackPaths.ToList(); + + TargetPath = ModPaths.Count > 0 + ? ModPaths[0] + : GamePath; } } \ No newline at end of file diff --git a/test/ModVerify.CliApp.Test/CommonTestBase.cs b/test/ModVerify.CliApp.Test/CommonTestBase.cs new file mode 100644 index 0000000..7d6dc18 --- /dev/null +++ b/test/ModVerify.CliApp.Test/CommonTestBase.cs @@ -0,0 +1,29 @@ +using System; +using System.IO.Abstractions; +using AnakinRaW.CommonUtilities.Hashing; +using Microsoft.Extensions.DependencyInjection; +using PG.Commons; +using Testably.Abstractions.Testing; + +namespace ModVerify.CliApp.Test; + +public abstract class CommonTestBase +{ + protected readonly MockFileSystem FileSystem = new(); + protected readonly IServiceProvider ServiceProvider; + + protected CommonTestBase() + { + var sc = new ServiceCollection(); + sc.AddSingleton(sp => new HashingService(sp)); + sc.AddSingleton(FileSystem); + PetroglyphCommons.ContributeServices(sc); + // ReSharper disable once VirtualMemberCallInConstructor + SetupServices(sc); + ServiceProvider = sc.BuildServiceProvider(); + } + + protected virtual void SetupServices(ServiceCollection serviceCollection) + { + } +} \ No newline at end of file diff --git a/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs new file mode 100644 index 0000000..4779f22 --- /dev/null +++ b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs @@ -0,0 +1,47 @@ +using AET.ModVerify.App; +using AET.ModVerify.App.Reporting; +using AET.ModVerify.App.Settings; +using AET.ModVerify.Settings; +using AnakinRaW.ApplicationBase.Environment; +using Microsoft.Extensions.DependencyInjection; +using PG.StarWarsGame.Engine; +using System; +using System.IO.Abstractions; +using ModVerify.CliApp.Test.TestData; +using Testably.Abstractions; + +namespace ModVerify.CliApp.Test; + +public class EmbeddedBaselineTest +{ + private static readonly IFileSystem FileSystem = new RealFileSystem(); + private static readonly ModVerifyAppSettings TestSettings = new() + { + ReportSettings = new(), + GameInstallationsSettings = new (), + VerifyPipelineSettings = new() + { + GameVerifySettings = new GameVerifySettings(), + VerifiersProvider = new NoVerifierProvider() + } + }; + + private readonly IServiceProvider _serviceProvider; + + public EmbeddedBaselineTest() + { + var sc = new ServiceCollection(); + sc.AddSingleton(FileSystem); + sc.AddSingleton(new ModVerifyAppEnvironment(typeof(ModVerifyAppEnvironment).Assembly, FileSystem)); + _serviceProvider = sc.BuildServiceProvider(); + } + + [Theory] + [InlineData(GameEngineType.Eaw)] + [InlineData(GameEngineType.Foc)] + public void Foo(GameEngineType engineType) + { + // Ensure this operation does not crash, meaning the embedded baseline is at least compatible. + new BaselineSelector(TestSettings, _serviceProvider).LoadEmbeddedBaseline(engineType); + } +} \ No newline at end of file diff --git a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs index e00d6d2..1fdaf46 100644 --- a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs +++ b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs @@ -1,10 +1,8 @@ -using System; -using System.Collections.Generic; -using System.IO.Abstractions; -using System.Reflection; -using AET.ModVerify.App.Settings.CommandLine; +using AET.ModVerify.App.Settings.CommandLine; using AnakinRaW.ApplicationBase.Environment; -using AnakinRaW.AppUpdaterFramework.Configuration; +using System; +using System.IO.Abstractions; +using ModVerify.CliApp.Test.TestData; using Testably.Abstractions; namespace ModVerify.CliApp.Test; @@ -79,7 +77,8 @@ public void Parse_NoArgs_IsVerify_IsInteractive() var settings = Parser.Parse([]); Assert.True(settings.HasOptions); - Assert.IsType(settings.ModVerifyOptions); + var verify = Assert.IsType(settings.ModVerifyOptions); + Assert.True(verify.IsRunningWithoutArguments); Assert.Null(settings.UpdateOptions); } @@ -94,9 +93,14 @@ public void Parse_Interactive(string argString, bool createBaseLine) Assert.True(settings.HasOptions); if (createBaseLine) + { Assert.IsType(settings.ModVerifyOptions); + } else - Assert.IsType(settings.ModVerifyOptions); + { + var verify = Assert.IsType(settings.ModVerifyOptions); + Assert.False(verify.IsRunningWithoutArguments); + } Assert.Null(settings.UpdateOptions); } @@ -115,7 +119,10 @@ public void Parse_NotInteractive(string argString, bool createBaseLine) if (createBaseLine) Assert.IsType(settings.ModVerifyOptions); else - Assert.IsType(settings.ModVerifyOptions); + { + var verify = Assert.IsType(settings.ModVerifyOptions); + Assert.False(verify.IsRunningWithoutArguments); + } Assert.Null(settings.UpdateOptions); } @@ -168,8 +175,9 @@ public void Parse_UpdatePerformed_RestartedFromNoArgs() Assert.False(settings.HasOptions); else { - Assert.True(settings.HasOptions); - Assert.IsType(settings.ModVerifyOptions); + Assert.True(settings.HasOptions); + var verify = Assert.IsType(settings.ModVerifyOptions); + Assert.True(verify.IsRunningWithoutArguments); Assert.Null(settings.UpdateOptions); } } @@ -185,22 +193,4 @@ public void Parse_CreateBaseline_MissingRequired_Fails(string argString) Assert.Null(settings.ModVerifyOptions); Assert.Null(settings.UpdateOptions); } -} - -internal class TestEnv(Assembly assembly, IFileSystem fileSystem) : ApplicationEnvironment(assembly, fileSystem) -{ - public override string ApplicationName => "TestEnv"; - protected override string ApplicationLocalDirectoryName => ApplicationName; -} - -internal class UpdatableEnv(Assembly assembly, IFileSystem fileSystem) : UpdatableApplicationEnvironment(assembly, fileSystem) -{ - public override string ApplicationName => "TestUpdateEnv"; - protected override string ApplicationLocalDirectoryName => ApplicationName; - public override ICollection UpdateMirrors => []; - public override string UpdateRegistryPath => ApplicationName; - protected override UpdateConfiguration CreateUpdateConfiguration() - { - return UpdateConfiguration.Default; - } } \ No newline at end of file diff --git a/test/ModVerify.CliApp.Test/TestData/NoVerifierProvider.cs b/test/ModVerify.CliApp.Test/TestData/NoVerifierProvider.cs new file mode 100644 index 0000000..ef7ad74 --- /dev/null +++ b/test/ModVerify.CliApp.Test/TestData/NoVerifierProvider.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using AET.ModVerify.Pipeline; +using AET.ModVerify.Settings; +using AET.ModVerify.Verifiers; +using PG.StarWarsGame.Engine; + +namespace ModVerify.CliApp.Test.TestData; + +internal sealed class NoVerifierProvider : IGameVerifiersProvider +{ + public IEnumerable GetVerifiers(IStarWarsGameEngine database, GameVerifySettings settings, IServiceProvider serviceProvider) + { + yield break; + } +} \ No newline at end of file diff --git a/test/ModVerify.CliApp.Test/TestData/TestEnv.cs b/test/ModVerify.CliApp.Test/TestData/TestEnv.cs new file mode 100644 index 0000000..f72f001 --- /dev/null +++ b/test/ModVerify.CliApp.Test/TestData/TestEnv.cs @@ -0,0 +1,11 @@ +using System.IO.Abstractions; +using System.Reflection; +using AnakinRaW.ApplicationBase.Environment; + +namespace ModVerify.CliApp.Test.TestData; + +internal class TestEnv(Assembly assembly, IFileSystem fileSystem) : ApplicationEnvironment(assembly, fileSystem) +{ + public override string ApplicationName => "TestEnv"; + protected override string ApplicationLocalDirectoryName => ApplicationName; +} \ No newline at end of file diff --git a/test/ModVerify.CliApp.Test/TestData/UpdatableEnv.cs b/test/ModVerify.CliApp.Test/TestData/UpdatableEnv.cs new file mode 100644 index 0000000..557f522 --- /dev/null +++ b/test/ModVerify.CliApp.Test/TestData/UpdatableEnv.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Reflection; +using AnakinRaW.ApplicationBase.Environment; +using AnakinRaW.AppUpdaterFramework.Configuration; + +namespace ModVerify.CliApp.Test.TestData; + +internal class UpdatableEnv(Assembly assembly, IFileSystem fileSystem) : UpdatableApplicationEnvironment(assembly, fileSystem) +{ + public override string ApplicationName => "TestUpdateEnv"; + protected override string ApplicationLocalDirectoryName => ApplicationName; + public override ICollection UpdateMirrors => []; + public override string UpdateRegistryPath => ApplicationName; + protected override UpdateConfiguration CreateUpdateConfiguration() + { + return UpdateConfiguration.Default; + } +} \ No newline at end of file From a95042c4b8d893c960e69f5a8af062ec0eb89152 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 14 Jul 2025 15:22:49 +0200 Subject: [PATCH 17/61] update deps --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 10 +++++----- src/ModVerify/ModVerify.csproj | 2 +- .../ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index c3a69f0..a2b80d8 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -29,11 +29,11 @@ - - - - - + + + + + diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index 3d7a664..92c7adf 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -35,7 +35,7 @@ - + diff --git a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj index 1698d94..a62be3f 100644 --- a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj +++ b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj @@ -13,9 +13,9 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 6099fcbed01e30b4e9f2d51b27423e29359880a8 Mon Sep 17 00:00:00 2001 From: AnakinRaW <8994294+AnakinRaW@users.noreply.github.com> Date: Mon, 14 Jul 2025 15:25:46 +0200 Subject: [PATCH 18/61] update sub --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 8a37dc1..25a2201 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 8a37dc10507230ad6b90fcb786f34c97a7d2ab55 +Subproject commit 25a2201af67eca4dcf60013cdffc8b0db51bd27c From 09a7647009f8bbe155fe3b231ba6b56b8eed60c5 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 16 Aug 2025 15:41:09 +0200 Subject: [PATCH 19/61] minor simplifications --- src/ModVerify.CliApp/Properties/launchSettings.json | 4 ++++ src/ModVerify.CliApp/Reporting/BaselineFactory.cs | 7 ++++--- src/ModVerify.CliApp/Reporting/BaselineSelector.cs | 6 +++--- src/ModVerify/Reporting/Json/JsonBaselineParser.cs | 3 --- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index db7fe89..e47583a 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -1,5 +1,9 @@ { "profiles": { + "Run": { + "commandName": "Project", + "commandLineArgs": "" + }, "Interactive Verify": { "commandName": "Project", "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --offline" diff --git a/src/ModVerify.CliApp/Reporting/BaselineFactory.cs b/src/ModVerify.CliApp/Reporting/BaselineFactory.cs index ebab6d1..9f5deb9 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineFactory.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineFactory.cs @@ -24,7 +24,7 @@ public bool TryCreateBaseline( if (!_fileSystem.Directory.Exists(directory)) return false; - _logger?.LogDebug(ModVerifyConstants.ConsoleEventId, $"Searching for baseline file at '{directory}'"); + _logger?.LogDebug(ModVerifyConstants.ConsoleEventId, "Searching for baseline file at '{Directory}'", directory); var jsonFiles = _fileSystem.Directory.EnumerateFiles( directory, @@ -44,11 +44,12 @@ public bool TryCreateBaseline( { baseline = CreateBaselineFromFilePath(jsonFile); path = jsonFile; + _logger?.LogDebug("Create baseline from file: {JsonFile}", jsonFile); return true; } - catch (InvalidBaselineException) + catch (InvalidBaselineException e) { - // TODO: Log + _logger?.LogDebug("'{JsonFile}' is not a valid baseline file: {EMessage}", jsonFile, e.Message); // Ignore this exception } } diff --git a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs index 816d182..6fb7492 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs @@ -26,11 +26,11 @@ public VerificationBaseline SelectBaseline(VerifyInstallationData installationDa { return _baselineFactory.CreateBaseline(baselinePath!); } - catch (InvalidBaselineException) + catch (InvalidBaselineException e) { using (ConsoleUtilities.HorizontalLineSeparatedBlock('*')) { - Console.WriteLine($"The baseline '{baselinePath}' is not compatible with this version of ModVerify." + + Console.WriteLine($"The baseline '{baselinePath}' is not a valid baseline file: {e.Message}" + $"{Environment.NewLine}Please generate a new baseline file or download the latest version." + $"{Environment.NewLine}"); } @@ -100,7 +100,7 @@ private VerificationBaseline TryGetDefaultBaseline(GameEngineType engineType) try { - return VerificationBaseline.Empty; + return LoadEmbeddedBaseline(engineType); } catch (InvalidBaselineException) { diff --git a/src/ModVerify/Reporting/Json/JsonBaselineParser.cs b/src/ModVerify/Reporting/Json/JsonBaselineParser.cs index 9441524..ef2f5d1 100644 --- a/src/ModVerify/Reporting/Json/JsonBaselineParser.cs +++ b/src/ModVerify/Reporting/Json/JsonBaselineParser.cs @@ -19,9 +19,6 @@ public static VerificationBaseline Parse(Stream dataStream) if (jsonBaseline is null) throw new InvalidBaselineException($"Unable to parse input from stream to {nameof(VerificationBaseline)}. Unknown Error!"); - if (jsonBaseline.Version != VerificationBaseline.LatestVersion) - throw new InvalidBaselineException("The parsed baseline version does not match the version used by this version of ModVerify."); - return new VerificationBaseline(jsonBaseline); } catch (JsonException cause) From 132b5200d6b7e0fb6374e8fd6275cb21df679768 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 16 Aug 2025 16:03:08 +0200 Subject: [PATCH 20/61] update module --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 25a2201..74dd654 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 25a2201af67eca4dcf60013cdffc8b0db51bd27c +Subproject commit 74dd654af059698b6e80c8115746d6907db79471 From 2bdaa693631e01b229db49cceeb4b442dac063b2 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 16 Aug 2025 16:06:07 +0200 Subject: [PATCH 21/61] test on .net framework too --- test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj index a62be3f..3297a24 100644 --- a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj +++ b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj @@ -1,7 +1,8 @@  - net9.0 + net9.0 + $(TargetFrameworks);net48 false preview From fb4353330bb218eb7180d2bbb7566722efd64569 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 16 Aug 2025 16:11:57 +0200 Subject: [PATCH 22/61] simplify --- src/ModVerify.CliApp/Reporting/BaselineSelector.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs index 6fb7492..a932cd0 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs @@ -51,7 +51,7 @@ public VerificationBaseline SelectBaseline(VerifyInstallationData installationDa return FindBaselineInteractive(installationData); // If the application is not interactive, we only use a baseline file present in the directory of the verification target. - return FindBaselineNonInteractive(installationData); + return FindBaselineNonInteractive(installationData.GameLocations.TargetPath); } @@ -115,15 +115,14 @@ internal VerificationBaseline LoadEmbeddedBaseline(GameEngineType engineType) return VerificationBaseline.FromJson(baselineStream); } - private VerificationBaseline FindBaselineNonInteractive(VerifyInstallationData installationData) + private VerificationBaseline FindBaselineNonInteractive(string targetPath) { - var targetPath = installationData.GameLocations.TargetPath; if (_baselineFactory.TryCreateBaseline(targetPath, out var baseline, out var path)) { - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Automatically applying local baseline file '{path}'."); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Automatically applying local baseline file '{Path}'.", path); return baseline; } - _logger?.LogTrace($"No baseline file found in taget path '{targetPath}'."); + _logger?.LogTrace("No baseline file found in taget path '{TargetPath}'.", targetPath); return VerificationBaseline.Empty; } } \ No newline at end of file From 76a6140c47a3b8f0a1b503f34a75c2570403f09c Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 16 Aug 2025 18:02:59 +0200 Subject: [PATCH 23/61] reenable update check and added default baseline --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 8 + src/ModVerify.CliApp/Program.cs | 37 +- .../Reporting/BaselineSelector.cs | 22 +- .../Resources/Baselines/BaselineResources.cs | 4 + .../Resources/Baselines/baseline-foc.json | 2081 +++++++++++++++++ .../{ => Github}/GithubReleaseEntry.cs | 2 +- .../Updates/{ => Github}/GithubReleaseList.cs | 2 +- .../Updates/Github/GithubUpdateChecker.cs | 69 + .../Updates/Github/GithubUpdateConstants.cs | 9 + .../GithubUpdateInfo.cs} | 4 +- .../Updates/ModVerifyUpdater.cs | 127 +- .../Updates/ModVerifyUpdaterInformation.cs | 22 - .../ModVerify.CliApp.Test.csproj | 2 +- .../ModVerifyOptionsParserTest.cs | 14 + .../Utilities/StringExtensions.cs | 13 + 15 files changed, 2278 insertions(+), 138 deletions(-) create mode 100644 src/ModVerify.CliApp/Resources/Baselines/BaselineResources.cs create mode 100644 src/ModVerify.CliApp/Resources/Baselines/baseline-foc.json rename src/ModVerify.CliApp/Updates/{ => Github}/GithubReleaseEntry.cs (91%) rename src/ModVerify.CliApp/Updates/{ => Github}/GithubReleaseList.cs (70%) create mode 100644 src/ModVerify.CliApp/Updates/Github/GithubUpdateChecker.cs create mode 100644 src/ModVerify.CliApp/Updates/Github/GithubUpdateConstants.cs rename src/ModVerify.CliApp/Updates/{UpdateInfo.cs => Github/GithubUpdateInfo.cs} (75%) delete mode 100644 src/ModVerify.CliApp/Updates/ModVerifyUpdaterInformation.cs create mode 100644 test/ModVerify.CliApp.Test/Utilities/StringExtensions.cs diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index a2b80d8..ba57213 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -21,6 +21,14 @@ en + + + + + + + + diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index c7cc6ec..0632752 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO.Abstractions; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using AET.ModVerify.App.Settings; +using AET.ModVerify.App.Settings; using AET.ModVerify.App.Settings.CommandLine; using AET.ModVerify.App.Updates; using AET.ModVerify.App.Utilities; @@ -40,6 +34,12 @@ using Serilog.Expressions; using Serilog.Filters; using Serilog.Sinks.SystemConsole.Themes; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO.Abstractions; +using System.Runtime.InteropServices; +using System.Threading.Tasks; using Testably.Abstractions; using ILogger = Serilog.ILogger; @@ -79,7 +79,7 @@ protected override async Task InitializeAppAsync(IReadOnlyList args } catch (Exception e) { - Logger?.LogCritical(e, $"Failed to create settings form commandline arguments: {e.Message}"); + Logger?.LogCritical(e, "Failed to parse commandline arguments: {Message}", e.Message); ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, e); return e.HResult; } @@ -315,6 +315,25 @@ private async Task HandleUpdate(IServiceProvider serviceProvider) else updateMode = ModVerifyUpdateMode.AutoUpdate; - return await new ModVerifyUpdater(updateOptions, serviceProvider).RunUpdateProcedure(updateMode).ConfigureAwait(false); + try + { + Logger?.LogDebug("Running update with mode '{ModVerifyUpdateMode}'", updateMode); + var modVerifyUpdater = new ModVerifyUpdater(updateOptions, serviceProvider); + await modVerifyUpdater.RunUpdateProcedure(updateMode).ConfigureAwait(false); + Logger?.LogDebug("Update procedure completed successfully."); + return 0; + } + catch (Exception e) + { + Logger?.LogCritical(e, e.Message); + var action = updateMode switch + { + ModVerifyUpdateMode.CheckOnly => "checking for updates", + _ => "updating" + }; + ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, $"Error while {action}: {e.Message}", e.StackTrace); + return e.HResult; + } + } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs index a932cd0..55004e4 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs @@ -1,20 +1,19 @@ -using System; -using System.Diagnostics; -using AET.ModVerify.App.ModSelectors; +using AET.ModVerify.App.ModSelectors; +using AET.ModVerify.App.Resources.Baselines; using AET.ModVerify.App.Settings; using AET.ModVerify.Reporting; using AnakinRaW.ApplicationBase; -using AnakinRaW.ApplicationBase.Environment; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; +using System; +using System.Diagnostics; namespace AET.ModVerify.App.Reporting; internal sealed class BaselineSelector(ModVerifyAppSettings settings, IServiceProvider services) { private readonly ILogger? _logger = services.GetService()?.CreateLogger(typeof(ModVerifyApplication)); - private readonly ApplicationEnvironment _applicationEnvironment = services.GetRequiredService(); private readonly BaselineFactory _baselineFactory = new(services); public VerificationBaseline SelectBaseline(VerifyInstallationData installationData) @@ -73,10 +72,13 @@ private VerificationBaseline FindBaselineInteractive(VerifyInstallationData inst // It does not make sense to load the game's default baselines if the user wants to verify the game, // as the verification result would always be empty (at least in a non-development scenario) if (installationData.GameLocations.ModPaths.Count == 0) - return TryGetDefaultBaseline(installationData.EngineType); + { + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "No local baseline file found."); + return VerificationBaseline.Empty; + } - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "No local baseline file found."); - return VerificationBaseline.Empty; + Console.WriteLine("No baseline found locally."); + return TryGetDefaultBaseline(installationData.EngineType); } } @@ -110,7 +112,9 @@ private VerificationBaseline TryGetDefaultBaseline(GameEngineType engineType) internal VerificationBaseline LoadEmbeddedBaseline(GameEngineType engineType) { - var resourcePath = $"{_applicationEnvironment.AssemblyInfo.Assembly.GetName().Name}.Resources.Baselines.{engineType}"; + var baselineFileName = $"baseline-{engineType.ToString().ToLower()}.json"; + + var resourcePath = $"{typeof(BaselineResources).Namespace}.{baselineFileName}"; using var baselineStream = typeof(BaselineSelector).Assembly.GetManifestResourceStream(resourcePath)!; return VerificationBaseline.FromJson(baselineStream); } diff --git a/src/ModVerify.CliApp/Resources/Baselines/BaselineResources.cs b/src/ModVerify.CliApp/Resources/Baselines/BaselineResources.cs new file mode 100644 index 0000000..2b8caf7 --- /dev/null +++ b/src/ModVerify.CliApp/Resources/Baselines/BaselineResources.cs @@ -0,0 +1,4 @@ +namespace AET.ModVerify.App.Resources.Baselines; + +// Marker class to provide static namespace information for resource lookup. +internal class BaselineResources; \ No newline at end of file diff --git a/src/ModVerify.CliApp/Resources/Baselines/baseline-foc.json b/src/ModVerify.CliApp/Resources/Baselines/baseline-foc.json new file mode 100644 index 0000000..c94d121 --- /dev/null +++ b/src/ModVerify.CliApp/Resources/Baselines/baseline-foc.json @@ -0,0 +1,2081 @@ +{ + "version": "2.0", + "minSeverity": "Information", + "errors": [ + { + "id": "XML04", + "verifiers": [ + "XMLError" + ], + "message": "Expected double but got value \u002737\u0060\u0027. File=\u0027DATA\\XML\\COMMANDBARCOMPONENTS.XML #11571\u0027", + "severity": "Warning", + "context": [ + "DATA\\XML\\COMMANDBARCOMPONENTS.XML", + "Size", + "parentName=\u0027bm_text_steal\u0027" + ], + "asset": "Size" + }, + { + "id": "XML04", + "verifiers": [ + "XMLError" + ], + "message": "Expected integer but got \u002780, 20\u0027. File=\u0027DATA\\XML\\SFXEVENTSWEAPONS.XML #90\u0027", + "severity": "Warning", + "context": [ + "DATA\\XML\\SFXEVENTSWEAPONS.XML", + "Probability", + "parentName=\u0027Unit_TIE_Fighter_Fire\u0027" + ], + "asset": "Probability" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Shader effect \u0027Default.fx\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_SKIPRAY.ALO\u0027.", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UV_SKIPRAY.ALO" + ], + "asset": "Default.fx" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_smoke_small_thin2\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_PRISON.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_PRISON.ALO" + ], + "asset": "p_smoke_small_thin2" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_Kamino_Reflect.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_Kamino_Reflect.ALO" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_ssd_debris\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_ECLIPSE_UC_DC.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UV_ECLIPSE_UC_DC.ALO" + ], + "asset": "p_ssd_debris" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_p_proton_torpedo.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_p_proton_torpedo.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_DStar_LeverPanel.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_DStar_LeverPanel.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027lookat\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_ECLIPSE.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UV_ECLIPSE.ALO" + ], + "asset": "lookat" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" + ], + "message": "Could not find texture \u0027Cin_DeathStar.tga\u0027 for context: [ALTTEST.ALO].", + "severity": "Error", + "context": [ + "ALTTEST.ALO" + ], + "asset": "Cin_DeathStar.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_prison_light\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_PRISON.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_PRISON.ALO" + ], + "asset": "p_prison_light" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Shader effect \u0027Default.fx\u0027 not found for model \u0027DATA\\ART\\MODELS\\EV_MDU_SENSORNODE.ALO\u0027.", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\EV_MDU_SENSORNODE.ALO" + ], + "asset": "Default.fx" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" + ], + "message": "Could not find texture \u0027w_grenade.tga\u0027 for context: [W_GRENADE.ALO].", + "severity": "Error", + "context": [ + "W_GRENADE.ALO" + ], + "asset": "w_grenade.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Rbel_NavyRow.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Rbel_NavyRow.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_Planet_Alderaan_High.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_Planet_Alderaan_High.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027lookat\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_ECLIPSE_UC.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UV_ECLIPSE_UC.ALO" + ], + "asset": "lookat" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_desert_ground_dust\u0027 not found for model \u0027DATA\\ART\\MODELS\\EI_MARAJADE.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\EI_MARAJADE.ALO" + ], + "asset": "p_desert_ground_dust" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027p_splash_wake_lava.alo\u0027", + "severity": "Error", + "context": [], + "asset": "p_splash_wake_lava.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_rv_XWingProp.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_rv_XWingProp.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Fire_Huge.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Fire_Huge.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Probe_Droid.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Probe_Droid.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_05_STATION_D.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UB_05_STATION_D.ALO" + ], + "asset": "p_uwstation_death" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_desert_ground_dust\u0027 not found for model \u0027DATA\\ART\\MODELS\\RI_KYLEKATARN.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\RI_KYLEKATARN.ALO" + ], + "asset": "p_desert_ground_dust" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_steam_small\u0027 not found for model \u0027DATA\\ART\\MODELS\\RB_HEAVYVEHICLEFACTORY.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\RB_HEAVYVEHICLEFACTORY.ALO" + ], + "asset": "p_steam_small" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" + ], + "message": "Could not find texture \u0027p_particle_master\u0027 for context: [P_DIRT_EMITTER_TEST1.ALO].", + "severity": "Error", + "context": [ + "P_DIRT_EMITTER_TEST1.ALO" + ], + "asset": "p_particle_master" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_smoke_small_thin4\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_PRISON.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_PRISON.ALO" + ], + "asset": "p_smoke_small_thin4" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_EI_Vader.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_EI_Vader.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_smoke_small_thin2\u0027 not found for model \u0027DATA\\ART\\MODELS\\RB_HYPERVELOCITYGUN.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\RB_HYPERVELOCITYGUN.ALO" + ], + "asset": "p_smoke_small_thin2" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Shader effect \u0027Default.fx\u0027 not found for model \u0027DATA\\ART\\MODELS\\EV_TIE_LANCET.ALO\u0027.", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\EV_TIE_LANCET.ALO" + ], + "asset": "Default.fx" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_DeathStar_Wall.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_DeathStar_Wall.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_droid_steam.alo\u0027", + "severity": "Error", + "context": [], + "asset": "W_droid_steam.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_DeathStar_High.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_DeathStar_High.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027MODELS\u0027", + "severity": "Error", + "context": [], + "asset": "MODELS" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_AllShaders.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_AllShaders.ALO" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_bomb_spin\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_THERMAL_DETONATOR_EMPIRE.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\W_THERMAL_DETONATOR_EMPIRE.ALO" + ], + "asset": "p_bomb_spin" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_03_STATION_D.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UB_03_STATION_D.ALO" + ], + "asset": "p_uwstation_death" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Rbel_GreyGroup.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Rbel_GreyGroup.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_cold_tiny01\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_SCH.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_SCH.ALO" + ], + "asset": "p_cold_tiny01" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CINE_EV_StarDestroyer.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "CINE_EV_StarDestroyer.ALO" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_hp_archammer-damage\u0027 not found for model \u0027DATA\\ART\\MODELS\\EV_ARCHAMMER.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\EV_ARCHAMMER.ALO" + ], + "asset": "p_hp_archammer-damage" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Rbel_grey.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Rbel_grey.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Reb_CelebHall.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Reb_CelebHall.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027P_mptl-2a_Die\u0027 not found for model \u0027DATA\\ART\\MODELS\\RV_MPTL-2A.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\RV_MPTL-2A.ALO" + ], + "asset": "P_mptl-2a_Die" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_ImperialCraft.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_ImperialCraft.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_DStar_Dish_close.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_DStar_Dish_close.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027pe_bwing_yellow\u0027 not found for model \u0027DATA\\ART\\MODELS\\RV_BWING.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\RV_BWING.ALO" + ], + "asset": "pe_bwing_yellow" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_bridge.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_bridge.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_desert_ground_dust\u0027 not found for model \u0027DATA\\ART\\MODELS\\UI_SABOTEUR.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UI_SABOTEUR.ALO" + ], + "asset": "p_desert_ground_dust" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Trooper_Row.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Trooper_Row.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_EV_TieAdvanced.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_EV_TieAdvanced.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027w_sith_arch.alo\u0027", + "severity": "Error", + "context": [], + "asset": "w_sith_arch.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" + ], + "message": "Could not find texture \u0027NB_YsalamiriTree_B.tga\u0027 for context: [UV_MDU_CAGE.ALO].", + "severity": "Error", + "context": [ + "UV_MDU_CAGE.ALO" + ], + "asset": "NB_YsalamiriTree_B.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" + ], + "message": "Could not find texture \u0027W_TE_Rock_f_02_b.tga\u0027 for context: [EV_TIE_PHANTOM.ALO].", + "severity": "Error", + "context": [ + "EV_TIE_PHANTOM.ALO" + ], + "asset": "W_TE_Rock_f_02_b.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_EI_Palpatine.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_EI_Palpatine.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Rbel_Soldier.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Rbel_Soldier.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_smoke_small_thin2\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_MONCAL_BUILDING.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_MONCAL_BUILDING.ALO" + ], + "asset": "p_smoke_small_thin2" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_Bush_Swmp00.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_Bush_Swmp00.ALO" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Officer_Row.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Officer_Row.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_HIGH.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\W_STARS_HIGH.ALO" + ], + "asset": "Lensflare0" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" + ], + "message": "Could not find texture \u0027Cin_Reb_CelebHall_Wall_B.tga\u0027 for context: [W_SITH_LEFTHALL.ALO].", + "severity": "Error", + "context": [ + "W_SITH_LEFTHALL.ALO" + ], + "asset": "Cin_Reb_CelebHall_Wall_B.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_Coruscant.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_Coruscant.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_ewok_drag_dirt\u0027 not found for model \u0027DATA\\ART\\MODELS\\UI_EWOK_HANDLER.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UI_EWOK_HANDLER.ALO" + ], + "asset": "p_ewok_drag_dirt" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027P_heat_small01\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_VCH.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_VCH.ALO" + ], + "asset": "P_heat_small01" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_Vol_Steam01.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_Vol_Steam01.ALO" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_explosion_smoke_small_thin5\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_NOGHRI_HUT.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_NOGHRI_HUT.ALO" + ], + "asset": "p_explosion_smoke_small_thin5" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_MEDIUM.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\W_STARS_MEDIUM.ALO" + ], + "asset": "Lensflare0" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_02_STATION_D.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UB_02_STATION_D.ALO" + ], + "asset": "p_uwstation_death" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_Officer.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_Officer.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_04_STATION_D.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UB_04_STATION_D.ALO" + ], + "asset": "p_uwstation_death" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Lambda_Head.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Lambda_Head.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Biker_Row.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Biker_Row.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_DStar_protons.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_DStar_protons.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_desert_ground_dust\u0027 not found for model \u0027DATA\\ART\\MODELS\\UI_IG88.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UI_IG88.ALO" + ], + "asset": "p_desert_ground_dust" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Lambda_Mouth.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Lambda_Mouth.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_Planet_Hoth_High.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_Planet_Hoth_High.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_LOW.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\W_STARS_LOW.ALO" + ], + "asset": "Lensflare0" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_NavyTrooper_Row.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_NavyTrooper_Row.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_DStar_TurretLasers.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_DStar_TurretLasers.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_SwampGasEmit.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_SwampGasEmit.ALO" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_Shuttle_Tyderium.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_Shuttle_Tyderium.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_EV_Stardestroyer_Warp.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_EV_Stardestroyer_Warp.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_CINE_LUA.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\W_STARS_CINE_LUA.ALO" + ], + "asset": "Lensflare0" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_DeathStar_Hangar.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_DeathStar_Hangar.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Fire_Medium.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Fire_Medium.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_CINE.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\W_STARS_CINE.ALO" + ], + "asset": "Lensflare0" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Rbel_Soldier_Group.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Rbel_Soldier_Group.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027RV_nebulonb_D_death_00.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "RV_nebulonb_D_death_00.ALO" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_Volcano_Rock02.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_Volcano_Rock02.ALO" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027w_planet_volcanic.alo\u0027", + "severity": "Error", + "context": [], + "asset": "w_planet_volcanic.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_REb_CelebCharacters.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_REb_CelebCharacters.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Shader effect \u0027Default.fx\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_CRUSADERCLASSCORVETTE.ALO\u0027.", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UV_CRUSADERCLASSCORVETTE.ALO" + ], + "asset": "Default.fx" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" + ], + "message": "Could not find texture \u0027Cin_Reb_CelebHall_Wall.tga\u0027 for context: [W_SITH_LEFTHALL.ALO].", + "severity": "Error", + "context": [ + "W_SITH_LEFTHALL.ALO" + ], + "asset": "Cin_Reb_CelebHall_Wall.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_EV_lambdaShuttle_150.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_EV_lambdaShuttle_150.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_explosion_small_delay00\u0027 not found for model \u0027DATA\\ART\\MODELS\\EB_COMMANDCENTER.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\EB_COMMANDCENTER.ALO" + ], + "asset": "p_explosion_small_delay00" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" + ], + "message": "Could not find texture \u0027UB_girder_B.tga\u0027 for context: [UV_MDU_CAGE.ALO].", + "severity": "Error", + "context": [ + "UV_MDU_CAGE.ALO" + ], + "asset": "UB_girder_B.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_01_STATION_D.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UB_01_STATION_D.ALO" + ], + "asset": "p_uwstation_death" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0206_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0206_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0204_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0204_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0102_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0102_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0215_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0215_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0107_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0107_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0504_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Remove_Corruption_Leia" + ], + "asset": "U000_LEI0504_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027AMB_DES_CLEAR_LOOP_1.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Weather_Ambient_Clear_Sandstorm_Loop" + ], + "asset": "AMB_DES_CLEAR_LOOP_1.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0105_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0105_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0213_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0213_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0201_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0201_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0303_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0303_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0103_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0103_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0207_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0207_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_DEF3006_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Corrupt_Sabateur" + ], + "asset": "U000_DEF3006_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0309_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0309_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0209_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0209_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_DEF3106_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Weaken_Sabateur" + ], + "asset": "U000_DEF3106_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0503_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Remove_Corruption_Leia" + ], + "asset": "U000_LEI0503_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0502_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Remove_Corruption_Leia" + ], + "asset": "U000_LEI0502_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0212_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0212_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027AMB_URB_CLEAR_LOOP_1.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Weather_Ambient_Clear_Urban_Loop" + ], + "asset": "AMB_URB_CLEAR_LOOP_1.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0311_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0311_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0115_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0115_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0101_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0101_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0401_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Guard_Leia" + ], + "asset": "U000_LEI0401_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0315_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0315_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0106_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0106_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0603_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Increase_Production_Leia" + ], + "asset": "U000_LEI0603_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0104_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0104_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0501_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Remove_Corruption_Leia" + ], + "asset": "U000_LEI0501_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027TESTUNITMOVE_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Gneneric_Test" + ], + "asset": "TESTUNITMOVE_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0108_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0108_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_MCF1601_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_StarDest_MC30_Frigate" + ], + "asset": "U000_MCF1601_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0111_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0111_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0211_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0211_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0110_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0110_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0403_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Guard_Leia" + ], + "asset": "U000_LEI0403_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0306_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0306_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0308_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0308_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0112_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0112_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0301_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0301_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0404_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Guard_Leia" + ], + "asset": "U000_LEI0404_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_TMC0212_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Tie_Mauler" + ], + "asset": "U000_TMC0212_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027EGL_STAR_VIPER_SPINNING_1.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Star_Viper_Spinning_By" + ], + "asset": "EGL_STAR_VIPER_SPINNING_1.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0208_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0208_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0604_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Increase_Production_Leia" + ], + "asset": "U000_LEI0604_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027FS_BEETLE_3.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "SFX_Anim_Beetle_Footsteps" + ], + "asset": "FS_BEETLE_3.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0109_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0109_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0202_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0202_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0602_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Increase_Production_Leia" + ], + "asset": "U000_LEI0602_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0305_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0305_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_MAL0503_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Assist_Move_Missile_Launcher" + ], + "asset": "U000_MAL0503_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0601_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Increase_Production_Leia" + ], + "asset": "U000_LEI0601_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_ARC3106_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Complete_Troops_Arc_Hammer" + ], + "asset": "U000_ARC3106_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027FS_BEETLE_4.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "SFX_Anim_Beetle_Footsteps" + ], + "asset": "FS_BEETLE_4.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027FS_BEETLE_1.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "SFX_Anim_Beetle_Footsteps" + ], + "asset": "FS_BEETLE_1.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0205_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0205_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0113_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0113_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0314_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0314_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0304_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0304_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0203_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0203_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027C000_DST0102_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "EHD_Death_Star_Activate" + ], + "asset": "C000_DST0102_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0114_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Select_Leia" + ], + "asset": "U000_LEI0114_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_ARC3104_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Produce_Troops_Arc_Hammer" + ], + "asset": "U000_ARC3104_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027FS_BEETLE_2.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "SFX_Anim_Beetle_Footsteps" + ], + "asset": "FS_BEETLE_2.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_ARC3105_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Complete_Troops_Arc_Hammer" + ], + "asset": "U000_ARC3105_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0307_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0307_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0402_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Guard_Leia" + ], + "asset": "U000_LEI0402_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0312_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0312_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0210_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0210_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0313_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [ + "Unit_Attack_Leia" + ], + "asset": "U000_LEI0313_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + ], + "message": "Could not find GUI texture \u0027underworld_logo_selected.tga\u0027 at location \u0027MegaTexture\u0027.", + "severity": "Error", + "context": [ + "IDC_PLAY_FACTION_A_BUTTON_BIG", + "MegaTexture" + ], + "asset": "underworld_logo_selected.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + ], + "message": "Could not find GUI texture \u0027i_button_petro_sliver.tga\u0027 at location \u0027MegaTexture\u0027.", + "severity": "Error", + "context": [ + "IDC_MENU_PETRO_LOGO", + "MegaTexture" + ], + "asset": "i_button_petro_sliver.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + ], + "message": "Could not find GUI texture \u0027i_dialogue_button_large_middle_off.tga\u0027 at location \u0027Repository\u0027.", + "severity": "Error", + "context": [ + "IDC_PLAY_FACTION_B_BUTTON_BIG", + "Repository" + ], + "asset": "i_dialogue_button_large_middle_off.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + ], + "message": "Could not find GUI texture \u0027underworld_logo_rollover.tga\u0027 at location \u0027MegaTexture\u0027.", + "severity": "Error", + "context": [ + "IDC_PLAY_FACTION_A_BUTTON_BIG", + "MegaTexture" + ], + "asset": "underworld_logo_rollover.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + ], + "message": "Could not find GUI texture \u0027underworld_logo_off.tga\u0027 at location \u0027MegaTexture\u0027.", + "severity": "Error", + "context": [ + "IDC_PLAY_FACTION_A_BUTTON_BIG", + "MegaTexture" + ], + "asset": "underworld_logo_off.tga" + } + ] +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/GithubReleaseEntry.cs b/src/ModVerify.CliApp/Updates/Github/GithubReleaseEntry.cs similarity index 91% rename from src/ModVerify.CliApp/Updates/GithubReleaseEntry.cs rename to src/ModVerify.CliApp/Updates/Github/GithubReleaseEntry.cs index 60b508d..4cd4585 100644 --- a/src/ModVerify.CliApp/Updates/GithubReleaseEntry.cs +++ b/src/ModVerify.CliApp/Updates/Github/GithubReleaseEntry.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace AET.ModVerify.App.Updates; +namespace AET.ModVerify.App.Updates.Github; [JsonUnmappedMemberHandling(JsonUnmappedMemberHandling.Skip)] [method: JsonConstructor] diff --git a/src/ModVerify.CliApp/Updates/GithubReleaseList.cs b/src/ModVerify.CliApp/Updates/Github/GithubReleaseList.cs similarity index 70% rename from src/ModVerify.CliApp/Updates/GithubReleaseList.cs rename to src/ModVerify.CliApp/Updates/Github/GithubReleaseList.cs index a6f0055..92ef368 100644 --- a/src/ModVerify.CliApp/Updates/GithubReleaseList.cs +++ b/src/ModVerify.CliApp/Updates/Github/GithubReleaseList.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -namespace AET.ModVerify.App.Updates; +namespace AET.ModVerify.App.Updates.Github; internal sealed class GithubReleaseList : List; \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/Github/GithubUpdateChecker.cs b/src/ModVerify.CliApp/Updates/Github/GithubUpdateChecker.cs new file mode 100644 index 0000000..823d907 --- /dev/null +++ b/src/ModVerify.CliApp/Updates/Github/GithubUpdateChecker.cs @@ -0,0 +1,69 @@ +using System; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Semver; + +namespace AET.ModVerify.App.Updates.Github; + +internal class GithubUpdateChecker +{ + private readonly ILogger? _logger; + private readonly ModVerifyAppEnvironment _appEnvironment; + + public GithubUpdateChecker(IServiceProvider serviceProvider) + { + _logger = serviceProvider.GetService()?.CreateLogger(GetType()); + _appEnvironment = serviceProvider.GetRequiredService(); + } + + public async Task CheckForUpdateAsync() + { + var githubReleases = await DownloadReleaseList().ConfigureAwait(false); + + var branch = GithubUpdateConstants.BranchName; + var latestRelease = githubReleases.FirstOrDefault(r => r.Branch == branch); + + if (latestRelease == null) + throw new InvalidOperationException($"Unable to find a release for branch '{branch}'."); + + if (!SemVersion.TryParse(latestRelease.Tag, SemVersionStyles.Any, out var latestVersion)) + throw new InvalidOperationException($"Cannot create a version from tag '{latestRelease.Tag}'."); + + var currentVersion = _appEnvironment.AssemblyInfo.InformationalAsSemVer(); + if (currentVersion is null) + throw new InvalidOperationException("Unable to get current version."); + + if (SemVersion.ComparePrecedence(currentVersion, latestVersion) >= 0) + { + _logger?.LogDebug($"No update available - [Current Version = {currentVersion}], [Available Version = {latestVersion}]"); + return default; + } + + _logger?.LogDebug($"Update available - [Current Version = {currentVersion}], [Available Version = {latestVersion}]"); + return new GithubUpdateInfo + { + DownloadLink = GithubUpdateConstants.ModVerifyReleasesDownloadLink, + IsUpdateAvailable = true, + NewVersion = latestVersion.ToString() + }; + } + + private static async Task DownloadReleaseList() + { + using var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(GithubUpdateConstants.UserAgent); + using var downloadStream = await httpClient.GetStreamAsync(GithubUpdateConstants.GithubReleasesApiLink).ConfigureAwait(false); + + using var jsonStream = new MemoryStream(); + await downloadStream.CopyToAsync(jsonStream).ConfigureAwait(false); + jsonStream.Seek(0, SeekOrigin.Begin); + + var releases = await JsonSerializer.DeserializeAsync(jsonStream).ConfigureAwait(false); + return releases ?? throw new InvalidOperationException("Unable to deserialize releases."); + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/Github/GithubUpdateConstants.cs b/src/ModVerify.CliApp/Updates/Github/GithubUpdateConstants.cs new file mode 100644 index 0000000..7695246 --- /dev/null +++ b/src/ModVerify.CliApp/Updates/Github/GithubUpdateConstants.cs @@ -0,0 +1,9 @@ +namespace AET.ModVerify.App.Updates.Github; + +internal static class GithubUpdateConstants +{ + public const string BranchName = "main"; + public const string GithubReleasesApiLink = "https://api.github.com/repos/AlamoEngine-Tools/ModVerify/releases"; + public const string ModVerifyReleasesDownloadLink = "https://github.com/AlamoEngine-Tools/ModVerify/releases/latest"; + public const string UserAgent = "AET.Modifo"; +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/UpdateInfo.cs b/src/ModVerify.CliApp/Updates/Github/GithubUpdateInfo.cs similarity index 75% rename from src/ModVerify.CliApp/Updates/UpdateInfo.cs rename to src/ModVerify.CliApp/Updates/Github/GithubUpdateInfo.cs index c4af83f..8d8f532 100644 --- a/src/ModVerify.CliApp/Updates/UpdateInfo.cs +++ b/src/ModVerify.CliApp/Updates/Github/GithubUpdateInfo.cs @@ -1,8 +1,8 @@ using System.Diagnostics.CodeAnalysis; -namespace AET.ModVerify.App.Updates; +namespace AET.ModVerify.App.Updates.Github; -internal readonly struct UpdateInfo +internal readonly struct GithubUpdateInfo { public string DownloadLink { get; init; } diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index 5c2b090..ac2e774 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -1,13 +1,10 @@ using System; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Text.Json; using System.Threading.Tasks; +using AET.ModVerify.App.Updates.Github; +using AnakinRaW.ApplicationBase; using AnakinRaW.ApplicationBase.Update.Options; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Semver; namespace AET.ModVerify.App.Updates; @@ -24,101 +21,45 @@ public ModVerifyUpdater(ApplicationUpdateOptions updateOptions, IServiceProvider _appEnvironment = serviceProvider.GetRequiredService(); } - - - public async Task RunUpdateProcedure(ModVerifyUpdateMode mode) + public async Task RunUpdateProcedure(ModVerifyUpdateMode mode) { - //if (_settings.Offline) - //{ - // _logger?.LogTrace("App is running in offline mode. Nothing to do here."); - // return; - //} - - //if (!IsUpdatable(out var updateEnv)) - //{ - // await CheckUpdateGithub(); - // return; - //} - - - - _logger?.LogDebug("Checking for available update"); - - //try - //{ - // var updateInfo = await updateChecker.CheckForUpdateAsync().ConfigureAwait(false); - // if (updateInfo.IsUpdateAvailable) - // { - // ConsoleUtilities.WriteHorizontalLine(); + _logger?.LogTrace("Running update procedure - '{mode}'", mode); - // Console.ForegroundColor = ConsoleColor.DarkGreen; - // Console.WriteLine("New Update Available!"); - // Console.ResetColor(); - - // Console.WriteLine($"Version: {updateInfo.NewVersion}, Download here: {updateInfo.DownloadLink}"); - // ConsoleUtilities.WriteHorizontalLine(); - // Console.WriteLine(); - - // } - //} - //catch (Exception e) - //{ - // _logger?.LogWarning(ModVerifyConstants.ConsoleEventId, $"Unable to check for updates due to an internal error: {e.Message}"); - // _logger?.LogTrace(e, "Checking for update failed: " + e.Message); - //} - - return 0; - } - - private async Task CheckUpdateGithub() - { - + // If we are in the check-only mode, GitHub check is sufficient. + if (mode == ModVerifyUpdateMode.CheckOnly) + { + await CheckForUpdateAndReport().ConfigureAwait(false); + return; + } } - - public async Task CheckForUpdateAsync() + private async Task CheckForUpdateAndReport() { - var githubReleases = await DownloadReleaseList().ConfigureAwait(false); - - var branch = ModVerifyUpdaterInformation.BranchName; - var latestRelease = githubReleases.FirstOrDefault(r => r.Branch == branch); - - if (latestRelease == null) - throw new InvalidOperationException($"Unable to find a release for branch '{branch}'."); - - if (!SemVersion.TryParse(latestRelease.Tag, SemVersionStyles.Any, out var latestVersion)) - throw new InvalidOperationException($"Cannot create a version from tag '{latestRelease.Tag}'."); - - var currentVersion = ModVerifyUpdaterInformation.CurrentVersion; - if (currentVersion is null) - throw new InvalidOperationException("Unable to get current version."); - - if (SemVersion.ComparePrecedence(currentVersion, latestVersion) >= 0) + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Checking for available update..."); + try { - _logger?.LogDebug($"No update available - [Current Version = {currentVersion}], [Available Version = {latestVersion}]"); - return default; + var updateInfo = await new GithubUpdateChecker(_serviceProvider) + .CheckForUpdateAsync().ConfigureAwait(false); + + if (updateInfo.IsUpdateAvailable) + { + using (ConsoleUtilities.HorizontalLineSeparatedBlock(startWithNewLine: true, newLineAtEnd: true)) + { + Console.ForegroundColor = ConsoleColor.DarkGreen; + Console.WriteLine("New Update Available!"); + Console.ResetColor(); + Console.WriteLine($"Version: {updateInfo.NewVersion}, Download here: {updateInfo.DownloadLink}"); + } + } + else + { + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "No update available."); + } } - - _logger?.LogDebug($"Update available - [Current Version = {currentVersion}], [Available Version = {latestVersion}]"); - return new UpdateInfo + catch (Exception e) { - DownloadLink = ModVerifyUpdaterInformation.ModVerifyReleasesDownloadLink, - IsUpdateAvailable = true, - NewVersion = latestVersion.ToString() - }; - } - - private static async Task DownloadReleaseList() - { - using var httpClient = new HttpClient(); - httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(ModVerifyUpdaterInformation.UserAgent); - using var downloadStream = await httpClient.GetStreamAsync(ModVerifyUpdaterInformation.GithubReleasesApiLink).ConfigureAwait(false); - - using var jsonStream = new MemoryStream(); - await downloadStream.CopyToAsync(jsonStream).ConfigureAwait(false); - jsonStream.Seek(0, SeekOrigin.Begin); - - var releases = await JsonSerializer.DeserializeAsync(jsonStream).ConfigureAwait(false); - return releases ?? throw new InvalidOperationException("Unable to deserialize releases."); + _logger?.LogWarning(ModVerifyConstants.ConsoleEventId, "Unable to check for updates due to an internal error: {message}", e.Message); + _logger?.LogTrace(e, "Checking for update failed: {message}", e.Message); + } } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdaterInformation.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdaterInformation.cs deleted file mode 100644 index b16083a..0000000 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdaterInformation.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Diagnostics; -using Semver; - -namespace AET.ModVerify.App.Updates; - -internal static class ModVerifyUpdaterInformation -{ - public const string BranchName = "main"; - public const string GithubReleasesApiLink = "https://api.github.com/repos/AlamoEngine-Tools/ModVerify/releases"; - public const string ModVerifyReleasesDownloadLink = "https://github.com/AlamoEngine-Tools/ModVerify/releases/latest"; - public const string UserAgent = "AET.Modifo"; - - public static readonly SemVersion? CurrentVersion; - - static ModVerifyUpdaterInformation() - { - var currentAssembly = typeof(ModVerifyUpdaterInformation).Assembly; - var fi = FileVersionInfo.GetVersionInfo(currentAssembly.Location); - SemVersion.TryParse(fi.ProductVersion, SemVersionStyles.Any, out var currentVersion); - CurrentVersion = currentVersion; - } -} \ No newline at end of file diff --git a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj index 3297a24..7b9e6d6 100644 --- a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj +++ b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj @@ -2,7 +2,7 @@ net9.0 - $(TargetFrameworks);net48 + $(TargetFrameworks);net481 false preview diff --git a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs index 1fdaf46..294baf7 100644 --- a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs +++ b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs @@ -4,6 +4,7 @@ using System.IO.Abstractions; using ModVerify.CliApp.Test.TestData; using Testably.Abstractions; +using ModVerify.CliApp.Test.Utilities; namespace ModVerify.CliApp.Test; @@ -29,6 +30,18 @@ public void Parse_UpdateAppArg() Assert.Equal("test", settings.UpdateOptions.BranchName); Assert.Equal("https://examlple.com", settings.UpdateOptions.ManifestUrl); } + + [Fact] + public void Parse_CombinedIsNotAllowed() + { + const string argString = "verify --updateBranch test --updateManifestUrl https://examlple.com"; + + var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + + Assert.False(settings.HasOptions); + Assert.Null(settings.ModVerifyOptions); + Assert.Null(settings.UpdateOptions); + } } public class ModVerifyOptionsParserTest_NotUpdateable : ModVerifyOptionsParserTestBase @@ -46,6 +59,7 @@ protected override ApplicationEnvironment CreateEnvironment() [InlineData("verify --junkOption")] [InlineData("createBaseline --junkOption")] [InlineData("updateApplication")] + [InlineData("updateApplication --updateBranch test --updateManifestUrl https://examlple.com")] public void Parse_InvalidArgs_NotUpdateable(string argString) { var settings = Parser.Parse(argString.Split(' ', StringSplitOptions.RemoveEmptyEntries)); diff --git a/test/ModVerify.CliApp.Test/Utilities/StringExtensions.cs b/test/ModVerify.CliApp.Test/Utilities/StringExtensions.cs new file mode 100644 index 0000000..052c322 --- /dev/null +++ b/test/ModVerify.CliApp.Test/Utilities/StringExtensions.cs @@ -0,0 +1,13 @@ +using System; + +namespace ModVerify.CliApp.Test.Utilities; + +internal static class StringExtensions +{ +#if NETFRAMEWORK + public static string[] Split(this string str, char separator, StringSplitOptions options) + { + return str.Split([separator], options); + } +#endif +} \ No newline at end of file From 3e652d562204d2e2fa5da76061a97c906be40432 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 16 Aug 2025 18:03:04 +0200 Subject: [PATCH 24/61] update sub --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 74dd654..dacdd20 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 74dd654af059698b6e80c8115746d6907db79471 +Subproject commit dacdd2091baa07f8df2935b3e363214b28ecc003 From aa94cdaff148593282f8bd95e21d47a714e79bd8 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 16 Aug 2025 18:28:53 +0200 Subject: [PATCH 25/61] print used baseline file --- src/ModVerify.CliApp/ModVerifyApplication.cs | 8 +++- .../Reporting/BaselineSelector.cs | 40 +++++++++++-------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index bb013e5..6120936 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -208,7 +208,10 @@ private async Task WriteBaseline( private GlobalVerifyReportSettings CreateGlobalReportSettings(VerifyInstallationData installData) { var baselineSelector = new BaselineSelector(settings, services); - var baseline = baselineSelector.SelectBaseline(installData); + var baseline = baselineSelector.SelectBaseline(installData, out var baselinePath); + + if (baseline.Count > 0) + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Using baseline '{baselinePath}'"); var suppressionsFile = settings.ReportSettings.SuppressionsPath; SuppressionList suppressions; @@ -219,6 +222,9 @@ private GlobalVerifyReportSettings CreateGlobalReportSettings(VerifyInstallation { using var fs = _fileSystem.File.OpenRead(suppressionsFile); suppressions = SuppressionList.FromJson(fs); + + if (suppressions.Count > 0) + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Using suppressions from '{suppressionsFile}'"); } diff --git a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs index 55004e4..178f7b5 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs @@ -16,13 +16,14 @@ internal sealed class BaselineSelector(ModVerifyAppSettings settings, IServicePr private readonly ILogger? _logger = services.GetService()?.CreateLogger(typeof(ModVerifyApplication)); private readonly BaselineFactory _baselineFactory = new(services); - public VerificationBaseline SelectBaseline(VerifyInstallationData installationData) + public VerificationBaseline SelectBaseline(VerifyInstallationData installationData, out string? usedBaselinePath) { var baselinePath = settings.ReportSettings.BaselinePath; if (!string.IsNullOrEmpty(baselinePath)) { try { + usedBaselinePath = baselinePath; return _baselineFactory.CreateBaseline(baselinePath!); } catch (InvalidBaselineException e) @@ -43,18 +44,19 @@ public VerificationBaseline SelectBaseline(VerifyInstallationData installationDa if (!settings.ReportSettings.SearchBaselineLocally) { _logger?.LogDebug(ModVerifyConstants.ConsoleEventId, "No baseline path specified and local search is not enabled. Using empty baseline."); + usedBaselinePath = null; return VerificationBaseline.Empty; } if (settings.Interactive) - return FindBaselineInteractive(installationData); - + return FindBaselineInteractive(installationData, out usedBaselinePath); + // If the application is not interactive, we only use a baseline file present in the directory of the verification target. - return FindBaselineNonInteractive(installationData.GameLocations.TargetPath); + return FindBaselineNonInteractive(installationData.GameLocations.TargetPath, out usedBaselinePath); } - private VerificationBaseline FindBaselineInteractive(VerifyInstallationData installationData) + private VerificationBaseline FindBaselineInteractive(VerifyInstallationData installationData, out string? baselinePath) { // The application is in interactive mode. We apply the following lookup: // 1. Use a baseline found in the directory of the verification target. @@ -65,7 +67,7 @@ private VerificationBaseline FindBaselineInteractive(VerifyInstallationData inst _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Searching for local baseline files..."); if (!_baselineFactory.TryCreateBaseline(installationData.GameLocations.TargetPath, out var baseline, - out var baselinePath)) + out baselinePath)) { if (!_baselineFactory.TryCreateBaseline(".", out baseline, out baselinePath)) { @@ -78,7 +80,7 @@ private VerificationBaseline FindBaselineInteractive(VerifyInstallationData inst } Console.WriteLine("No baseline found locally."); - return TryGetDefaultBaseline(installationData.EngineType); + return TryGetDefaultBaseline(installationData.EngineType, out baselinePath); } } @@ -89,8 +91,9 @@ private VerificationBaseline FindBaselineInteractive(VerifyInstallationData inst : VerificationBaseline.Empty; } - private VerificationBaseline TryGetDefaultBaseline(GameEngineType engineType) + private VerificationBaseline TryGetDefaultBaseline(GameEngineType engineType, out string? baselinePath) { + baselinePath = null; if (engineType == GameEngineType.Eaw) { // TODO: EAW currently not implemented @@ -100,33 +103,36 @@ private VerificationBaseline TryGetDefaultBaseline(GameEngineType engineType) if (!ConsoleUtilities.UserYesNoQuestion($"Do you want to load the default baseline for game engine '{engineType}'?")) return VerificationBaseline.Empty; + var baselineFileName = $"baseline-{engineType.ToString().ToLower()}.json"; + var resourcePath = $"{typeof(BaselineResources).Namespace}.{baselineFileName}"; + baselinePath = $"{baselineFileName} (Default)"; + try { - return LoadEmbeddedBaseline(engineType); + return LoadEmbeddedBaseline(resourcePath); } catch (InvalidBaselineException) { - throw new InvalidOperationException("Invalid baseline packed along ModVerify App. Please reach out to the creators. Thanks!"); + throw new InvalidOperationException( + "Invalid baseline packed along ModVerify App. Please reach out to the creators. Thanks!"); } } - internal VerificationBaseline LoadEmbeddedBaseline(GameEngineType engineType) + internal VerificationBaseline LoadEmbeddedBaseline(string resourcePath) { - var baselineFileName = $"baseline-{engineType.ToString().ToLower()}.json"; - - var resourcePath = $"{typeof(BaselineResources).Namespace}.{baselineFileName}"; using var baselineStream = typeof(BaselineSelector).Assembly.GetManifestResourceStream(resourcePath)!; return VerificationBaseline.FromJson(baselineStream); } - private VerificationBaseline FindBaselineNonInteractive(string targetPath) + private VerificationBaseline FindBaselineNonInteractive(string targetPath, out string? usedPath) { - if (_baselineFactory.TryCreateBaseline(targetPath, out var baseline, out var path)) + if (_baselineFactory.TryCreateBaseline(targetPath, out var baseline, out usedPath)) { - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Automatically applying local baseline file '{Path}'.", path); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Automatically applying local baseline file '{Path}'.", usedPath); return baseline; } _logger?.LogTrace("No baseline file found in taget path '{TargetPath}'.", targetPath); + usedPath = null; return VerificationBaseline.Empty; } } \ No newline at end of file From 8ec90c53cf4f342707e58d43c232ed86a511ebcd Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 16 Aug 2025 18:29:44 +0200 Subject: [PATCH 26/61] make marker static --- src/ModVerify.CliApp/Resources/Baselines/BaselineResources.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ModVerify.CliApp/Resources/Baselines/BaselineResources.cs b/src/ModVerify.CliApp/Resources/Baselines/BaselineResources.cs index 2b8caf7..bf49f9e 100644 --- a/src/ModVerify.CliApp/Resources/Baselines/BaselineResources.cs +++ b/src/ModVerify.CliApp/Resources/Baselines/BaselineResources.cs @@ -1,4 +1,4 @@ namespace AET.ModVerify.App.Resources.Baselines; // Marker class to provide static namespace information for resource lookup. -internal class BaselineResources; \ No newline at end of file +internal static class BaselineResources; \ No newline at end of file From df67160cdfb9bb0551730c86439992ae472f83c3 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 16 Aug 2025 18:52:49 +0200 Subject: [PATCH 27/61] fix test --- src/ModVerify.CliApp/Reporting/BaselineSelector.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs index 178f7b5..9650ddd 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs @@ -103,13 +103,11 @@ private VerificationBaseline TryGetDefaultBaseline(GameEngineType engineType, ou if (!ConsoleUtilities.UserYesNoQuestion($"Do you want to load the default baseline for game engine '{engineType}'?")) return VerificationBaseline.Empty; - var baselineFileName = $"baseline-{engineType.ToString().ToLower()}.json"; - var resourcePath = $"{typeof(BaselineResources).Namespace}.{baselineFileName}"; - baselinePath = $"{baselineFileName} (Default)"; + baselinePath = $"{engineType} (Default)"; try { - return LoadEmbeddedBaseline(resourcePath); + return LoadEmbeddedBaseline(engineType); } catch (InvalidBaselineException) { @@ -118,8 +116,11 @@ private VerificationBaseline TryGetDefaultBaseline(GameEngineType engineType, ou } } - internal VerificationBaseline LoadEmbeddedBaseline(string resourcePath) + internal VerificationBaseline LoadEmbeddedBaseline(GameEngineType engineType) { + var baselineFileName = $"baseline-{engineType.ToString().ToLower()}.json"; + var resourcePath = $"{typeof(BaselineResources).Namespace}.{baselineFileName}"; + using var baselineStream = typeof(BaselineSelector).Assembly.GetManifestResourceStream(resourcePath)!; return VerificationBaseline.FromJson(baselineStream); } From 7469b2070e649e512f62e8edaf7935a46312e571 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 17 Aug 2025 21:20:16 +0200 Subject: [PATCH 28/61] some cli improvements --- src/ModVerify.CliApp/Program.cs | 4 +- .../Updates/ModVerifyUpdater.cs | 71 +++++++- .../SelfUpdate/ModVerifyApplicationUpdater.cs | 33 ++++ .../Utilities/ExtensionMethods.cs | 7 +- src/ModVerify.CliApp/Utilities/Spinner.cs | 152 ++++++++++++++++++ 5 files changed, 258 insertions(+), 9 deletions(-) create mode 100644 src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs create mode 100644 src/ModVerify.CliApp/Utilities/Spinner.cs diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 0632752..c4ddb2b 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -318,8 +318,8 @@ private async Task HandleUpdate(IServiceProvider serviceProvider) try { Logger?.LogDebug("Running update with mode '{ModVerifyUpdateMode}'", updateMode); - var modVerifyUpdater = new ModVerifyUpdater(updateOptions, serviceProvider); - await modVerifyUpdater.RunUpdateProcedure(updateMode).ConfigureAwait(false); + var modVerifyUpdater = new ModVerifyUpdater(serviceProvider); + await modVerifyUpdater.RunUpdateProcedure(updateOptions, updateMode).ConfigureAwait(false); Logger?.LogDebug("Update procedure completed successfully."); return 0; } diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index ac2e774..1e0be31 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -1,10 +1,13 @@ -using System; -using System.Threading.Tasks; -using AET.ModVerify.App.Updates.Github; +using AET.ModVerify.App.Updates.Github; using AnakinRaW.ApplicationBase; using AnakinRaW.ApplicationBase.Update.Options; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; +using AET.ModVerify.App.Updates.SelfUpdate; +using AET.ModVerify.App.Utilities; +using Vanara.PInvoke; namespace AET.ModVerify.App.Updates; @@ -14,14 +17,14 @@ internal sealed class ModVerifyUpdater private readonly ILogger? _logger; private readonly ModVerifyAppEnvironment _appEnvironment; - public ModVerifyUpdater(ApplicationUpdateOptions updateOptions, IServiceProvider serviceProvider) + public ModVerifyUpdater(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; _logger = serviceProvider.GetService()?.CreateLogger(GetType()); _appEnvironment = serviceProvider.GetRequiredService(); } - public async Task RunUpdateProcedure(ModVerifyUpdateMode mode) + public async Task RunUpdateProcedure(ApplicationUpdateOptions updateOptions, ModVerifyUpdateMode mode) { _logger?.LogTrace("Running update procedure - '{mode}'", mode); @@ -31,6 +34,64 @@ public async Task RunUpdateProcedure(ModVerifyUpdateMode mode) await CheckForUpdateAndReport().ConfigureAwait(false); return; } + + await UpdateApplication(updateOptions, mode).ConfigureAwait(false); + } + + private async Task UpdateApplication(ApplicationUpdateOptions updateOptions, ModVerifyUpdateMode mode) + { + if (!_appEnvironment.IsUpdatable(out var updatableEnvironment)) + { + _logger?.LogWarning("Application is not updatable, yet we entered the update path. Checking only."); + await CheckForUpdateAndReport().ConfigureAwait(false); + return; + } + + var updater = new ModVerifyApplicationUpdater(mode, updatableEnvironment, _serviceProvider); + + var actualBranchName = updater.GetBranchNameFromRegistry(updateOptions.BranchName, false); + var branch = updater.CreateBranch(actualBranchName, updateOptions.ManifestUrl); + + using (var block = ConsoleUtilities.CreateFixedHorizontalLineBlock('=', 40, + startWithNewLine: true, + newLineAtEnd: true)) + { + block.WriteLine("This is inside the block."); + block.WriteLine("The bottom line will move down as you write more lines."); + // Simulate long-running output + + + for (var i = 0; i < 3; i++) + { + await Task.Delay(500); + await block.Writer.WriteAsync(i.ToString()); + } + + block.WriteLine(); + + for (var i = 0; i < 3; i++) + { + await Task.Delay(500); + block.WriteLine(i.ToString()); + } + + var spinnerOptions = new ConsoleSpinnerOptions + { + Writer = block.Writer, + CompletedMessage = "DONE", + RunningMessage = "Checking for update...", + FailedMessage = "Update check failed", + HideCursor = true + }; + await ConsoleSpinner.Run(async () => + { + await Task.Delay(2000); // Simulate some work + }, spinnerOptions); + + block.WriteLine("456"); + } + + Console.WriteLine(123); } private async Task CheckForUpdateAndReport() diff --git a/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs b/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs new file mode 100644 index 0000000..df03aaa --- /dev/null +++ b/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using AnakinRaW.ApplicationBase.Environment; +using AnakinRaW.ApplicationBase.Update; +using AnakinRaW.AppUpdaterFramework.Metadata.Product; +using AnakinRaW.AppUpdaterFramework.Metadata.Update; + +namespace AET.ModVerify.App.Updates.SelfUpdate; + + +internal class ModVerifyApplicationUpdater : ApplicationUpdater +{ + public ModVerifyApplicationUpdater( + ModVerifyUpdateMode updateMode, + UpdatableApplicationEnvironment environment, + IServiceProvider serviceProvider) + : base(environment, serviceProvider) + { + if (updateMode == ModVerifyUpdateMode.CheckOnly) + throw new ArgumentException("Check-only mode is not supported by this updater.", nameof(updateMode)); + } + + public override Task CheckForUpdateAsync(ProductBranch branch, CancellationToken token = default) + { + throw new NotImplementedException(); + } + + public override Task UpdateAsync(UpdateCatalog updateCatalog, CancellationToken token = default) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs index 325fa7e..8f833ee 100644 --- a/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs +++ b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs @@ -1,4 +1,5 @@ -using AET.ModVerify.App.Settings.CommandLine; +using System.Diagnostics.CodeAnalysis; +using AET.ModVerify.App.Settings.CommandLine; using AnakinRaW.ApplicationBase.Environment; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure.Games; @@ -22,7 +23,9 @@ public static bool IsUpdatable(this ApplicationEnvironment modVerifyEnvironment) return modVerifyEnvironment.IsUpdatable(out _); } - public static bool IsUpdatable(this ApplicationEnvironment applicationEnvironment, out UpdatableApplicationEnvironment? updatableEnvironment) + public static bool IsUpdatable( + this ApplicationEnvironment applicationEnvironment, + [NotNullWhen(true)] out UpdatableApplicationEnvironment? updatableEnvironment) { updatableEnvironment = applicationEnvironment as UpdatableApplicationEnvironment; return updatableEnvironment is not null; diff --git a/src/ModVerify.CliApp/Utilities/Spinner.cs b/src/ModVerify.CliApp/Utilities/Spinner.cs new file mode 100644 index 0000000..e9e4442 --- /dev/null +++ b/src/ModVerify.CliApp/Utilities/Spinner.cs @@ -0,0 +1,152 @@ +using AnakinRaW.CommonUtilities; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace AET.ModVerify.App.Utilities; + +/// +/// Options for configuring a . +/// +public sealed class ConsoleSpinnerOptions +{ + public string? RunningMessage { get; init; } + public string? CompletedMessage { get; init; } + public string? FailedMessage { get; init; } + public bool HideCursor { get; init; } + public TextWriter Writer { get; init; } = Console.Out; + public int Interval { get; init; } = 200; + public string[] Animation { get; init; } = ["|", "/", "-", "\\"]; + + public static ConsoleSpinnerOptions Default { get; } = new(); +} + + + +internal sealed class ConsoleSpinner : IDisposable +{ + private readonly ConsoleSpinnerOptions _options; + private readonly CancellationTokenSource _cts = new(); + private readonly Task _observedTask; + private readonly TaskCompletionSource _cleanupCompleted = new(); + private readonly bool _origCursorVisibility; + private readonly string[] _animation; + private int _frame; + private int _lastTextLength; + + private ConsoleSpinner(Task observedTask, ConsoleSpinnerOptions options) + { + _observedTask = observedTask; + _options = options; + _animation = options.Animation; + _origCursorVisibility = Console.CursorVisible; + + if (_options.HideCursor) + Console.CursorVisible = false; + + SpinnerLoop().Forget(); + } + + public static async Task Run(Task task, ConsoleSpinnerOptions? options = null) + { + options ??= ConsoleSpinnerOptions.Default; + using var spinner = new ConsoleSpinner(task, options); + await task.ConfigureAwait(false); + await spinner._cleanupCompleted.Task.ConfigureAwait(false); + } + + public static Task Run(Func asyncAction, ConsoleSpinnerOptions? options = null) + { + if (asyncAction is null) + throw new ArgumentNullException(nameof(asyncAction)); + return Run(asyncAction(), options); + } + + public static ConsoleSpinner Endless(ConsoleSpinnerOptions? options = null) + { + options ??= ConsoleSpinnerOptions.Default; + var tcs = new TaskCompletionSource(); + return new ConsoleSpinner(tcs.Task, options); + } + + public void Dispose() + { + _cts.Cancel(); + _cts.Dispose(); + } + + private async Task SpinnerLoop() + { + try + { + while (!_cts.IsCancellationRequested && !_observedTask.IsCompleted) + { + await ShowFrameAsync(); + await Task.Delay(_options.Interval, _cts.Token); + } + } + catch (OperationCanceledException) + { + // Ignore + } + finally + { + await CleanupAndFinishAsync(); + } + } + + private async Task ShowFrameAsync() + { + // Clear previous content if any + if (_lastTextLength > 0) + { + await ClearTextAsync(_lastTextLength); + } + + // Write new frame + var frameChar = _animation[_frame++ % _animation.Length]; + var text = string.IsNullOrEmpty(_options.RunningMessage) + ? frameChar + : $"{frameChar} {_options.RunningMessage}"; + + await _options.Writer.WriteAsync(text); + await _options.Writer.FlushAsync(); + _lastTextLength = text.Length; + } + + private async Task CleanupAndFinishAsync() + { + // Clear spinner content + if (_lastTextLength > 0) + { + await ClearTextAsync(_lastTextLength); + } + + // Show final message if needed + var finalMessage = GetFinalMessage(); + if (!string.IsNullOrEmpty(finalMessage)) + { + await _options.Writer.WriteLineAsync(finalMessage); + } + + await _options.Writer.FlushAsync(); + Console.CursorVisible = _origCursorVisibility; + _cleanupCompleted.SetResult(true); + } + + private async Task ClearTextAsync(int length) + { + // Use backspaces to go back, spaces to clear, backspaces to return to start + await _options.Writer.WriteAsync(new string('\b', length)); + await _options.Writer.WriteAsync(new string(' ', length)); + await _options.Writer.WriteAsync(new string('\b', length)); + } + + private string? GetFinalMessage() + { + return _observedTask.IsCompleted + ? _observedTask.IsFaulted || _observedTask.IsCanceled ? _options.FailedMessage : _options.CompletedMessage + : null; + } +} \ No newline at end of file From be15b41bb618ee83417316e75ac65424972d37fa Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 17 Aug 2025 21:20:22 +0200 Subject: [PATCH 29/61] update sub --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index dacdd20..dbcbcd3 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit dacdd2091baa07f8df2935b3e363214b28ecc003 +Subproject commit dbcbcd31cf72868520150dc6b85134caa964e2a6 From cf536dd5aede987ef4790b63e9102c9329be8638 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Wed, 20 Aug 2025 20:24:53 +0200 Subject: [PATCH 30/61] update module --- modules/ModdingToolBase | 2 +- .../Updates/ModVerifyUpdater.cs | 20 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index dbcbcd3..8010e85 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit dbcbcd31cf72868520150dc6b85134caa964e2a6 +Subproject commit 8010e8588b1e5c976ab2f5e2b74320cc49cafae7 diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index 1e0be31..241d457 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using AET.ModVerify.App.Updates.SelfUpdate; using AET.ModVerify.App.Utilities; -using Vanara.PInvoke; namespace AET.ModVerify.App.Updates; @@ -52,27 +51,32 @@ private async Task UpdateApplication(ApplicationUpdateOptions updateOptions, Mod var actualBranchName = updater.GetBranchNameFromRegistry(updateOptions.BranchName, false); var branch = updater.CreateBranch(actualBranchName, updateOptions.ManifestUrl); - using (var block = ConsoleUtilities.CreateFixedHorizontalLineBlock('=', 40, + using (var block = ConsoleUtilities.CreateHorizontalFrame('=', 40, startWithNewLine: true, newLineAtEnd: true)) { block.WriteLine("This is inside the block."); block.WriteLine("The bottom line will move down as you write more lines."); - // Simulate long-running output - + var b = ConsoleUtilities.UserYesNoQuestion("a", frame: block); + block.WriteLine(b.ToString()); for (var i = 0; i < 3; i++) { await Task.Delay(500); - await block.Writer.WriteAsync(i.ToString()); + block.Write(i.ToString()); } - block.WriteLine(); + + block.Write("YourInput:"); + var a = block.ReadLine(); + + block.WriteLine(a); + for (var i = 0; i < 3; i++) { await Task.Delay(500); - block.WriteLine(i.ToString()); + await block.Writer.WriteLineAsync(i.ToString()); } var spinnerOptions = new ConsoleSpinnerOptions @@ -91,7 +95,7 @@ await ConsoleSpinner.Run(async () => block.WriteLine("456"); } - Console.WriteLine(123); + //Console.WriteLine(123); } private async Task CheckForUpdateAndReport() From de3590fa1913a5b0cfaf036b647e52ce73328935 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 25 Aug 2025 11:23:02 +0200 Subject: [PATCH 31/61] update logging --- .../GameFinder/GameFinderService.cs | 6 ++-- .../ModSelectors/AutomaticModSelector.cs | 4 +-- src/ModVerify.CliApp/ModVerifyApplication.cs | 33 +++++++++++++------ src/ModVerify.CliApp/Program.cs | 4 +-- .../Reporting/BaselineFactory.cs | 2 +- .../Reporting/BaselineSelector.cs | 4 +-- .../Updates/ModVerifyUpdater.cs | 4 +-- .../EmbeddedBaselineTest.cs | 6 ++-- 8 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs index 82a0ecd..0e3f6f0 100644 --- a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs +++ b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs @@ -79,7 +79,7 @@ private bool TryDetectGame(GameType gameType, IList detectors, ou catch (Exception e) { result = GameDetectionResult.NotInstalled(gameType); - _logger?.LogTrace($"Unable to find game installation: {e.Message}"); + _logger?.LogTrace("Unable to find game installation: {EMessage}", e.Message); return false; } } @@ -98,7 +98,7 @@ private GameFinderResult FindGames(IList detectors) throw new GameNotFoundException("Unable to find game installation: Wrong install path?"); _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, - $"Found game installation: {result.GameIdentity} at {result.GameLocation.FullName}"); + "Found game installation: {ResultGameIdentity} at {GameLocationFullName}", result.GameIdentity, result.GameLocation.FullName); var game = _gameFactory.CreateGame(result, CultureInfo.InvariantCulture); @@ -120,7 +120,7 @@ private GameFinderResult FindGames(IList detectors) throw new GameNotFoundException("Unable to find fallback game installation: Wrong install path?"); _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, - $"Found fallback game installation: {fallbackResult.GameIdentity} at {fallbackResult.GameLocation.FullName}"); + "Found fallback game installation: {FallbackResultGameIdentity} at {GameLocationFullName}", fallbackResult.GameIdentity, fallbackResult.GameLocation.FullName); fallbackGame = _gameFactory.CreateGame(fallbackResult, CultureInfo.InvariantCulture); diff --git a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs index 1137263..717db7b 100644 --- a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs @@ -38,7 +38,7 @@ internal class AutomaticModSelector(IServiceProvider serviceProvider) : ModSelec } catch (GameNotFoundException) { - Logger?.LogError(ModVerifyConstants.ConsoleEventId, $"Unable to find games based of the given location '{settings.GamePath}'. Consider specifying all paths manually."); + Logger?.LogError(ModVerifyConstants.ConsoleEventId, "Unable to find games based of the given location '{SettingsGamePath}'. Consider specifying all paths manually.", settings.GamePath); targetObject = null!; return null; } @@ -60,7 +60,7 @@ internal class AutomaticModSelector(IServiceProvider serviceProvider) : ModSelec if (!settings.EngineType.HasValue) throw new ArgumentException("Unable to determine game type. Use --type argument to set the game type."); - Logger?.LogDebug($"The requested mod at '{pathToVerify}' is detached from its games."); + Logger?.LogDebug("The requested mod at '{PathToVerify}' is detached from its games.", pathToVerify); // The path is a detached mod, that exists on a different location than the game. var result = GetDetachedModLocations(pathToVerify, finderResult, settings, out var mod); diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index bb013e5..bea33fc 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -17,6 +17,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using AET.ModVerify.App.GameFinder; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace AET.ModVerify.App; @@ -25,6 +26,7 @@ internal sealed class ModVerifyApplication(ModVerifyAppSettings settings, IServi { private readonly ILogger? _logger = services.GetService()?.CreateLogger(typeof(ModVerifyApplication)); private readonly IFileSystem _fileSystem = services.GetRequiredService(); + private readonly ModVerifyAppEnvironment _appEnvironment = services.GetRequiredService(); public async Task Run() { @@ -35,7 +37,7 @@ public async Task Run() private async Task RunCore() { - _logger?.LogDebug($"Raw command line: {Environment.CommandLine}"); + _logger?.LogDebug("Raw command line: {CommandLine}", Environment.CommandLine); var interactive = settings.Interactive; try @@ -68,13 +70,24 @@ private async Task RunCore() private async Task RunVerify() { - var installData = new SettingsBasedModSelector(services) - .CreateInstallationDataFromSettings(settings.GameInstallationsSettings); + VerifyInstallationData installData; + try + { + installData = new SettingsBasedModSelector(services) + .CreateInstallationDataFromSettings(settings.GameInstallationsSettings); + } + catch (GameNotFoundException ex) + { + ConsoleUtilities.WriteApplicationFatalError(_appEnvironment.ApplicationName, + "Unable to find an installation of Empire at War or Forces of Corruption."); + _logger?.LogError(ex, "Game not found: {Message}", ex.Message); + return ex.HResult; + } var reportSettings = CreateGlobalReportSettings(installData); - _logger?.LogDebug($"Verify install data: {installData}"); - _logger?.LogTrace($"Verify settings: {settings}"); + _logger?.LogDebug("Verify install data: {VerifyInstallationData}", installData); + _logger?.LogTrace("Verify settings: {ModVerifyAppSettings}", settings); var allErrors = await Verify(installData, reportSettings) .ConfigureAwait(false); @@ -113,7 +126,7 @@ private async Task> Verify( try { - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Creating Game Engine '{installData.EngineType}'"); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Creating Game Engine '{InstallDataEngineType}'", installData.EngineType); gameEngine = await gameEngineService.InitializeAsync( installData.EngineType, installData.GameLocations, @@ -130,7 +143,7 @@ private async Task> Verify( } catch (Exception e) { - _logger?.LogError(e, $"Creating game engine failed: {e.Message}"); + _logger?.LogError(e, "Creating game engine failed: {EMessage}", e.Message); throw; } @@ -148,7 +161,7 @@ private async Task> Verify( { try { - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Verifying '{installData.Name}'..."); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Verifying '{InstallDataName}'...", installData.Name); await verifyPipeline.RunAsync().ConfigureAwait(false); progressReporter.Report(string.Empty, 1.0); } @@ -168,7 +181,7 @@ private async Task> Verify( } catch (Exception e) { - _logger?.LogError(e, $"Verification failed: {e.Message}"); + _logger?.LogError(e, "Verification failed: {EMessage}", e.Message); throw; } @@ -196,7 +209,7 @@ private async Task WriteBaseline( var baseline = new VerificationBaseline(reportSettings.MinimumReportSeverity, errors); var fullPath = _fileSystem.Path.GetFullPath(baselineFile); - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Writing Baseline to '{fullPath}'"); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Writing Baseline to '{FullPath}'", fullPath); #if NET await diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index c7cc6ec..f04ed05 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -79,7 +79,7 @@ protected override async Task InitializeAppAsync(IReadOnlyList args } catch (Exception e) { - Logger?.LogCritical(e, $"Failed to create settings form commandline arguments: {e.Message}"); + Logger?.LogCritical(e, "Failed to create settings form commandline arguments: {EMessage}", e.Message); ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, e); return e.HResult; } @@ -168,7 +168,7 @@ protected override async Task RunAppAsync(string[] args, IServiceProvider a } catch (Exception e) { - Logger?.LogCritical(e, $"Failed to create settings form commandline arguments: {e.Message}"); + Logger?.LogCritical(e, "Failed to create settings form commandline arguments: {EMessage}", e.Message); ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, e); return e.HResult; } diff --git a/src/ModVerify.CliApp/Reporting/BaselineFactory.cs b/src/ModVerify.CliApp/Reporting/BaselineFactory.cs index ebab6d1..b5a389b 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineFactory.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineFactory.cs @@ -24,7 +24,7 @@ public bool TryCreateBaseline( if (!_fileSystem.Directory.Exists(directory)) return false; - _logger?.LogDebug(ModVerifyConstants.ConsoleEventId, $"Searching for baseline file at '{directory}'"); + _logger?.LogDebug(ModVerifyConstants.ConsoleEventId, "Searching for baseline file at '{Directory}'", directory); var jsonFiles = _fileSystem.Directory.EnumerateFiles( directory, diff --git a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs index 816d182..b08a1a5 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs @@ -120,10 +120,10 @@ private VerificationBaseline FindBaselineNonInteractive(VerifyInstallationData i var targetPath = installationData.GameLocations.TargetPath; if (_baselineFactory.TryCreateBaseline(targetPath, out var baseline, out var path)) { - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Automatically applying local baseline file '{path}'."); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Automatically applying local baseline file '{Path}'.", path); return baseline; } - _logger?.LogTrace($"No baseline file found in taget path '{targetPath}'."); + _logger?.LogTrace("No baseline file found in taget path '{TargetPath}'.", targetPath); return VerificationBaseline.Empty; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index 5c2b090..1d13198 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -95,11 +95,11 @@ public async Task CheckForUpdateAsync() if (SemVersion.ComparePrecedence(currentVersion, latestVersion) >= 0) { - _logger?.LogDebug($"No update available - [Current Version = {currentVersion}], [Available Version = {latestVersion}]"); + _logger?.LogDebug("No update available - [Current Version = {CurrentVersion}], [Available Version = {LatestVersion}]", currentVersion, latestVersion); return default; } - _logger?.LogDebug($"Update available - [Current Version = {currentVersion}], [Available Version = {latestVersion}]"); + _logger?.LogDebug("Update available - [Current Version = {CurrentVersion}], [Available Version = {LatestVersion}]", currentVersion, latestVersion); return new UpdateInfo { DownloadLink = ModVerifyUpdaterInformation.ModVerifyReleasesDownloadLink, diff --git a/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs index 4779f22..db119dc 100644 --- a/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs +++ b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs @@ -12,7 +12,7 @@ namespace ModVerify.CliApp.Test; -public class EmbeddedBaselineTest +public class BaselineSelectorTest { private static readonly IFileSystem FileSystem = new RealFileSystem(); private static readonly ModVerifyAppSettings TestSettings = new() @@ -28,7 +28,7 @@ public class EmbeddedBaselineTest private readonly IServiceProvider _serviceProvider; - public EmbeddedBaselineTest() + public BaselineSelectorTest() { var sc = new ServiceCollection(); sc.AddSingleton(FileSystem); @@ -39,7 +39,7 @@ public EmbeddedBaselineTest() [Theory] [InlineData(GameEngineType.Eaw)] [InlineData(GameEngineType.Foc)] - public void Foo(GameEngineType engineType) + public void LoadEmbeddedBaseline(GameEngineType engineType) { // Ensure this operation does not crash, meaning the embedded baseline is at least compatible. new BaselineSelector(TestSettings, _serviceProvider).LoadEmbeddedBaseline(engineType); From b3b513cac80c546e5edb2268b3d679020cd1c00a Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 25 Aug 2025 12:30:09 +0200 Subject: [PATCH 32/61] update --- src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index 241d457..7f9e1d6 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -51,7 +51,7 @@ private async Task UpdateApplication(ApplicationUpdateOptions updateOptions, Mod var actualBranchName = updater.GetBranchNameFromRegistry(updateOptions.BranchName, false); var branch = updater.CreateBranch(actualBranchName, updateOptions.ManifestUrl); - using (var block = ConsoleUtilities.CreateHorizontalFrame('=', 40, + using (var block = ConsoleUtilities.CreateHorizontalFrame(length: 40, startWithNewLine: true, newLineAtEnd: true)) { From 37dbd85896f7a5b549d79f78f19c2e9b598de58e Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 25 Aug 2025 16:49:40 +0200 Subject: [PATCH 33/61] update deps --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 10 +++++----- src/ModVerify/ModVerify.csproj | 4 ++-- .../ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index ba57213..7cd1812 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -37,11 +37,11 @@ - - - - - + + + + + diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index 92c7adf..a77bcef 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -35,8 +35,8 @@ - - + + diff --git a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj index 7b9e6d6..78ddfac 100644 --- a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj +++ b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj @@ -14,9 +14,9 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 83f552e6cb0207e5aea5f90ba61b73bfeb3d1ca3 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 25 Aug 2025 16:49:53 +0200 Subject: [PATCH 34/61] update sub --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 8010e85..55e73b2 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 8010e8588b1e5c976ab2f5e2b74320cc49cafae7 +Subproject commit 55e73b28f760b1aab5dfc7900e83e6b566eb4d41 From a7b6d70afaf6fed8889c3807e3f541cb4dcf539a Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 26 Aug 2025 12:20:51 +0200 Subject: [PATCH 35/61] start update impl --- .../Updates/ModVerifyUpdater.cs | 55 ++++++++++++++++--- .../Updates/SelfUpdate/AssemblyInfo.cs | 6 ++ .../SelfUpdate/ModVerifyApplicationUpdater.cs | 20 +++---- src/ModVerify.CliApp/Utilities/Spinner.cs | 52 ++++++++++++------ 4 files changed, 96 insertions(+), 37 deletions(-) create mode 100644 src/ModVerify.CliApp/Updates/SelfUpdate/AssemblyInfo.cs diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index 7f9e1d6..fc4eb31 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -1,12 +1,12 @@ using AET.ModVerify.App.Updates.Github; +using AET.ModVerify.App.Updates.SelfUpdate; +using AET.ModVerify.App.Utilities; using AnakinRaW.ApplicationBase; using AnakinRaW.ApplicationBase.Update.Options; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; using System.Threading.Tasks; -using AET.ModVerify.App.Updates.SelfUpdate; -using AET.ModVerify.App.Utilities; namespace AET.ModVerify.App.Updates; @@ -46,15 +46,17 @@ private async Task UpdateApplication(ApplicationUpdateOptions updateOptions, Mod return; } - var updater = new ModVerifyApplicationUpdater(mode, updatableEnvironment, _serviceProvider); + var updater = new ModVerifyApplicationUpdater(updatableEnvironment, _serviceProvider); var actualBranchName = updater.GetBranchNameFromRegistry(updateOptions.BranchName, false); var branch = updater.CreateBranch(actualBranchName, updateOptions.ManifestUrl); - + using (var block = ConsoleUtilities.CreateHorizontalFrame(length: 40, startWithNewLine: true, newLineAtEnd: true)) { + + block.WriteLine("This is inside the block."); block.WriteLine("The bottom line will move down as you write more lines."); @@ -66,7 +68,7 @@ private async Task UpdateApplication(ApplicationUpdateOptions updateOptions, Mod block.Write(i.ToString()); } - + block.Write("YourInput:"); var a = block.ReadLine(); @@ -92,10 +94,47 @@ await ConsoleSpinner.Run(async () => await Task.Delay(2000); // Simulate some work }, spinnerOptions); - block.WriteLine("456"); - } - //Console.WriteLine(123); + //var currentAction = "checking for update"; + //try + //{ + + // var updateCheckSpinner = new ConsoleSpinnerOptions + // { + // Writer = block.Writer, + // CompletedMessage = "Update check completed.", + // RunningMessage = "Checking for update...", + // FailedMessage = "Update check failed", + // HideCursor = true + // }; + + // var updateCatalog = + // await ConsoleSpinner.Run(async () => await updater.CheckForUpdateAsync(branch, CancellationToken.None), + // updateCheckSpinner); + + + // if (updateCatalog is null || updateCatalog.Action != UpdateCatalogAction.Update) + // return; + + + // if (mode == ModVerifyUpdateMode.InteractiveUpdate) + // { + + // } + + // currentAction = "updating"; + // await updater.UpdateAsync(updateCatalog); + //} + //catch (Exception e) + //{ + // block.WriteLine("TEST"); + // Console.ForegroundColor = ConsoleColor.DarkRed; + // block.WriteLine($"Error while {currentAction}: {e.Message}"); + // _logger?.LogError(e, "Unable to check for updates: {error}", e.Message); + // Console.ResetColor(); + // block.WriteLine(); + //} + } } private async Task CheckForUpdateAndReport() diff --git a/src/ModVerify.CliApp/Updates/SelfUpdate/AssemblyInfo.cs b/src/ModVerify.CliApp/Updates/SelfUpdate/AssemblyInfo.cs new file mode 100644 index 0000000..4b94b5e --- /dev/null +++ b/src/ModVerify.CliApp/Updates/SelfUpdate/AssemblyInfo.cs @@ -0,0 +1,6 @@ +#if NETFRAMEWORK +using AnakinRaW.AppUpdaterFramework.Attributes; + +[assembly: UpdateProduct("AET ModVerify")] +[assembly: UpdateComponent("AET.ModVerify.Exe", Name = "AET ModVerify")] +#endif \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs b/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs index df03aaa..c96fa5c 100644 --- a/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs +++ b/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs @@ -9,21 +9,15 @@ namespace AET.ModVerify.App.Updates.SelfUpdate; -internal class ModVerifyApplicationUpdater : ApplicationUpdater +internal class ModVerifyApplicationUpdater( + UpdatableApplicationEnvironment environment, + IServiceProvider serviceProvider) + : ApplicationUpdater(environment, serviceProvider) { - public ModVerifyApplicationUpdater( - ModVerifyUpdateMode updateMode, - UpdatableApplicationEnvironment environment, - IServiceProvider serviceProvider) - : base(environment, serviceProvider) + public override async Task CheckForUpdateAsync(ProductBranch branch, CancellationToken token = default) { - if (updateMode == ModVerifyUpdateMode.CheckOnly) - throw new ArgumentException("Check-only mode is not supported by this updater.", nameof(updateMode)); - } - - public override Task CheckForUpdateAsync(ProductBranch branch, CancellationToken token = default) - { - throw new NotImplementedException(); + await Task.Delay(2000, token); + throw new Exception("Test"); } public override Task UpdateAsync(UpdateCatalog updateCatalog, CancellationToken token = default) diff --git a/src/ModVerify.CliApp/Utilities/Spinner.cs b/src/ModVerify.CliApp/Utilities/Spinner.cs index e9e4442..f25edda 100644 --- a/src/ModVerify.CliApp/Utilities/Spinner.cs +++ b/src/ModVerify.CliApp/Utilities/Spinner.cs @@ -24,12 +24,11 @@ public sealed class ConsoleSpinnerOptions -internal sealed class ConsoleSpinner : IDisposable +internal sealed class ConsoleSpinner : IAsyncDisposable { private readonly ConsoleSpinnerOptions _options; private readonly CancellationTokenSource _cts = new(); private readonly Task _observedTask; - private readonly TaskCompletionSource _cleanupCompleted = new(); private readonly bool _origCursorVisibility; private readonly string[] _animation; private int _frame; @@ -48,12 +47,19 @@ private ConsoleSpinner(Task observedTask, ConsoleSpinnerOptions options) SpinnerLoop().Forget(); } + public static async Task Run(Task task, ConsoleSpinnerOptions? options = null) + { + options ??= ConsoleSpinnerOptions.Default; + await using var spinner = new ConsoleSpinner(task, options); + var result = await task.ConfigureAwait(false); + return result; + } + public static async Task Run(Task task, ConsoleSpinnerOptions? options = null) { options ??= ConsoleSpinnerOptions.Default; - using var spinner = new ConsoleSpinner(task, options); + await using var spinner = new ConsoleSpinner(task, options); await task.ConfigureAwait(false); - await spinner._cleanupCompleted.Task.ConfigureAwait(false); } public static Task Run(Func asyncAction, ConsoleSpinnerOptions? options = null) @@ -63,6 +69,13 @@ public static Task Run(Func asyncAction, ConsoleSpinnerOptions? options = return Run(asyncAction(), options); } + public static Task Run(Func> asyncAction, ConsoleSpinnerOptions? options = null) + { + if (asyncAction is null) + throw new ArgumentNullException(nameof(asyncAction)); + return Run(asyncAction(), options); + } + public static ConsoleSpinner Endless(ConsoleSpinnerOptions? options = null) { options ??= ConsoleSpinnerOptions.Default; @@ -70,12 +83,6 @@ public static ConsoleSpinner Endless(ConsoleSpinnerOptions? options = null) return new ConsoleSpinner(tcs.Task, options); } - public void Dispose() - { - _cts.Cancel(); - _cts.Dispose(); - } - private async Task SpinnerLoop() { try @@ -90,10 +97,6 @@ private async Task SpinnerLoop() { // Ignore } - finally - { - await CleanupAndFinishAsync(); - } } private async Task ShowFrameAsync() @@ -115,7 +118,7 @@ private async Task ShowFrameAsync() _lastTextLength = text.Length; } - private async Task CleanupAndFinishAsync() + public async Task CleanupAndFinishAsync() { // Clear spinner content if (_lastTextLength > 0) @@ -132,7 +135,6 @@ private async Task CleanupAndFinishAsync() await _options.Writer.FlushAsync(); Console.CursorVisible = _origCursorVisibility; - _cleanupCompleted.SetResult(true); } private async Task ClearTextAsync(int length) @@ -149,4 +151,22 @@ private async Task ClearTextAsync(int length) ? _observedTask.IsFaulted || _observedTask.IsCanceled ? _options.FailedMessage : _options.CompletedMessage : null; } + + public async ValueTask DisposeAsync() + { + await CleanupAndFinishAsync(); + + await CastAndDispose(_cts); + await CastAndDispose(_observedTask); + + return; + + static async ValueTask CastAndDispose(IDisposable resource) + { + if (resource is IAsyncDisposable resourceAsyncDisposable) + await resourceAsyncDisposable.DisposeAsync(); + else + resource.Dispose(); + } + } } \ No newline at end of file From 4305692c70729002f7abbf3cbb9701752f09d356 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 30 Aug 2025 09:48:43 +0200 Subject: [PATCH 36/61] horizontal frame supports native console --- .../Updates/ModVerifyUpdater.cs | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index fc4eb31..6c418e5 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -50,49 +50,61 @@ private async Task UpdateApplication(ApplicationUpdateOptions updateOptions, Mod var actualBranchName = updater.GetBranchNameFromRegistry(updateOptions.BranchName, false); var branch = updater.CreateBranch(actualBranchName, updateOptions.ManifestUrl); + + { + var b = ConsoleUtilities.UserYesNoQuestion("a"); + Console.WriteLine(b.ToString()); + } - using (var block = ConsoleUtilities.CreateHorizontalFrame(length: 40, + using (ConsoleUtilities.CreateHorizontalFrame(length: 40, startWithNewLine: true, newLineAtEnd: true)) { + Console.WriteLine("This is inside the block."); + Console.WriteLine("The bottom line will move down as you write more lines."); - - block.WriteLine("This is inside the block."); - block.WriteLine("The bottom line will move down as you write more lines."); - - var b = ConsoleUtilities.UserYesNoQuestion("a", frame: block); - block.WriteLine(b.ToString()); + + var b = ConsoleUtilities.UserYesNoQuestion("a"); + Console.WriteLine(b.ToString()); for (var i = 0; i < 3; i++) { await Task.Delay(500); - block.Write(i.ToString()); + Console.Write(i.ToString()); } - block.Write("YourInput:"); - var a = block.ReadLine(); + Console.Write("YourInput:"); + var a = Console.ReadLine(); - block.WriteLine(a); + Console.WriteLine(a); for (var i = 0; i < 3; i++) { await Task.Delay(500); - await block.Writer.WriteLineAsync(i.ToString()); + await Console.Out.WriteLineAsync(i.ToString()); } var spinnerOptions = new ConsoleSpinnerOptions { - Writer = block.Writer, CompletedMessage = "DONE", RunningMessage = "Checking for update...", FailedMessage = "Update check failed", HideCursor = true }; - await ConsoleSpinner.Run(async () => + + try { - await Task.Delay(2000); // Simulate some work - }, spinnerOptions); + await ConsoleSpinner.Run(async () => + { + await Task.Delay(2000); // Simulate some work + throw new Exception("Test"); + }, spinnerOptions); + } + catch (Exception e) + { + _logger?.LogError(e, e.Message); + } //var currentAction = "checking for update"; @@ -107,19 +119,19 @@ await ConsoleSpinner.Run(async () => // FailedMessage = "Update check failed", // HideCursor = true // }; - + // var updateCatalog = // await ConsoleSpinner.Run(async () => await updater.CheckForUpdateAsync(branch, CancellationToken.None), // updateCheckSpinner); - + // if (updateCatalog is null || updateCatalog.Action != UpdateCatalogAction.Update) // return; - - + + // if (mode == ModVerifyUpdateMode.InteractiveUpdate) // { - + // } // currentAction = "updating"; From 6a90e18664d550ce34001f3a96f67c78054d67ea Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 30 Aug 2025 09:48:50 +0200 Subject: [PATCH 37/61] update sub --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 55e73b2..b3e9b82 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 55e73b28f760b1aab5dfc7900e83e6b566eb4d41 +Subproject commit b3e9b82897f99278c014b9e87710dace949857d0 From 1bedc5630a235366d50ceea6342cab9ca79a087d Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 1 Sep 2025 09:28:50 +0200 Subject: [PATCH 38/61] update sub --- .../ModVerifyAppEnvironment.cs | 17 ++- .../Updates/ModVerifyUpdater.cs | 118 ++++++------------ .../SelfUpdate/ModVerifyApplicationUpdater.cs | 12 +- 3 files changed, 62 insertions(+), 85 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs b/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs index ff1758a..86bfc40 100644 --- a/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs +++ b/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs @@ -25,6 +25,9 @@ internal sealed class ModVerifyAppEnvironment(Assembly assembly, IFileSystem fil public override ICollection UpdateMirrors { get; } = new List { +#if DEBUG + new("C:\\Test\\ModVerify"), +#endif new($"https://republicatwar.com/downloads/{ModVerifyConstants.ModVerifyToolPath}") }; @@ -37,12 +40,24 @@ protected override UpdateConfiguration CreateUpdateConfiguration() DownloadLocation = FileSystem.Path.Combine(ApplicationLocalPath, "downloads"), BackupLocation = FileSystem.Path.Combine(ApplicationLocalPath, "backups"), BackupPolicy = BackupPolicy.Required, - DownloadConfiguration = new DownloadManagerConfiguration + ComponentDownloadConfiguration = new DownloadManagerConfiguration { AllowEmptyFileDownload = false, DownloadRetryDelay = 500, ValidationPolicy = ValidationPolicy.Required }, + ManifestDownloadConfiguration = new DownloadManagerConfiguration + { + AllowEmptyFileDownload = false, + DownloadRetryDelay = 500, + ValidationPolicy = ValidationPolicy.Optional + }, + BranchDownloadConfiguration = new DownloadManagerConfiguration + { + AllowEmptyFileDownload = false, + DownloadRetryDelay = 500, + ValidationPolicy = ValidationPolicy.NoValidation + }, DownloadRetryCount = 3, RestartConfiguration = new UpdateRestartConfiguration { diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index 6c418e5..aef8e4b 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -6,7 +6,9 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; +using System.Threading; using System.Threading.Tasks; +using AnakinRaW.AppUpdaterFramework.Metadata.Update; namespace AET.ModVerify.App.Updates; @@ -51,101 +53,53 @@ private async Task UpdateApplication(ApplicationUpdateOptions updateOptions, Mod var actualBranchName = updater.GetBranchNameFromRegistry(updateOptions.BranchName, false); var branch = updater.CreateBranch(actualBranchName, updateOptions.ManifestUrl); + using (ConsoleUtilities.CreateHorizontalFrame(length: 40, startWithNewLine: true, newLineAtEnd: true)) { - var b = ConsoleUtilities.UserYesNoQuestion("a"); - Console.WriteLine(b.ToString()); - } - - using (ConsoleUtilities.CreateHorizontalFrame(length: 40, - startWithNewLine: true, - newLineAtEnd: true)) - { - Console.WriteLine("This is inside the block."); - Console.WriteLine("The bottom line will move down as you write more lines."); - - - var b = ConsoleUtilities.UserYesNoQuestion("a"); - Console.WriteLine(b.ToString()); - for (var i = 0; i < 3; i++) + var currentAction = "checking for update"; + try { - await Task.Delay(500); - Console.Write(i.ToString()); - } - - - Console.Write("YourInput:"); - var a = Console.ReadLine(); + var updateCheckSpinner = new ConsoleSpinnerOptions + { + CompletedMessage = "Update check completed.", + RunningMessage = "Checking for update...", + FailedMessage = "Update check failed", + HideCursor = true + }; - Console.WriteLine(a); + var updateCatalog = + await ConsoleSpinner.Run(async () => + await updater.CheckForUpdateAsync(branch, CancellationToken.None), + updateCheckSpinner); - for (var i = 0; i < 3; i++) - { - await Task.Delay(500); - await Console.Out.WriteLineAsync(i.ToString()); - } + if (updateCatalog.Action != UpdateCatalogAction.Update) + { + Console.WriteLine("No update available."); + return; + } - var spinnerOptions = new ConsoleSpinnerOptions - { - CompletedMessage = "DONE", - RunningMessage = "Checking for update...", - FailedMessage = "Update check failed", - HideCursor = true - }; + Console.ForegroundColor = ConsoleColor.DarkGreen; + Console.WriteLine($"New update available: Version {updateCatalog.UpdateReference.Version}"); + Console.ResetColor(); - try - { - await ConsoleSpinner.Run(async () => + if (mode == ModVerifyUpdateMode.InteractiveUpdate) { - await Task.Delay(2000); // Simulate some work - throw new Exception("Test"); - }, spinnerOptions); + var shallUpdate = ConsoleUtilities.UserYesNoQuestion("Do you want to update now?"); + if (!shallUpdate) + return; + } + + currentAction = "updating"; + await updater.UpdateAsync(updateCatalog); } catch (Exception e) { + Console.WriteLine(); + Console.ForegroundColor = ConsoleColor.DarkRed; + Console.WriteLine($"Error while {currentAction}: {e.Message}"); + Console.ResetColor(); _logger?.LogError(e, e.Message); } - - - //var currentAction = "checking for update"; - //try - //{ - - // var updateCheckSpinner = new ConsoleSpinnerOptions - // { - // Writer = block.Writer, - // CompletedMessage = "Update check completed.", - // RunningMessage = "Checking for update...", - // FailedMessage = "Update check failed", - // HideCursor = true - // }; - - // var updateCatalog = - // await ConsoleSpinner.Run(async () => await updater.CheckForUpdateAsync(branch, CancellationToken.None), - // updateCheckSpinner); - - - // if (updateCatalog is null || updateCatalog.Action != UpdateCatalogAction.Update) - // return; - - - // if (mode == ModVerifyUpdateMode.InteractiveUpdate) - // { - - // } - - // currentAction = "updating"; - // await updater.UpdateAsync(updateCatalog); - //} - //catch (Exception e) - //{ - // block.WriteLine("TEST"); - // Console.ForegroundColor = ConsoleColor.DarkRed; - // block.WriteLine($"Error while {currentAction}: {e.Message}"); - // _logger?.LogError(e, "Unable to check for updates: {error}", e.Message); - // Console.ResetColor(); - // block.WriteLine(); - //} } } diff --git a/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs b/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs index c96fa5c..8ff92be 100644 --- a/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs +++ b/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs @@ -16,8 +16,16 @@ internal class ModVerifyApplicationUpdater( { public override async Task CheckForUpdateAsync(ProductBranch branch, CancellationToken token = default) { - await Task.Delay(2000, token); - throw new Exception("Test"); + var updateReference = ProductService.CreateProductReference(null, branch); + + var updateCatalog = await UpdateService.CheckForUpdatesAsync(updateReference, token); + + if (updateCatalog is null) + throw new InvalidOperationException("Update service was already doing something."); + + return updateCatalog.Action is UpdateCatalogAction.Install or UpdateCatalogAction.Uninstall + ? throw new NotSupportedException("Install and Uninstall operations are not supported") + : updateCatalog; } public override Task UpdateAsync(UpdateCatalog updateCatalog, CancellationToken token = default) From fdad3f2477c5619cd3d132f3bc1505cfd67a68a5 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 1 Sep 2025 09:28:57 +0200 Subject: [PATCH 39/61] update sub --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index b3e9b82..93f53b0 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit b3e9b82897f99278c014b9e87710dace949857d0 +Subproject commit 93f53b054fa0afcaab9dae756aaa8aa8ce826862 From 7d126129054578d18bdf03af722fe701aad25850 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Wed, 1 Oct 2025 14:54:37 +0200 Subject: [PATCH 40/61] git pull --- .../Updates/ModVerifyUpdater.cs | 11 +++++++- .../SelfUpdate/ModVerifyApplicationUpdater.cs | 9 ++++-- .../ModVerifyUpdateResultHandler.cs | 28 +++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyUpdateResultHandler.cs diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index aef8e4b..935641a 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -90,7 +90,16 @@ await updater.CheckForUpdateAsync(branch, CancellationToken.None), } currentAction = "updating"; - await updater.UpdateAsync(updateCatalog); + + + var updatingSpinner = new ConsoleSpinnerOptions + { + RunningMessage = $"Updating {ModVerifyConstants.AppNameString}...", + HideCursor = true + }; + await ConsoleSpinner.Run(async () => + await updater.UpdateAsync(updateCatalog, CancellationToken.None), + updatingSpinner); } catch (Exception e) { diff --git a/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs b/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs index 8ff92be..9b97043 100644 --- a/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs +++ b/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyApplicationUpdater.cs @@ -28,8 +28,13 @@ public override async Task CheckForUpdateAsync(ProductBranch bran : updateCatalog; } - public override Task UpdateAsync(UpdateCatalog updateCatalog, CancellationToken token = default) + public override async Task UpdateAsync(UpdateCatalog updateCatalog, CancellationToken token = default) { - throw new NotImplementedException(); + var updateResult = await UpdateService.UpdateAsync(updateCatalog, token).ConfigureAwait(false); + if (updateResult is null) + throw new InvalidOperationException("There is already an update running."); + + var resultHandler = new ModVerifyUpdateResultHandler(Environment, ServiceProvider); + await resultHandler.Handle(updateResult).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyUpdateResultHandler.cs b/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyUpdateResultHandler.cs new file mode 100644 index 0000000..42ab413 --- /dev/null +++ b/src/ModVerify.CliApp/Updates/SelfUpdate/ModVerifyUpdateResultHandler.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading.Tasks; +using AnakinRaW.ApplicationBase.Environment; +using AnakinRaW.ApplicationBase.Update; +using AnakinRaW.AppUpdaterFramework.Handlers; +using AnakinRaW.AppUpdaterFramework.Updater; + +namespace AET.ModVerify.App.Updates.SelfUpdate; + +internal sealed class ModVerifyUpdateResultHandler( + UpdatableApplicationEnvironment applicationEnvironment, + IServiceProvider serviceProvider) + : ApplicationUpdateResultHandler(applicationEnvironment, serviceProvider) +{ + protected override Task ShowError(UpdateResult updateResult) + { + Console.WriteLine(); + Console.WriteLine($"Update failed with error: {updateResult.ErrorMessage}"); + return base.ShowError(updateResult); + } + + protected override void RestartApplication(RestartReason reason) + { + Console.WriteLine(); + Console.WriteLine("Restarting application to complete update..."); + base.RestartApplication(reason); + } +} \ No newline at end of file From 98c165a536f92cd1e18533ef1f13234a03a3f65e Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 12:49:17 +0100 Subject: [PATCH 41/61] update dependencies --- Directory.Build.props | 2 +- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 16 ++++++++-------- src/ModVerify/ModVerify.csproj | 2 +- .../PG.StarWarsGame.Engine.csproj | 4 ++-- .../ModVerify.CliApp.Test.csproj | 10 +++++----- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 1dbc929..d475e7f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -39,7 +39,7 @@ all - 3.7.115 + 3.9.50 diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 7cd1812..a27cd7f 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -1,7 +1,7 @@  - net9.0;net481 + net10.0;net481 Exe AET.ModVerify.App ModVerify @@ -34,15 +34,15 @@ - + - - - - - - + + + + + + diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index a77bcef..cd0f7b9 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -35,7 +35,7 @@ - + diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index 47f5955..3269598 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -1,6 +1,6 @@  - netstandard2.0;netstandard2.1;net9.0 + netstandard2.0;netstandard2.1;net10.0 PG.StarWarsGame.Engine PG.StarWarsGame.Engine AlamoEngineTools.PG.StarWarsGame.Engine @@ -26,7 +26,7 @@ - + diff --git a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj index 78ddfac..84d1bba 100644 --- a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj +++ b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 $(TargetFrameworks);net481 false preview @@ -12,11 +12,11 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 9b4834d8a594dd5f662f89fd963885ce2f0e4644 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 12:49:29 +0100 Subject: [PATCH 42/61] update logging --- modules/ModdingToolBase | 2 +- .../GameFinder/GameFinderService.cs | 2 +- .../Settings/SettingsBuilder.cs | 3 +- .../Updates/Github/GithubUpdateChecker.cs | 4 +- .../Updates/ModVerifyUpdater.cs | 27 +++++--- .../Utilities/ExtensionMethods.cs | 19 +++--- .../Pipeline/GameVerifierPipelineStep.cs | 4 +- .../Reporting/Json/JsonVerificationError.cs | 2 +- .../Engine/GameAssertErrorReporter.cs | 1 + .../VerificationReportersExtensions.cs | 67 +++++++++---------- .../Utilities/VerificationErrorExtensions.cs | 33 ++++----- .../PG.StarWarsGame.Engine/GameManagerBase.cs | 2 +- .../GuiDialog/GuiDialogGameManager.cs | 4 +- .../IO/Repositories/GameRepository.Files.cs | 4 +- .../IO/Repositories/GameRepository.cs | 10 +-- .../PetroglyphStarWarsGameEngineService.cs | 2 +- .../Xml/Parsers/XmlContainerContentParser.cs | 8 +-- 17 files changed, 99 insertions(+), 95 deletions(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 93f53b0..fa72788 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 93f53b054fa0afcaab9dae756aaa8aa8ce826862 +Subproject commit fa72788b1630140937053c6e3d71a011eb632b60 diff --git a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs index 0e3f6f0..a88ebd9 100644 --- a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs +++ b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs @@ -79,7 +79,7 @@ private bool TryDetectGame(GameType gameType, IList detectors, ou catch (Exception e) { result = GameDetectionResult.NotInstalled(gameType); - _logger?.LogTrace("Unable to find game installation: {EMessage}", e.Message); + _logger?.LogTrace("Unable to find game installation: {Message}", e.Message); return false; } } diff --git a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs index caf0353..3c5535f 100644 --- a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs @@ -56,8 +56,7 @@ private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) if (minFailSeverity == null) { _logger?.LogWarning(ModVerifyConstants.ConsoleEventId, - $"Verification is configured to fail fast but 'minFailSeverity' is not specified. " + - $"Using severity '{VerificationSeverity.Information}'."); + "Verification is configured to fail fast but 'minFailSeverity' is not specified. Using severity '{Info}'.", VerificationSeverity.Information); minFailSeverity = VerificationSeverity.Information; } diff --git a/src/ModVerify.CliApp/Updates/Github/GithubUpdateChecker.cs b/src/ModVerify.CliApp/Updates/Github/GithubUpdateChecker.cs index 823d907..df4d769 100644 --- a/src/ModVerify.CliApp/Updates/Github/GithubUpdateChecker.cs +++ b/src/ModVerify.CliApp/Updates/Github/GithubUpdateChecker.cs @@ -40,11 +40,11 @@ public async Task CheckForUpdateAsync() if (SemVersion.ComparePrecedence(currentVersion, latestVersion) >= 0) { - _logger?.LogDebug($"No update available - [Current Version = {currentVersion}], [Available Version = {latestVersion}]"); + _logger?.LogDebug("No update available - [Current Version = {CurrentVersion}], [Available Version = {LatestVersion}]", currentVersion, latestVersion); return default; } - _logger?.LogDebug($"Update available - [Current Version = {currentVersion}], [Available Version = {latestVersion}]"); + _logger?.LogDebug("Update available - [Current Version = {CurrentVersion}], [Available Version = {LatestVersion}]", currentVersion, latestVersion); return new GithubUpdateInfo { DownloadLink = GithubUpdateConstants.ModVerifyReleasesDownloadLink, diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs index 935641a..af7d369 100644 --- a/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdater.cs @@ -3,12 +3,12 @@ using AET.ModVerify.App.Utilities; using AnakinRaW.ApplicationBase; using AnakinRaW.ApplicationBase.Update.Options; +using AnakinRaW.AppUpdaterFramework.Metadata.Update; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; using System.Threading; using System.Threading.Tasks; -using AnakinRaW.AppUpdaterFramework.Metadata.Update; namespace AET.ModVerify.App.Updates; @@ -66,11 +66,10 @@ private async Task UpdateApplication(ApplicationUpdateOptions updateOptions, Mod HideCursor = true }; - var updateCatalog = - await ConsoleSpinner.Run(async () => - await updater.CheckForUpdateAsync(branch, CancellationToken.None), - updateCheckSpinner); - + var updateCatalog = await ConsoleSpinner.Run(async () => + await updater.CheckForUpdateAsync(branch, CancellationToken.None), + updateCheckSpinner); + if (updateCatalog.Action != UpdateCatalogAction.Update) { @@ -103,11 +102,7 @@ await updater.UpdateAsync(updateCatalog, CancellationToken.None), } catch (Exception e) { - Console.WriteLine(); - Console.ForegroundColor = ConsoleColor.DarkRed; - Console.WriteLine($"Error while {currentAction}: {e.Message}"); - Console.ResetColor(); - _logger?.LogError(e, e.Message); + WriteError(e, $"Error while {currentAction}: {e.Message}"); } } } @@ -141,4 +136,14 @@ private async Task CheckForUpdateAndReport() _logger?.LogTrace(e, "Checking for update failed: {message}", e.Message); } } + + private void WriteError(Exception e, string? customMessage) + { + Console.WriteLine(); + Console.ForegroundColor = ConsoleColor.DarkRed; + if (!string.IsNullOrEmpty(customMessage)) + Console.WriteLine(customMessage); + Console.ResetColor(); + _logger?.LogError(e, e.Message); + } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs index 8f833ee..b2e7ab0 100644 --- a/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs +++ b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs @@ -18,17 +18,18 @@ public static GameType FromEngineType(this GameEngineType type) return type == GameEngineType.Foc ? GameType.Foc : GameType.Eaw; } - public static bool IsUpdatable(this ApplicationEnvironment modVerifyEnvironment) + extension(ApplicationEnvironment modVerifyEnvironment) { - return modVerifyEnvironment.IsUpdatable(out _); - } + public bool IsUpdatable() + { + return modVerifyEnvironment.IsUpdatable(out _); + } - public static bool IsUpdatable( - this ApplicationEnvironment applicationEnvironment, - [NotNullWhen(true)] out UpdatableApplicationEnvironment? updatableEnvironment) - { - updatableEnvironment = applicationEnvironment as UpdatableApplicationEnvironment; - return updatableEnvironment is not null; + public bool IsUpdatable([NotNullWhen(true)] out UpdatableApplicationEnvironment? updatableEnvironment) + { + updatableEnvironment = modVerifyEnvironment as UpdatableApplicationEnvironment; + return updatableEnvironment is not null; + } } public static bool LaunchedWithoutArguments(this BaseModVerifyOptions options) diff --git a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs index a27ee9b..6405dbd 100644 --- a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs +++ b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs @@ -26,13 +26,13 @@ protected override void RunCore(CancellationToken token) { try { - Logger?.LogDebug($"Running verifier '{GameVerifier.FriendlyName}'..."); + Logger?.LogDebug("Running verifier '{Name}'...", GameVerifier.FriendlyName); ReportProgress(new ProgressEventArgs(0.0, "Started")); GameVerifier.Progress += OnVerifyProgress; GameVerifier.Verify(token); - Logger?.LogDebug($"Finished verifier '{GameVerifier.FriendlyName}'"); + Logger?.LogDebug("Finished verifier '{Name}'", GameVerifier.FriendlyName); ReportProgress(new ProgressEventArgs(1.0, "Finished")); } finally diff --git a/src/ModVerify/Reporting/Json/JsonVerificationError.cs b/src/ModVerify/Reporting/Json/JsonVerificationError.cs index ac80810..55f7b92 100644 --- a/src/ModVerify/Reporting/Json/JsonVerificationError.cs +++ b/src/ModVerify/Reporting/Json/JsonVerificationError.cs @@ -31,7 +31,7 @@ private JsonVerificationError( string message, VerificationSeverity severity, IEnumerable? contextEntries, - string asset) + string? asset) { Id = id; VerifierChain = verifierChain ?? []; diff --git a/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs b/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs index a2a00c5..cb6b258 100644 --- a/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs +++ b/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs @@ -14,6 +14,7 @@ internal sealed class GameAssertErrorReporter(IGameRepository gameRepository, IS protected override ErrorData CreateError(EngineAssert assert) { + // TODO: Why is context not used atm? var context = new List(); if (assert.Value is not null) diff --git a/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs b/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs index 601e424..7de81eb 100644 --- a/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs +++ b/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs @@ -7,49 +7,46 @@ namespace AET.ModVerify.Reporting.Reporters; public static class VerificationReportersExtensions { - public static IServiceCollection RegisterJsonReporter(this IServiceCollection serviceCollection) + extension(IServiceCollection serviceCollection) { - return RegisterJsonReporter(serviceCollection, new JsonReporterSettings + public IServiceCollection RegisterJsonReporter() { - OutputDirectory = "." - }); - } + return RegisterJsonReporter(serviceCollection, new JsonReporterSettings + { + OutputDirectory = "." + }); + } - public static IServiceCollection RegisterTextFileReporter(this IServiceCollection serviceCollection) - { - return RegisterTextFileReporter(serviceCollection, new TextFileReporterSettings + public IServiceCollection RegisterTextFileReporter() { - OutputDirectory = "." - }); - } + return RegisterTextFileReporter(serviceCollection, new TextFileReporterSettings + { + OutputDirectory = "." + }); + } - public static IServiceCollection RegisterConsoleReporter(this IServiceCollection serviceCollection, bool summaryOnly = false) - { - return RegisterConsoleReporter(serviceCollection, new VerifyReportSettings + public IServiceCollection RegisterConsoleReporter(bool summaryOnly = false) { - MinimumReportSeverity = VerificationSeverity.Error - }, summaryOnly); - } + return RegisterConsoleReporter(serviceCollection, new VerifyReportSettings + { + MinimumReportSeverity = VerificationSeverity.Error + }, summaryOnly); + } - public static IServiceCollection RegisterJsonReporter( - this IServiceCollection serviceCollection, - JsonReporterSettings settings) - { - return serviceCollection.AddSingleton(sp => new JsonReporter(settings, sp)); - } + public IServiceCollection RegisterJsonReporter(JsonReporterSettings settings) + { + return serviceCollection.AddSingleton(sp => new JsonReporter(settings, sp)); + } - public static IServiceCollection RegisterTextFileReporter( - this IServiceCollection serviceCollection, - TextFileReporterSettings settings) - { - return serviceCollection.AddSingleton(sp => new TextFileReporter(settings, sp)); - } + public IServiceCollection RegisterTextFileReporter(TextFileReporterSettings settings) + { + return serviceCollection.AddSingleton(sp => new TextFileReporter(settings, sp)); + } - public static IServiceCollection RegisterConsoleReporter( - this IServiceCollection serviceCollection, - VerifyReportSettings settings, - bool summaryOnly = false) - { - return serviceCollection.AddSingleton(sp => new ConsoleReporter(settings, summaryOnly, sp)); + public IServiceCollection RegisterConsoleReporter(VerifyReportSettings settings, + bool summaryOnly = false) + { + return serviceCollection.AddSingleton(sp => new ConsoleReporter(settings, summaryOnly, sp)); + } } } \ No newline at end of file diff --git a/src/ModVerify/Utilities/VerificationErrorExtensions.cs b/src/ModVerify/Utilities/VerificationErrorExtensions.cs index 1244b45..e5aa9fe 100644 --- a/src/ModVerify/Utilities/VerificationErrorExtensions.cs +++ b/src/ModVerify/Utilities/VerificationErrorExtensions.cs @@ -6,23 +6,24 @@ namespace AET.ModVerify.Utilities; public static class VerificationErrorExtensions { - public static IEnumerable ApplyBaseline(this IEnumerable errors, - VerificationBaseline baseline) + extension(IEnumerable errors) { - if (errors == null) - throw new ArgumentNullException(nameof(errors)); - if (baseline == null) - throw new ArgumentNullException(nameof(baseline)); - return baseline.Apply(errors); - } + public IEnumerable ApplyBaseline(VerificationBaseline baseline) + { + if (errors == null) + throw new ArgumentNullException(nameof(errors)); + if (baseline == null) + throw new ArgumentNullException(nameof(baseline)); + return baseline.Apply(errors); + } - public static IEnumerable ApplySuppressions(this IEnumerable errors, - SuppressionList suppressions) - { - if (errors == null) - throw new ArgumentNullException(nameof(errors)); - if (suppressions == null) - throw new ArgumentNullException(nameof(suppressions)); - return suppressions.Apply(errors); + public IEnumerable ApplySuppressions(SuppressionList suppressions) + { + if (errors == null) + throw new ArgumentNullException(nameof(errors)); + if (suppressions == null) + throw new ArgumentNullException(nameof(suppressions)); + return suppressions.Apply(errors); + } } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs index 7fa4d02..130fb50 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs @@ -62,7 +62,7 @@ public async Task InitializeAsync(CancellationToken token) } catch (Exception e) { - Logger?.LogError(e, $"Initialization of {this} failed: {e.Message}"); + Logger?.LogError(e, "Initialization of {Class} failed: {Message}", this, e.Message); throw; } OnInitialized(); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs index 82ce825..879c16f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs @@ -79,7 +79,7 @@ public IReadOnlyDictionary GetTextureEn { if (!_perComponentTextures.TryGetValue(component, out var textures)) { - Logger?.LogDebug($"The component '{component}' has no overrides. Using default textures."); + Logger?.LogDebug("The component '{Component}' has no overrides. Using default textures.", component); componentExist = false; return DefaultTextureEntries; } @@ -92,7 +92,7 @@ public bool TryGetTextureEntry(string component, GuiComponentType key, out Compo { if (!_perComponentTextures.TryGetValue(component, out var textures)) { - Logger?.LogDebug($"The component '{component}' has no overrides. Using default textures."); + Logger?.LogDebug("The component '{Component}' has no overrides. Using default textures.", component); textures = _defaultTextures; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs index 1304a89..e952db8 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs @@ -84,7 +84,7 @@ protected FileFoundInfo GetFileInfoFromMasterMeg(ReadOnlySpan filePath) if (filePath.Length > PGConstants.MaxMegEntryPathLength) { - Logger.LogWarning($"Trying to open a MEG entry which is longer than 259 characters: '{filePath.ToString()}'"); + Logger.LogWarning("Trying to open a MEG entry which is longer than 259 characters: '{FilePath}'", filePath.ToString()); return default; } @@ -97,7 +97,7 @@ protected FileFoundInfo GetFileInfoFromMasterMeg(ReadOnlySpan filePath) if (fileName.Length > PGConstants.MaxMegEntryPathLength) { - Logger.LogWarning($"Trying to open a MEG entry which is longer than 259 characters after normalization: '{fileName.ToString()}'"); + Logger.LogWarning("Trying to open a MEG entry which is longer than 259 characters after normalization: '{FileName}'", fileName.ToString()); return default; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs index 42bfa54..d1451b0 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs @@ -116,9 +116,9 @@ public void AddMegFile(string megFile) if (megArchive is null) { if (IsSpeechMeg(megFile)) - Logger.LogDebug($"Unable to find Speech MEG file at '{megFile}'"); + Logger.LogDebug("Unable to find Speech MEG file at '{MegFile}'", megFile); else - Logger.LogWarning($"Unable to find MEG file at '{megFile}'"); + Logger.LogWarning("Unable to find MEG file at '{MegFile}'", megFile); return; } @@ -217,7 +217,7 @@ protected IList LoadMegArchivesFromXml(string lookupPath) if (xmlStream is null) { - Logger.LogWarning($"Unable to find MegaFiles.xml at '{lookupPath}'"); + Logger.LogWarning("Unable to find MegaFiles.xml at '{LookupPath}'", lookupPath); return Array.Empty(); } @@ -251,12 +251,12 @@ internal void Seal() if (megFileStream is not FileSystemStream fileSystemStream) { if (IsSpeechMeg(megPath)) - Logger.LogDebug($"Unable to find Speech MEG file '{megPath}'"); + Logger.LogDebug("Unable to find Speech MEG file '{MegPath}'", megPath); else { var message = $"Unable to find MEG file '{megPath}'"; _errorReporter.Assert(EngineAssert.Create(EngineAssertKind.FileNotFound, megPath, [], message)); - Logger.LogWarning($"Unable to find MEG file '{megPath}'"); + Logger.LogWarning("Unable to find MEG file '{MegPath}'", megPath); } return null; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs index d2acfba..8ce1a5a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs @@ -65,7 +65,7 @@ private async Task InitializeEngine( { try { - _logger?.LogInformation($"Initializing game engine for type '{engineType}'."); + _logger?.LogInformation("Initializing game engine for type '{GameEngineType}'.", engineType); var repoFactory = _serviceProvider.GetRequiredService(); var repository = repoFactory.Create(engineType, gameLocations, errorReporter); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs index 5d5f98a..0c911e2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs @@ -37,13 +37,13 @@ public void ParseEntriesFromFileListXml( ValueListDictionary entries, Action? onFileParseAction = null) where T : notnull { - Logger.LogDebug($"Parsing container data '{xmlFile}'"); + Logger.LogDebug("Parsing container data '{XmlFile}'", xmlFile); using var containerStream = gameRepository.TryOpenFile(xmlFile); if (containerStream == null) { _reporter?.Report(this, XmlParseErrorEventArgs.FromMissingFile(xmlFile)); - Logger.LogWarning($"Could not find XML file '{xmlFile}'"); + Logger.LogWarning("Could not find XML file '{XmlFile}'", xmlFile); var args = new XmlContainerParserErrorEventArgs(xmlFile, null, true) { @@ -89,7 +89,7 @@ public void ParseEntriesFromFileListXml( if (fileStream is null) { _reporter?.Report(parser, XmlParseErrorEventArgs.FromMissingFile(file)); - Logger.LogWarning($"Could not find XML file '{file}'"); + Logger.LogWarning("Could not find XML file '{File}'", file); var args = new XmlContainerParserErrorEventArgs(file); XmlParseError?.Invoke(this, args); @@ -99,7 +99,7 @@ public void ParseEntriesFromFileListXml( return; } - Logger.LogDebug($"Parsing File '{file}'"); + Logger.LogDebug("Parsing File '{File}'", file); try { From 8147e9556827a1e58a38d58af6ab9f5125aac1e0 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 12:56:45 +0100 Subject: [PATCH 43/61] update modules --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index fa72788..e846c49 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit fa72788b1630140937053c6e3d71a011eb632b60 +Subproject commit e846c49181cf6e58818f4d8756b51050b05e0672 From dc238f0dd02e14efa8c27f7772fd6557df467a5f Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 13:16:45 +0100 Subject: [PATCH 44/61] update logging --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index e846c49..9335426 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit e846c49181cf6e58818f4d8756b51050b05e0672 +Subproject commit 9335426bb79a83597b31ce47175a97ee605840ff From 90c51bcc90da673632eab14e045e80e9a65dbf46 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 13:16:50 +0100 Subject: [PATCH 45/61] update module --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 9335426..c71cc09 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 9335426bb79a83597b31ce47175a97ee605840ff +Subproject commit c71cc0996132099771f9b39e8baa4105eadbbbf4 From 7f44bd54f65eef34ac730520ac13eeaf022dd9d6 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 13:21:46 +0100 Subject: [PATCH 46/61] update ci to .net 10 --- .github/workflows/release.yml | 18 +++++++++--------- .github/workflows/test.yml | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7ff6b7d..e0c54ab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,18 +35,18 @@ jobs: runs-on: windows-latest steps: - name: Checkout sources - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 submodules: recursive - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 - name: Create NetFramework Release run: dotnet publish ${{ env.TOOL_PROJ_PATH }} --configuration Release -f net48 --output ./releases/net48 /p:DebugType=None /p:DebugSymbols=false - name: Create Net Core Release - run: dotnet publish ${{ env.TOOL_PROJ_PATH }} --configuration Release -f net9.0 --output ./releases/net9.0 /p:DebugType=None /p:DebugSymbols=false + run: dotnet publish ${{ env.TOOL_PROJ_PATH }} --configuration Release -f net10.0 --output ./releases/net10.0 /p:DebugType=None /p:DebugSymbols=false - name: Upload a Build Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: Binary Releases path: ./releases @@ -62,17 +62,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v6 with: name: Binary Releases path: ./releases - name: Create NET Core .zip # Change into the artifacts directory to avoid including the directory itself in the zip archive - working-directory: ./releases/net9.0 - run: zip -r ../ModVerify-Net9.zip . + working-directory: ./releases/net10.0 + run: zip -r ../ModVerify-Net10.zip . - uses: dotnet/nbgv@v0.4.2 id: nbgv - name: Create GitHub release @@ -87,4 +87,4 @@ jobs: generate_release_notes: true files: | ./releases/net48/ModVerify.exe - ./releases/ModVerify-Net9.zip \ No newline at end of file + ./releases/ModVerify-Net10.zip \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f6c4d43..6e60fde 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,12 +19,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 submodules: recursive - uses: actions/setup-dotnet@v4 with: - dotnet-version: 9.0.x + dotnet-version: 10.0.x - name: Build & Test in Release Mode run: dotnet test --configuration Release \ No newline at end of file From 1f2e5732fa5ce5179f893b426604608eed88f165 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 13:27:19 +0100 Subject: [PATCH 47/61] fix test --- test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs index db119dc..cde1dad 100644 --- a/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs +++ b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs @@ -37,7 +37,7 @@ public BaselineSelectorTest() } [Theory] - [InlineData(GameEngineType.Eaw)] + // [InlineData(GameEngineType.Eaw)] TODO EaW is currently not supported [InlineData(GameEngineType.Foc)] public void LoadEmbeddedBaseline(GameEngineType engineType) { From d56a906bbf89d540bc5a0e4b1b2946eb5f3358fd Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 13:31:23 +0100 Subject: [PATCH 48/61] mograte to new solution file --- ModVerify.sln | 151 ------------------------------------------------- ModVerify.slnx | 28 +++++++++ 2 files changed, 28 insertions(+), 151 deletions(-) delete mode 100644 ModVerify.sln create mode 100644 ModVerify.slnx diff --git a/ModVerify.sln b/ModVerify.sln deleted file mode 100644 index ce8d0b9..0000000 --- a/ModVerify.sln +++ /dev/null @@ -1,151 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.11.34909.67 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PetroglyphTools", "PetroglyphTools", "{15F8B753-814A-406E-9147-EB048DADAC96}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModVerify", "src\ModVerify\ModVerify.csproj", "{22ED0E2C-FF3B-40EB-9CE2-DCDE65CDF31B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModVerify.CliApp", "src\ModVerify.CliApp\ModVerify.CliApp.csproj", "{84479931-A329-4113-9BE5-90B71E5486E6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PG.StarWarsGame.Files.ChunkFiles", "src\PetroglyphTools\PG.StarWarsGame.Files.ChunkFiles\PG.StarWarsGame.Files.ChunkFiles.csproj", "{92F2A0C8-61B6-424B-99D5-7898CDBA7CA6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PG.StarWarsGame.Files.ALO", "src\PetroglyphTools\PG.StarWarsGame.Files.ALO\PG.StarWarsGame.Files.ALO.csproj", "{DF76A383-C94E-4D03-A07C-22D61ED37059}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PG.StarWarsGame.Files.XML", "src\PetroglyphTools\PG.StarWarsGame.Files.XML\PG.StarWarsGame.Files.XML.csproj", "{418C68FA-531B-432E-8459-6433181C8AD3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PG.StarWarsGame.Engine", "src\PetroglyphTools\PG.StarWarsGame.Engine\PG.StarWarsGame.Engine.csproj", "{DFD62F61-3455-44BE-BB7C-E954FF48534B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AnakinApps", "AnakinApps", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ModdingToolBase", "ModdingToolBase", "{E6AFA947-48BE-416A-BEB1-C76E9D0843A8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApplicationBase", "modules\ModdingToolBase\src\AnakinApps\ApplicationBase\ApplicationBase.csproj", "{92A18973-0EB9-120E-7850-B37A24189DA8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApplicationBase.CommandLine", "modules\ModdingToolBase\src\AnakinApps\ApplicationBase.CommandLine\ApplicationBase.CommandLine.csproj", "{E06AF71E-5BC3-6837-4B6F-19A153B48327}" -EndProject -Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ApplicationBase.Shared", "modules\ModdingToolBase\src\AnakinApps\ApplicationBase.Shared\ApplicationBase.Shared.shproj", "{B297A13A-8E3A-436C-BA97-8B5F57827FFE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DeployTools", "DeployTools", "{749BC149-9DBD-46CA-B039-DAEA4C05FD97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApplicationManifestCreator", "modules\ModdingToolBase\src\AnakinApps\ApplicationManifestCreator\ApplicationManifestCreator.csproj", "{E8D2FB2D-8C9C-37D8-58AD-1A8F05E89999}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FtpUploader", "modules\ModdingToolBase\src\AnakinApps\FtpUploader\FtpUploader.csproj", "{4F70D564-DE7F-2101-EA21-B6B9398D13F2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UpdateFrameworks", "UpdateFrameworks", "{C535EAD3-87C0-4C85-BBF6-03A8B3A3E389}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppUpdaterFramework", "modules\ModdingToolBase\src\Updater\AppUpdaterFramework\AppUpdaterFramework.csproj", "{D7E1FA39-5484-B17A-2138-798858D5DA09}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppUpdaterFramework.Attributes", "modules\ModdingToolBase\src\Updater\AppUpdaterFramework.Attributes\AppUpdaterFramework.Attributes.csproj", "{7908FA18-0B16-B6E7-207F-077048A0CB63}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppUpdaterFramework.Manifest", "modules\ModdingToolBase\src\Updater\AppUpdaterFramework.Manifest\AppUpdaterFramework.Manifest.csproj", "{F1666AC0-2207-B91F-4369-7C8C38A4245D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExternalUpdater.App", "modules\ModdingToolBase\src\Updater\ExternalUpdater.App\ExternalUpdater.App.csproj", "{18C9D26C-6BBC-E3E0-25EF-D66762103B14}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExternalUpdater.Core", "modules\ModdingToolBase\src\Updater\ExternalUpdater.Core\ExternalUpdater.Core.csproj", "{D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModVerify.CliApp.Test", "test\ModVerify.CliApp.Test\ModVerify.CliApp.Test.csproj", "{EBFFD896-7432-4199-A7A3-311839E67278}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {22ED0E2C-FF3B-40EB-9CE2-DCDE65CDF31B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {22ED0E2C-FF3B-40EB-9CE2-DCDE65CDF31B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {22ED0E2C-FF3B-40EB-9CE2-DCDE65CDF31B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {22ED0E2C-FF3B-40EB-9CE2-DCDE65CDF31B}.Release|Any CPU.Build.0 = Release|Any CPU - {84479931-A329-4113-9BE5-90B71E5486E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {84479931-A329-4113-9BE5-90B71E5486E6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {84479931-A329-4113-9BE5-90B71E5486E6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {84479931-A329-4113-9BE5-90B71E5486E6}.Release|Any CPU.Build.0 = Release|Any CPU - {92F2A0C8-61B6-424B-99D5-7898CDBA7CA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {92F2A0C8-61B6-424B-99D5-7898CDBA7CA6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {92F2A0C8-61B6-424B-99D5-7898CDBA7CA6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {92F2A0C8-61B6-424B-99D5-7898CDBA7CA6}.Release|Any CPU.Build.0 = Release|Any CPU - {DF76A383-C94E-4D03-A07C-22D61ED37059}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DF76A383-C94E-4D03-A07C-22D61ED37059}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DF76A383-C94E-4D03-A07C-22D61ED37059}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DF76A383-C94E-4D03-A07C-22D61ED37059}.Release|Any CPU.Build.0 = Release|Any CPU - {418C68FA-531B-432E-8459-6433181C8AD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {418C68FA-531B-432E-8459-6433181C8AD3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {418C68FA-531B-432E-8459-6433181C8AD3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {418C68FA-531B-432E-8459-6433181C8AD3}.Release|Any CPU.Build.0 = Release|Any CPU - {DFD62F61-3455-44BE-BB7C-E954FF48534B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DFD62F61-3455-44BE-BB7C-E954FF48534B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DFD62F61-3455-44BE-BB7C-E954FF48534B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DFD62F61-3455-44BE-BB7C-E954FF48534B}.Release|Any CPU.Build.0 = Release|Any CPU - {92A18973-0EB9-120E-7850-B37A24189DA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {92A18973-0EB9-120E-7850-B37A24189DA8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {92A18973-0EB9-120E-7850-B37A24189DA8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {92A18973-0EB9-120E-7850-B37A24189DA8}.Release|Any CPU.Build.0 = Release|Any CPU - {E06AF71E-5BC3-6837-4B6F-19A153B48327}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E06AF71E-5BC3-6837-4B6F-19A153B48327}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E06AF71E-5BC3-6837-4B6F-19A153B48327}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E06AF71E-5BC3-6837-4B6F-19A153B48327}.Release|Any CPU.Build.0 = Release|Any CPU - {E8D2FB2D-8C9C-37D8-58AD-1A8F05E89999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8D2FB2D-8C9C-37D8-58AD-1A8F05E89999}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8D2FB2D-8C9C-37D8-58AD-1A8F05E89999}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8D2FB2D-8C9C-37D8-58AD-1A8F05E89999}.Release|Any CPU.Build.0 = Release|Any CPU - {4F70D564-DE7F-2101-EA21-B6B9398D13F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4F70D564-DE7F-2101-EA21-B6B9398D13F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4F70D564-DE7F-2101-EA21-B6B9398D13F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4F70D564-DE7F-2101-EA21-B6B9398D13F2}.Release|Any CPU.Build.0 = Release|Any CPU - {D7E1FA39-5484-B17A-2138-798858D5DA09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D7E1FA39-5484-B17A-2138-798858D5DA09}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D7E1FA39-5484-B17A-2138-798858D5DA09}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D7E1FA39-5484-B17A-2138-798858D5DA09}.Release|Any CPU.Build.0 = Release|Any CPU - {7908FA18-0B16-B6E7-207F-077048A0CB63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7908FA18-0B16-B6E7-207F-077048A0CB63}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7908FA18-0B16-B6E7-207F-077048A0CB63}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7908FA18-0B16-B6E7-207F-077048A0CB63}.Release|Any CPU.Build.0 = Release|Any CPU - {F1666AC0-2207-B91F-4369-7C8C38A4245D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1666AC0-2207-B91F-4369-7C8C38A4245D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1666AC0-2207-B91F-4369-7C8C38A4245D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1666AC0-2207-B91F-4369-7C8C38A4245D}.Release|Any CPU.Build.0 = Release|Any CPU - {18C9D26C-6BBC-E3E0-25EF-D66762103B14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {18C9D26C-6BBC-E3E0-25EF-D66762103B14}.Debug|Any CPU.Build.0 = Debug|Any CPU - {18C9D26C-6BBC-E3E0-25EF-D66762103B14}.Release|Any CPU.ActiveCfg = Release|Any CPU - {18C9D26C-6BBC-E3E0-25EF-D66762103B14}.Release|Any CPU.Build.0 = Release|Any CPU - {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030}.Release|Any CPU.Build.0 = Release|Any CPU - {EBFFD896-7432-4199-A7A3-311839E67278}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EBFFD896-7432-4199-A7A3-311839E67278}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EBFFD896-7432-4199-A7A3-311839E67278}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EBFFD896-7432-4199-A7A3-311839E67278}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {92F2A0C8-61B6-424B-99D5-7898CDBA7CA6} = {15F8B753-814A-406E-9147-EB048DADAC96} - {DF76A383-C94E-4D03-A07C-22D61ED37059} = {15F8B753-814A-406E-9147-EB048DADAC96} - {418C68FA-531B-432E-8459-6433181C8AD3} = {15F8B753-814A-406E-9147-EB048DADAC96} - {DFD62F61-3455-44BE-BB7C-E954FF48534B} = {15F8B753-814A-406E-9147-EB048DADAC96} - {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {E6AFA947-48BE-416A-BEB1-C76E9D0843A8} - {92A18973-0EB9-120E-7850-B37A24189DA8} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {E06AF71E-5BC3-6837-4B6F-19A153B48327} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {B297A13A-8E3A-436C-BA97-8B5F57827FFE} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {749BC149-9DBD-46CA-B039-DAEA4C05FD97} = {E6AFA947-48BE-416A-BEB1-C76E9D0843A8} - {E8D2FB2D-8C9C-37D8-58AD-1A8F05E89999} = {749BC149-9DBD-46CA-B039-DAEA4C05FD97} - {4F70D564-DE7F-2101-EA21-B6B9398D13F2} = {749BC149-9DBD-46CA-B039-DAEA4C05FD97} - {C535EAD3-87C0-4C85-BBF6-03A8B3A3E389} = {E6AFA947-48BE-416A-BEB1-C76E9D0843A8} - {D7E1FA39-5484-B17A-2138-798858D5DA09} = {C535EAD3-87C0-4C85-BBF6-03A8B3A3E389} - {7908FA18-0B16-B6E7-207F-077048A0CB63} = {C535EAD3-87C0-4C85-BBF6-03A8B3A3E389} - {F1666AC0-2207-B91F-4369-7C8C38A4245D} = {C535EAD3-87C0-4C85-BBF6-03A8B3A3E389} - {18C9D26C-6BBC-E3E0-25EF-D66762103B14} = {C535EAD3-87C0-4C85-BBF6-03A8B3A3E389} - {D97BEDD9-E04C-8CBC-29CC-B34AF2E35030} = {C535EAD3-87C0-4C85-BBF6-03A8B3A3E389} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {D74A22E2-91F1-4BC7-9630-3CF930B45408} - EndGlobalSection - GlobalSection(SharedMSBuildProjectFiles) = preSolution - modules\ModdingToolBase\src\AnakinApps\ApplicationBase.Shared\ApplicationBase.Shared.projitems*{4f70d564-de7f-2101-ea21-b6b9398d13f2}*SharedItemsImports = 5 - modules\ModdingToolBase\src\AnakinApps\ApplicationBase.Shared\ApplicationBase.Shared.projitems*{92a18973-0eb9-120e-7850-b37a24189da8}*SharedItemsImports = 5 - modules\ModdingToolBase\src\AnakinApps\ApplicationBase.Shared\ApplicationBase.Shared.projitems*{e8d2fb2d-8c9c-37d8-58ad-1a8f05e89999}*SharedItemsImports = 5 - EndGlobalSection -EndGlobal diff --git a/ModVerify.slnx b/ModVerify.slnx new file mode 100644 index 0000000..3527ff4 --- /dev/null +++ b/ModVerify.slnx @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 7b18778462b03d61f62a5a3240733c67912df352 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 13:31:28 +0100 Subject: [PATCH 49/61] update module --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index c71cc09..9c42cab 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit c71cc0996132099771f9b39e8baa4105eadbbbf4 +Subproject commit 9c42cab05da5a8890f94dbe24898551076a2fe77 From f9baac2ca4cb2e2aa33dd7f21c4cbc66db9d6e28 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 13:39:28 +0100 Subject: [PATCH 50/61] fix ci --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e0c54ab..ca0b612 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,7 +42,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v5 - name: Create NetFramework Release - run: dotnet publish ${{ env.TOOL_PROJ_PATH }} --configuration Release -f net48 --output ./releases/net48 /p:DebugType=None /p:DebugSymbols=false + run: dotnet publish ${{ env.TOOL_PROJ_PATH }} --configuration Release -f net481 --output ./releases/net481 /p:DebugType=None /p:DebugSymbols=false - name: Create Net Core Release run: dotnet publish ${{ env.TOOL_PROJ_PATH }} --configuration Release -f net10.0 --output ./releases/net10.0 /p:DebugType=None /p:DebugSymbols=false - name: Upload a Build Artifact @@ -86,5 +86,5 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} generate_release_notes: true files: | - ./releases/net48/ModVerify.exe + ./releases/net481/ModVerify.exe ./releases/ModVerify-Net10.zip \ No newline at end of file From 0fef562382142fc0f550e6241ddf0e8bd88c43bc Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 15:35:38 +0100 Subject: [PATCH 51/61] deploy to server --- .github/workflows/release.yml | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ca0b612..cd88dad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,8 +13,8 @@ on: env: TOOL_PROJ_PATH: ./src/ModVerify.CliApp/ModVerify.CliApp.csproj - CREATOR_PROJ_PATH: ./Modules/ModdingToolBase/src/AnakinApps/ApplicationManifestCreator/ApplicationManifestCreator.csproj - UPLOADER_PROJ_PATH: ./Modules/ModdingToolBase/src/AnakinApps/FtpUploader/FtpUploader.csproj + CREATOR_PROJ_PATH: ./modules/ModdingToolBase/src/AnakinApps/ApplicationManifestCreator/ApplicationManifestCreator.csproj + UPLOADER_PROJ_PATH: ./modules/ModdingToolBase/src/AnakinApps/FtpUploader/FtpUploader.csproj TOOL_EXE: ModVerify.exe UPDATER_EXE: AnakinRaW.ExternalUpdater.exe MANIFEST_CREATOR: AnakinRaW.ApplicationManifestCreator.dll @@ -65,10 +65,38 @@ jobs: uses: actions/checkout@v6 with: fetch-depth: 0 + submodules: recursive - uses: actions/download-artifact@v6 with: name: Binary Releases path: ./releases + + # Deploy .NET Framework self-update release + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.0.x + - name: Build Creator + run: dotnet build ${{env.CREATOR_PROJ_PATH}} --configuration Release --output ./dev + - name: Build Uploader + run: dotnet build ${{env.UPLOADER_PROJ_PATH}} --configuration Release --output ./dev + - name: Create binaries directory + run: mkdir -p ./deploy + - name: Copy self-update files + run: | + cp ./releases/net481/${{env.TOOL_EXE}} ./deploy/ + cp ./releases/net481/${{env.UPDATER_EXE}} ./deploy/ + - name: Create Manifest + run: dotnet ./dev/${{env.MANIFEST_CREATOR}} -a deploy/${{env.TOOL_EXE}} --appDataFiles deploy/${{env.UPDATER_EXE}} --origin ${{env.ORIGIN_BASE}} -o ./deploy -b ${{env.BRANCH_NAME}} + - name: Upload Build + run: dotnet ./dev/${{env.SFTP_UPLOADER}} -h $host --port $port -u ${{secrets.SFTP_USER}} -p ${{secrets.SFTP_PASSWORD}} --base $base_path -s $source + env: + host: republicatwar.com + port: 1579 + base_path: ${{env.ORIGIN_BASE_PART}} + source: ./deploy + + # Deploy .NET Core and .NET Framework apps to Github - name: Create NET Core .zip # Change into the artifacts directory to avoid including the directory itself in the zip archive working-directory: ./releases/net10.0 From e95a54d0f88de6ba2a9343626ad6f61d1a163644 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 16:14:37 +0100 Subject: [PATCH 52/61] update config --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index a27cd7f..03f89d6 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -57,7 +57,6 @@ all - runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -78,8 +77,8 @@ - - + + From e08c75739daa3df24c58fc7a9c399d3993ec6d53 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 16:30:51 +0100 Subject: [PATCH 53/61] new publish --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 03f89d6..f926fba 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -78,6 +78,9 @@ + + false + From 0dfbc5479c8f22690155075ad785dc95e07589f1 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 16:42:18 +0100 Subject: [PATCH 54/61] try fix deploy --- .github/workflows/release.yml | 4 +++- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 3 --- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cd88dad..b7bf527 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,8 +42,10 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v5 - name: Create NetFramework Release - run: dotnet publish ${{ env.TOOL_PROJ_PATH }} --configuration Release -f net481 --output ./releases/net481 /p:DebugType=None /p:DebugSymbols=false + # use build for .NET Framework to enusre external updatere .EXE is included + run: dotnet build ${{ env.TOOL_PROJ_PATH }} --configuration Release -f net481 --output ./releases/net481 /p:DebugType=None /p:DebugSymbols=false - name: Create Net Core Release + # use publish for .NET Core run: dotnet publish ${{ env.TOOL_PROJ_PATH }} --configuration Release -f net10.0 --output ./releases/net10.0 /p:DebugType=None /p:DebugSymbols=false - name: Upload a Build Artifact uses: actions/upload-artifact@v5 diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index f926fba..03f89d6 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -78,9 +78,6 @@ - - false - From 5b32d69c7d5402d79d37152a18dae8ec12f67728 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 16:49:18 +0100 Subject: [PATCH 55/61] fix launch of uploader --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b7bf527..7a9b844 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -91,7 +91,7 @@ jobs: - name: Create Manifest run: dotnet ./dev/${{env.MANIFEST_CREATOR}} -a deploy/${{env.TOOL_EXE}} --appDataFiles deploy/${{env.UPDATER_EXE}} --origin ${{env.ORIGIN_BASE}} -o ./deploy -b ${{env.BRANCH_NAME}} - name: Upload Build - run: dotnet ./dev/${{env.SFTP_UPLOADER}} -h $host --port $port -u ${{secrets.SFTP_USER}} -p ${{secrets.SFTP_PASSWORD}} --base $base_path -s $source + run: dotnet ./dev/${{env.SFTP_UPLOADER}} ftp --host $host --port $port -u ${{secrets.SFTP_USER}} -p ${{secrets.SFTP_PASSWORD}} --base $base_path -s $source env: host: republicatwar.com port: 1579 From 27785a4124eaf44fd7b2d454ee05550cb1fa2022 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 16:49:35 +0100 Subject: [PATCH 56/61] update module --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 9c42cab..38ab920 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 9c42cab05da5a8890f94dbe24898551076a2fe77 +Subproject commit 38ab920c9b0016c8f2e2234e23aec6a41b4e55eb From 23e56c7b7c0a177ee1b8ef368fa8121b52edd658 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 7 Dec 2025 17:03:07 +0100 Subject: [PATCH 57/61] udpate version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 251e6e2..3e87545 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "0.0-alpha", + "version": "0.1-beta", "publicReleaseRefSpec": [ "^refs/heads/main$" ], From 61dd6ca1272c9060ca54b5071a715b9cf7051505 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 8 Dec 2025 14:31:11 +0100 Subject: [PATCH 58/61] update dependencies --- modules/ModdingToolBase | 2 +- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 6 +++--- src/ModVerify/ModVerify.csproj | 4 ++-- .../PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj | 6 +++--- .../PG.StarWarsGame.Files.ChunkFiles.csproj | 2 +- .../PG.StarWarsGame.Files.XML.csproj | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 38ab920..479a088 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 38ab920c9b0016c8f2e2234e23aec6a41b4e55eb +Subproject commit 479a088a2b26dd4a3e2342b2e34f5359b0252e88 diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 03f89d6..0073b2b 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -30,12 +30,12 @@ - - + + - + diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index cd0f7b9..427458d 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -33,8 +33,8 @@ - - + + diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index 3269598..e54e6f0 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -23,9 +23,9 @@ - - - + + + diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj index 88b3cb9..36221be 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj @@ -17,7 +17,7 @@ preview - + diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj index f161cd5..fbb055c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj @@ -18,7 +18,7 @@ preview - + From 2c60e80bf53a3d099f97381295f351daba39b042 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 8 Dec 2025 18:07:34 +0100 Subject: [PATCH 59/61] logging again --- src/ModVerify.CliApp/ModVerifyApplication.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index c8a89bd..ce5bb40 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -161,7 +161,7 @@ private async Task> Verify( { try { - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Verifying '{InstallDataName}'...", installData.Name); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Verifying '{Target}'...", installData.Name); await verifyPipeline.RunAsync().ConfigureAwait(false); progressReporter.Report(string.Empty, 1.0); } @@ -224,7 +224,7 @@ private GlobalVerifyReportSettings CreateGlobalReportSettings(VerifyInstallation var baseline = baselineSelector.SelectBaseline(installData, out var baselinePath); if (baseline.Count > 0) - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Using baseline '{baselinePath}'"); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Using baseline '{UsedBaselinePath}'", baselinePath); var suppressionsFile = settings.ReportSettings.SuppressionsPath; SuppressionList suppressions; @@ -237,7 +237,7 @@ private GlobalVerifyReportSettings CreateGlobalReportSettings(VerifyInstallation suppressions = SuppressionList.FromJson(fs); if (suppressions.Count > 0) - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, $"Using suppressions from '{suppressionsFile}'"); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Using suppressions from '{SuppressionsFile}'", suppressionsFile); } From 7ceeb88c5a75322b4ece467357bba796385d30bb Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 8 Dec 2025 18:16:10 +0100 Subject: [PATCH 60/61] logigng --- src/ModVerify.CliApp/ModVerifyApplication.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index ce5bb40..7ea461c 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -86,8 +86,8 @@ private async Task RunVerify() var reportSettings = CreateGlobalReportSettings(installData); - _logger?.LogDebug("Verify install data: {VerifyInstallationData}", installData); - _logger?.LogTrace("Verify settings: {ModVerifyAppSettings}", settings); + _logger?.LogDebug("Verify install data: {InstallData}", installData); + _logger?.LogTrace("Verify settings: {Settings}", settings); var allErrors = await Verify(installData, reportSettings) .ConfigureAwait(false); @@ -126,7 +126,7 @@ private async Task> Verify( try { - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Creating Game Engine '{InstallDataEngineType}'", installData.EngineType); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Creating Game Engine '{Engine}'", installData.EngineType); gameEngine = await gameEngineService.InitializeAsync( installData.EngineType, installData.GameLocations, @@ -143,7 +143,7 @@ private async Task> Verify( } catch (Exception e) { - _logger?.LogError(e, "Creating game engine failed: {EMessage}", e.Message); + _logger?.LogError(e, "Creating game engine failed: {Message}", e.Message); throw; } @@ -181,7 +181,7 @@ private async Task> Verify( } catch (Exception e) { - _logger?.LogError(e, "Verification failed: {EMessage}", e.Message); + _logger?.LogError(e, "Verification failed: {Message}", e.Message); throw; } @@ -224,7 +224,7 @@ private GlobalVerifyReportSettings CreateGlobalReportSettings(VerifyInstallation var baseline = baselineSelector.SelectBaseline(installData, out var baselinePath); if (baseline.Count > 0) - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Using baseline '{UsedBaselinePath}'", baselinePath); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Using baseline '{Baseline}'", baselinePath); var suppressionsFile = settings.ReportSettings.SuppressionsPath; SuppressionList suppressions; @@ -237,7 +237,7 @@ private GlobalVerifyReportSettings CreateGlobalReportSettings(VerifyInstallation suppressions = SuppressionList.FromJson(fs); if (suppressions.Count > 0) - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Using suppressions from '{SuppressionsFile}'", suppressionsFile); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Using suppressions from '{Suppressions}'", suppressionsFile); } From 27e603db91cc660a1477125c598d40d85a18ae09 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 8 Dec 2025 18:25:55 +0100 Subject: [PATCH 61/61] logging der 5. --- src/ModVerify.CliApp/Reporting/BaselineFactory.cs | 2 +- src/ModVerify.CliApp/Reporting/BaselineSelector.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ModVerify.CliApp/Reporting/BaselineFactory.cs b/src/ModVerify.CliApp/Reporting/BaselineFactory.cs index 9f5deb9..9fcc911 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineFactory.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineFactory.cs @@ -49,7 +49,7 @@ public bool TryCreateBaseline( } catch (InvalidBaselineException e) { - _logger?.LogDebug("'{JsonFile}' is not a valid baseline file: {EMessage}", jsonFile, e.Message); + _logger?.LogDebug("'{JsonFile}' is not a valid baseline file: {Message}", jsonFile, e.Message); // Ignore this exception } } diff --git a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs index 9650ddd..95953f1 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs @@ -69,7 +69,7 @@ private VerificationBaseline FindBaselineInteractive(VerifyInstallationData inst if (!_baselineFactory.TryCreateBaseline(installationData.GameLocations.TargetPath, out var baseline, out baselinePath)) { - if (!_baselineFactory.TryCreateBaseline(".", out baseline, out baselinePath)) + if (!_baselineFactory.TryCreateBaseline("./", out baseline, out baselinePath)) { // It does not make sense to load the game's default baselines if the user wants to verify the game, // as the verification result would always be empty (at least in a non-development scenario)