From 9c237b231b84934eade275c63df21b895d730b28 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 13 Dec 2025 11:06:55 +0100 Subject: [PATCH 01/22] start verify target impl --- .../ModSelectors/VerifyInstallationData.cs | 28 --- src/ModVerify.CliApp/ModVerifyApplication.cs | 42 +--- src/ModVerify/Pipeline/GameVerifyPipeline.cs | 220 +++++++++++++++--- src/ModVerify/VerificationTarget.cs | 41 ++++ src/ModVerify/Verifiers/GameVerifierBase.cs | 5 +- .../PG.StarWarsGame.Engine/GameLocations.cs | 21 +- .../IGameEngineInitializationReporter.cs | 10 + .../IPetroglyphStarWarsGameEngineService.cs | 5 +- .../PetroglyphStarWarsGameEngineService.cs | 22 +- 9 files changed, 276 insertions(+), 118 deletions(-) delete mode 100644 src/ModVerify.CliApp/ModSelectors/VerifyInstallationData.cs create mode 100644 src/ModVerify/VerificationTarget.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/IGameEngineInitializationReporter.cs diff --git a/src/ModVerify.CliApp/ModSelectors/VerifyInstallationData.cs b/src/ModVerify.CliApp/ModSelectors/VerifyInstallationData.cs deleted file mode 100644 index 1a1fcd2..0000000 --- a/src/ModVerify.CliApp/ModSelectors/VerifyInstallationData.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Text; -using PG.StarWarsGame.Engine; - -namespace AET.ModVerify.App.ModSelectors; - -internal sealed class VerifyInstallationData -{ - public required string Name { get; init; } - - public required GameEngineType EngineType { get; init; } - - public required GameLocations GameLocations { get; init; } - - public override string ToString() - { - var sb = new StringBuilder(); - - sb.AppendLine($"ObjectToVerify={Name};EngineType={EngineType};Locations=["); - if (GameLocations.ModPaths.Count > 0) - sb.AppendLine($"Mods=[{string.Join(";", GameLocations.ModPaths)}];"); - sb.AppendLine($"Game=[{GameLocations.GamePath}];"); - if (GameLocations.FallbackPaths.Count > 0) - sb.AppendLine($"Fallbacks=[{string.Join(";", GameLocations.FallbackPaths)}];"); - sb.AppendLine("]"); - - return sb.ToString(); - } -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index 7ea461c..43f3632 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -8,14 +8,12 @@ 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 AET.ModVerify.App.GameFinder; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -114,47 +112,15 @@ private async Task> Verify( VerifyInstallationData installData, GlobalVerifyReportSettings reportSettings) { - var gameEngineService = services.GetRequiredService(); - var engineErrorReporter = new ConcurrentGameEngineErrorReporter(); - - IStarWarsGameEngine gameEngine; - - try - { - var initProgress = new Progress(); - var initProgressReporter = new EngineInitializeProgressReporter(initProgress); - - try - { - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Creating Game Engine '{Engine}'", installData.EngineType); - gameEngine = await gameEngineService.InitializeAsync( - installData.EngineType, - installData.GameLocations, - engineErrorReporter, - initProgress, - false, - CancellationToken.None).ConfigureAwait(false); - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Game Engine created"); - } - finally - { - initProgressReporter.Dispose(); - } - } - catch (Exception e) - { - _logger?.LogError(e, "Creating game engine failed: {Message}", e.Message); - throw; - } - + var initProgressReporter = new EngineInitializeProgressReporter(null); var progressReporter = new VerifyConsoleProgressReporter(installData.Name); - using var verifyPipeline = new GameVerifyPipeline( - gameEngine, - engineErrorReporter, + using var verifyPipeline = new NewGameVerifyPipeline( + null, settings.VerifyPipelineSettings, reportSettings, progressReporter, + null, services); try diff --git a/src/ModVerify/Pipeline/GameVerifyPipeline.cs b/src/ModVerify/Pipeline/GameVerifyPipeline.cs index 810651a..ab62511 100644 --- a/src/ModVerify/Pipeline/GameVerifyPipeline.cs +++ b/src/ModVerify/Pipeline/GameVerifyPipeline.cs @@ -1,4 +1,5 @@ -using AET.ModVerify.Reporting; +using AET.ModVerify.Pipeline.Progress; +using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Settings; using AET.ModVerify.Utilities; @@ -12,72 +13,96 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using AET.ModVerify.Pipeline.Progress; +using Microsoft.Extensions.DependencyInjection; namespace AET.ModVerify.Pipeline; -public sealed class GameVerifyPipeline : AnakinRaW.CommonUtilities.SimplePipeline.Pipeline +public sealed class NewGameVerifyPipeline : AnakinRaW.CommonUtilities.SimplePipeline.Pipeline { private readonly List _verifiers = new(); private readonly List _verificationSteps = new(); - private readonly StepRunnerBase _verifyRunner; - - private readonly IStarWarsGameEngine _gameEngine; - private readonly IGameEngineErrorCollection _engineErrors; + private readonly ConcurrentGameEngineErrorReporter _engineErrorReporter = new(); + private readonly StepRunnerBase _verifyRunner; + private readonly VerificationTarget _verificationTarget; private readonly VerifyPipelineSettings _pipelineSettings; private readonly GlobalVerifyReportSettings _reportSettings; - private readonly IVerifyProgressReporter _progressReporter; + private readonly IGameEngineInitializationReporter? _engineInitializationReporter; + private readonly IPetroglyphStarWarsGameEngineService _gameEngineService; + private readonly ILogger? _logger; protected override bool FailFast { get; } public IReadOnlyCollection FilteredErrors { get; private set; } = []; - - public GameVerifyPipeline( - IStarWarsGameEngine gameEngine, - IGameEngineErrorCollection engineErrors, - VerifyPipelineSettings pipelineSettings, + + public NewGameVerifyPipeline( + VerificationTarget verificationTarget, + VerifyPipelineSettings pipelineSettings, GlobalVerifyReportSettings reportSettings, IVerifyProgressReporter progressReporter, + IGameEngineInitializationReporter? engineInitializationReporter, IServiceProvider serviceProvider) : base(serviceProvider) { - _gameEngine = gameEngine ?? throw new ArgumentNullException(nameof(gameEngine)); - _engineErrors = engineErrors ?? throw new ArgumentNullException(nameof(gameEngine)); + _verificationTarget = verificationTarget ?? throw new ArgumentNullException(nameof(verificationTarget)); _pipelineSettings = pipelineSettings ?? throw new ArgumentNullException(nameof(pipelineSettings)); _reportSettings = reportSettings ?? throw new ArgumentNullException(nameof(reportSettings)); _progressReporter = progressReporter ?? throw new ArgumentNullException(nameof(progressReporter)); + _engineInitializationReporter = engineInitializationReporter; + _gameEngineService = serviceProvider.GetRequiredService(); + _logger = serviceProvider.GetService()?.CreateLogger(GetType()); - if (pipelineSettings.ParallelVerifiers is < 0 or > 64) - throw new ArgumentException("_pipelineSettings has invalid parallel worker number.", nameof(pipelineSettings)); - - if (pipelineSettings.ParallelVerifiers == 1) - _verifyRunner = new SequentialStepRunner(serviceProvider); - else - _verifyRunner = new ParallelStepRunner(pipelineSettings.ParallelVerifiers, serviceProvider); + _verifyRunner = pipelineSettings.ParallelVerifiers switch + { + < 0 or > 64 => throw new ArgumentException("_pipelineSettings has invalid parallel worker number.", + nameof(pipelineSettings)), + 1 => new SequentialStepRunner(serviceProvider), + _ => new ParallelStepRunner(pipelineSettings.ParallelVerifiers, serviceProvider) + }; FailFast = pipelineSettings.FailFast; } - protected override Task PrepareCoreAsync() + protected override async Task PrepareCoreAsync() { _verifiers.Clear(); - AddStep(new GameEngineErrorCollector(_engineErrors, _gameEngine, _pipelineSettings.GameVerifySettings, ServiceProvider)); - foreach (var gameVerificationStep in CreateVerificationSteps(_gameEngine)) + IStarWarsGameEngine gameEngine; + + try + { + gameEngine = await _gameEngineService.InitializeAsync( + _verificationTarget.Engine, + _verificationTarget.Location, + _engineErrorReporter, + _engineInitializationReporter, + false, + CancellationToken.None).ConfigureAwait(false); + } + catch (Exception e) + { + _logger?.LogError(e, "Creating game engine failed: {Message}", e.Message); + throw; + } + + + AddStep(new GameEngineErrorCollector(_engineErrorReporter, gameEngine, _pipelineSettings.GameVerifySettings, ServiceProvider)); + + foreach (var gameVerificationStep in CreateVerificationSteps(gameEngine)) AddStep(gameVerificationStep); - return Task.FromResult(true); + return true; } protected override async Task RunCoreAsync(CancellationToken token) - { + { var aggregatedVerifyProgressReporter = new AggregatedVerifyProgressReporter(_progressReporter, _verificationSteps); try { Logger?.LogInformation("Running game verifiers..."); + _progressReporter.Report(0.0, $"Verifing {_verificationTarget.Name}...", VerifyProgress.ProgressType, default); _verifyRunner.Error += OnError; await _verifyRunner.RunAsync(token); } @@ -97,6 +122,9 @@ protected override async Task RunCoreAsync(CancellationToken token) throw new StepFailureException(failedSteps); FilteredErrors = GetReportableErrors(_verifiers.SelectMany(s => s.VerifyErrors)).ToList(); + + + _progressReporter.Report(1.0, $"Finished Verifing {_verificationTarget.Name}", VerifyProgress.ProgressType, default); } protected override void OnError(object sender, StepRunnerErrorEventArgs e) @@ -110,11 +138,6 @@ protected override void OnError(object sender, StepRunnerErrorEventArgs e) base.OnError(sender, e); } - private IEnumerable CreateVerificationSteps(IStarWarsGameEngine database) - { - return _pipelineSettings.VerifiersProvider.GetVerifiers(database, _pipelineSettings.GameVerifySettings, ServiceProvider); - } - private void AddStep(GameVerifier verifier) { var verificationStep = new GameVerifierPipelineStep(verifier, ServiceProvider); @@ -131,4 +154,135 @@ private IEnumerable GetReportableErrors(IEnumerable CreateVerificationSteps(IStarWarsGameEngine engine) + { + return _pipelineSettings.VerifiersProvider + .GetVerifiers(engine, _pipelineSettings.GameVerifySettings, ServiceProvider); + } +} + + + + + + + + + +//public sealed class GameVerifyPipeline : AnakinRaW.CommonUtilities.SimplePipeline.Pipeline +//{ +// private readonly List _verifiers = new(); +// private readonly List _verificationSteps = new(); +// private readonly StepRunnerBase _verifyRunner; + +// private readonly IStarWarsGameEngine _gameEngine; +// private readonly IGameEngineErrorCollection _engineErrors; + +// private readonly VerifyPipelineSettings _pipelineSettings; +// private readonly GlobalVerifyReportSettings _reportSettings; + +// private readonly IVerifyProgressReporter _progressReporter; + +// protected override bool FailFast { get; } + +// public IReadOnlyCollection FilteredErrors { get; private set; } = []; + +// public GameVerifyPipeline( +// IStarWarsGameEngine gameEngine, +// IGameEngineErrorCollection engineErrors, +// VerifyPipelineSettings pipelineSettings, +// GlobalVerifyReportSettings reportSettings, +// IVerifyProgressReporter progressReporter, +// IServiceProvider serviceProvider) : base(serviceProvider) +// { +// _gameEngine = gameEngine ?? throw new ArgumentNullException(nameof(gameEngine)); +// _engineErrors = engineErrors ?? throw new ArgumentNullException(nameof(gameEngine)); +// _pipelineSettings = pipelineSettings ?? throw new ArgumentNullException(nameof(pipelineSettings)); +// _reportSettings = reportSettings ?? throw new ArgumentNullException(nameof(reportSettings)); +// _progressReporter = progressReporter ?? throw new ArgumentNullException(nameof(progressReporter)); + +// if (pipelineSettings.ParallelVerifiers is < 0 or > 64) +// throw new ArgumentException("_pipelineSettings has invalid parallel worker number.", nameof(pipelineSettings)); + +// if (pipelineSettings.ParallelVerifiers == 1) +// _verifyRunner = new SequentialStepRunner(serviceProvider); +// else +// _verifyRunner = new ParallelStepRunner(pipelineSettings.ParallelVerifiers, serviceProvider); + +// FailFast = pipelineSettings.FailFast; +// } + +// protected override Task PrepareCoreAsync() +// { +// _verifiers.Clear(); + +// AddStep(new GameEngineErrorCollector(_engineErrors, _gameEngine, _pipelineSettings.GameVerifySettings, ServiceProvider)); + +// foreach (var gameVerificationStep in CreateVerificationSteps(_gameEngine)) +// AddStep(gameVerificationStep); + +// return Task.FromResult(true); +// } + +// protected override async Task RunCoreAsync(CancellationToken token) +// { +// var aggregatedVerifyProgressReporter = new AggregatedVerifyProgressReporter(_progressReporter, _verificationSteps); + +// try +// { +// Logger?.LogInformation("Running game verifiers..."); +// _verifyRunner.Error += OnError; +// await _verifyRunner.RunAsync(token); +// } +// finally +// { +// aggregatedVerifyProgressReporter.Dispose(); +// _verifyRunner.Error -= OnError; +// Logger?.LogDebug("Game verifiers finished."); +// } + +// token.ThrowIfCancellationRequested(); + +// var failedSteps = _verifyRunner.ExecutedSteps.Where(p => +// p.Error != null && !p.Error.IsExceptionType()).ToList(); + +// if (failedSteps.Count != 0) +// throw new StepFailureException(failedSteps); + +// FilteredErrors = GetReportableErrors(_verifiers.SelectMany(s => s.VerifyErrors)).ToList(); +// } + +// 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; +// } +// base.OnError(sender, e); +// } + +// private IEnumerable CreateVerificationSteps(IStarWarsGameEngine database) +// { +// return _pipelineSettings.VerifiersProvider.GetVerifiers(database, _pipelineSettings.GameVerifySettings, ServiceProvider); +// } + +// private void AddStep(GameVerifier verifier) +// { +// var verificationStep = new GameVerifierPipelineStep(verifier, ServiceProvider); +// _verifyRunner.AddStep(verificationStep); +// _verificationSteps.Add(verificationStep); +// _verifiers.Add(verifier); +// } + +// private IEnumerable GetReportableErrors(IEnumerable errors) +// { +// Logger?.LogDebug("Applying baseline and suppressions."); +// // NB: We don't filter for severity here, as the individual reporters handle that. +// // This allows better control over what gets reported. +// return errors.ApplyBaseline(_reportSettings.Baseline) +// .ApplySuppressions(_reportSettings.Suppressions); +// } +//} \ No newline at end of file diff --git a/src/ModVerify/VerificationTarget.cs b/src/ModVerify/VerificationTarget.cs new file mode 100644 index 0000000..d3afdc7 --- /dev/null +++ b/src/ModVerify/VerificationTarget.cs @@ -0,0 +1,41 @@ +using System; +using System.Text; +using PG.StarWarsGame.Engine; + +namespace AET.ModVerify; + +public sealed class VerificationTarget +{ + public required GameEngineType Engine { get; init; } + + public required string Name + { + get; + init + { + if (string.IsNullOrEmpty(value)) + throw new ArgumentNullException(nameof(value)); + field = value; + } + } + + public required GameLocations Location + { + get; + init => field = value ?? throw new ArgumentNullException(nameof(value)); + } + + public string? Version { get; init; } + + public bool IsGame => Location.ModPaths.Count == 0; + + public override string ToString() + { + var sb = new StringBuilder($"[Name={Name};EngineType={Engine};"); + if (!string.IsNullOrEmpty(Version)) + sb.Append($"Version={Version};"); + sb.Append($"Location={Location};"); + sb.Append("]"); + return sb.ToString(); + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/GameVerifierBase.cs b/src/ModVerify/Verifiers/GameVerifierBase.cs index 8c9d67d..02bacc4 100644 --- a/src/ModVerify/Verifiers/GameVerifierBase.cs +++ b/src/ModVerify/Verifiers/GameVerifierBase.cs @@ -15,10 +15,8 @@ namespace AET.ModVerify.Verifiers; public abstract class GameVerifierBase : IGameVerifierInfo { public event EventHandler? Error; - public event EventHandler>? Progress; - private readonly IStarWarsGameEngine _gameEngine; private readonly ConcurrentDictionary _verifyErrors = new(); protected readonly IFileSystem FileSystem; @@ -35,7 +33,7 @@ public abstract class GameVerifierBase : IGameVerifierInfo protected IStarWarsGameEngine GameEngine { get; } - protected IGameRepository Repository => _gameEngine.GameRepository; + protected IGameRepository Repository => GameEngine.GameRepository; protected IReadOnlyList VerifierChain { get; } @@ -49,7 +47,6 @@ protected GameVerifierBase( throw new ArgumentNullException(nameof(serviceProvider)); FileSystem = serviceProvider.GetRequiredService(); Services = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - _gameEngine = gameEngine ?? throw new ArgumentNullException(nameof(gameEngine)); Parent = parent; Settings = settings ?? throw new ArgumentNullException(nameof(settings)); GameEngine = gameEngine ?? throw new ArgumentNullException(nameof(gameEngine)); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs index 3b77732..73aa400 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs @@ -1,7 +1,9 @@ -using System; +using AnakinRaW.CommonUtilities; +using System; using System.Collections.Generic; using System.Linq; -using AnakinRaW.CommonUtilities; +using System.Text; +using System.Xml.Linq; namespace PG.StarWarsGame.Engine; @@ -48,4 +50,19 @@ public GameLocations(IList modPaths, string gamePath, IList fall ? ModPaths[0] : GamePath; } + + public override string ToString() + { + var sb = new StringBuilder(); + + sb.AppendLine("GameLocation=["); + if (ModPaths.Count > 0) + sb.AppendLine($"Mods=[{string.Join(";", ModPaths)}];"); + sb.AppendLine($"Game=[{GamePath}];"); + if (FallbackPaths.Count > 0) + sb.AppendLine($"Fallbacks=[{string.Join(";", FallbackPaths)}];"); + sb.AppendLine("]"); + + return sb.ToString(); + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IGameEngineInitializationReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IGameEngineInitializationReporter.cs new file mode 100644 index 0000000..2ff43bd --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IGameEngineInitializationReporter.cs @@ -0,0 +1,10 @@ +namespace PG.StarWarsGame.Engine; + +public interface IGameEngineInitializationReporter +{ + void ReportProgress(string message); + + void ReportStarted(); + + void ReportFinished(); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IPetroglyphStarWarsGameEngineService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IPetroglyphStarWarsGameEngineService.cs index 314ce06..80aaa61 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IPetroglyphStarWarsGameEngineService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IPetroglyphStarWarsGameEngineService.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading; +using System.Threading; using System.Threading.Tasks; using PG.StarWarsGame.Engine.ErrorReporting; @@ -11,7 +10,7 @@ public Task InitializeAsync( GameEngineType engineType, GameLocations gameLocations, IGameEngineErrorReporter? errorReporter = null, - IProgress? initProgress = null, + IGameEngineInitializationReporter? initReporter = null, bool cancelOnInitializationError = false, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs index 8ce1a5a..02a8d9f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs @@ -27,7 +27,7 @@ public async Task InitializeAsync( GameEngineType engineType, GameLocations gameLocations, IGameEngineErrorReporter? errorReporter = null, - IProgress? progress = null, + IGameEngineInitializationReporter? initReporter = null, bool cancelOnInitializationError = false, CancellationToken cancellationToken = default) @@ -39,7 +39,7 @@ public async Task InitializeAsync( try { - return await InitializeEngine(engineType, gameLocations, errorListenerWrapper, progress, cts.Token) + return await InitializeEngineAsync(engineType, gameLocations, errorListenerWrapper, initReporter, cts.Token) .ConfigureAwait(false); } finally @@ -56,16 +56,17 @@ void OnInitializationError(object sender, InitializationError e) } } - private async Task InitializeEngine( + private async Task InitializeEngineAsync( GameEngineType engineType, GameLocations gameLocations, GameEngineErrorReporterWrapper errorReporter, - IProgress? progress, + IGameEngineInitializationReporter? initReporter, CancellationToken token) { try { _logger?.LogInformation("Initializing game engine for type '{GameEngineType}'.", engineType); + initReporter?.ReportStarted(); var repoFactory = _serviceProvider.GetRequiredService(); var repository = repoFactory.Create(engineType, gameLocations, errorReporter); @@ -73,7 +74,7 @@ private async Task InitializeEngine( var pgRender = new PGRender(repository, errorReporter, serviceProvider); var gameConstants = new GameConstants.GameConstants(repository, errorReporter, serviceProvider); - progress?.Report("Initializing GameConstants"); + initReporter?.ReportProgress("Initializing GameConstants"); await gameConstants.InitializeAsync(token); // AudioConstants @@ -81,23 +82,23 @@ private async Task InitializeEngine( // MousePointer var fontManger = new FontManager(repository, errorReporter, serviceProvider); - progress?.Report("Initializing FontManager"); + initReporter?.ReportProgress("Initializing FontManager"); await fontManger.InitializeAsync(token); var guiDialogs = new GuiDialogGameManager(repository, errorReporter, serviceProvider); - progress?.Report("Initializing GUIDialogManager"); + initReporter?.ReportProgress("Initializing GUIDialogManager"); await guiDialogs.InitializeAsync(token); var sfxGameManager = new SfxEventGameManager(repository, errorReporter, serviceProvider); - progress?.Report("Initializing SFXManager"); + initReporter?.ReportProgress("Initializing SFXManager"); await sfxGameManager.InitializeAsync(token); var commandBarManager = new CommandBarGameManager(repository, pgRender, gameConstants, fontManger, errorReporter, serviceProvider); - progress?.Report("Initializing CommandBar"); + initReporter?.ReportProgress("Initializing CommandBar"); await commandBarManager.InitializeAsync(token); var gameObjetTypeManager = new GameObjectTypeGameManager(repository, errorReporter, serviceProvider); - progress?.Report("Initializing GameObjectTypeManager"); + initReporter?.ReportProgress("Initializing GameObjectTypeManager"); await gameObjetTypeManager.InitializeAsync(token); token.ThrowIfCancellationRequested(); @@ -120,6 +121,7 @@ private async Task InitializeEngine( } finally { + initReporter?.ReportFinished(); _logger?.LogDebug("Finished initializing game database."); } } From b4829cb75fc93d81a0e26bb2b79c997cded1f444 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 13 Dec 2025 13:55:41 +0100 Subject: [PATCH 02/22] update deps --- .../ModSelectors/SettingsBasedModSelector.cs | 8 ++-- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 12 ++--- src/ModVerify.CliApp/ModVerifyApplication.cs | 20 ++++---- .../Reporting/BaselineSelector.cs | 17 ++++--- src/ModVerify/ModVerify.csproj | 4 +- .../Reporting/Json/JsonBaselineParser.cs | 11 ++--- .../Reporting/Json/JsonBaselineSchema.cs | 46 +++++++++++-------- 7 files changed, 61 insertions(+), 57 deletions(-) diff --git a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs index 221bb9e..f6538c5 100644 --- a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs @@ -9,7 +9,7 @@ namespace AET.ModVerify.App.ModSelectors; internal class SettingsBasedModSelector(IServiceProvider serviceProvider) { - public VerifyInstallationData CreateInstallationDataFromSettings(GameInstallationsSettings settings) + public VerificationTarget CreateInstallationDataFromSettings(GameInstallationsSettings settings) { var gameLocations = new ModSelectorFactory(serviceProvider) .CreateSelector(settings) @@ -21,10 +21,10 @@ public VerifyInstallationData CreateInstallationDataFromSettings(GameInstallatio if (engineType is null) throw new InvalidOperationException("Engine type not specified."); - return new VerifyInstallationData + return new VerificationTarget { - EngineType = engineType.Value, - GameLocations = gameLocations, + Engine = engineType.Value, + Location = gameLocations, Name = GetNameFromGameLocations(targetObject, gameLocations, engineType.Value) }; } diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 0073b2b..9874e8c 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -30,18 +30,18 @@ - + - - - - - + + + + + diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index 43f3632..754523b 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -68,10 +68,10 @@ private async Task RunCore() private async Task RunVerify() { - VerifyInstallationData installData; + VerificationTarget verificationTarget; try { - installData = new SettingsBasedModSelector(services) + verificationTarget = new SettingsBasedModSelector(services) .CreateInstallationDataFromSettings(settings.GameInstallationsSettings); } catch (GameNotFoundException ex) @@ -82,12 +82,12 @@ private async Task RunVerify() return ex.HResult; } - var reportSettings = CreateGlobalReportSettings(installData); + var reportSettings = CreateGlobalReportSettings(verificationTarget); - _logger?.LogDebug("Verify install data: {InstallData}", installData); + _logger?.LogDebug("Verification taget: {Target}", verificationTarget); _logger?.LogTrace("Verify settings: {Settings}", settings); - var allErrors = await Verify(installData, reportSettings) + var allErrors = await Verify(verificationTarget, reportSettings) .ConfigureAwait(false); try @@ -109,11 +109,11 @@ private async Task RunVerify() } private async Task> Verify( - VerifyInstallationData installData, + VerificationTarget verificationTarget, GlobalVerifyReportSettings reportSettings) { var initProgressReporter = new EngineInitializeProgressReporter(null); - var progressReporter = new VerifyConsoleProgressReporter(installData.Name); + var progressReporter = new VerifyConsoleProgressReporter(verificationTarget.Name); using var verifyPipeline = new NewGameVerifyPipeline( null, @@ -127,7 +127,7 @@ private async Task> Verify( { try { - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Verifying '{Target}'...", installData.Name); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Verifying '{Target}'...", verificationTarget.Name); await verifyPipeline.RunAsync().ConfigureAwait(false); progressReporter.Report(string.Empty, 1.0); } @@ -184,10 +184,10 @@ private async Task WriteBaseline( await baseline.ToJsonAsync(fs); } - private GlobalVerifyReportSettings CreateGlobalReportSettings(VerifyInstallationData installData) + private GlobalVerifyReportSettings CreateGlobalReportSettings(VerificationTarget verificationTarget) { var baselineSelector = new BaselineSelector(settings, services); - var baseline = baselineSelector.SelectBaseline(installData, out var baselinePath); + var baseline = baselineSelector.SelectBaseline(verificationTarget, out var baselinePath); if (baseline.Count > 0) _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Using baseline '{Baseline}'", baselinePath); diff --git a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs index 95953f1..9fa637f 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs @@ -1,5 +1,4 @@ -using AET.ModVerify.App.ModSelectors; -using AET.ModVerify.App.Resources.Baselines; +using AET.ModVerify.App.Resources.Baselines; using AET.ModVerify.App.Settings; using AET.ModVerify.Reporting; using AnakinRaW.ApplicationBase; @@ -16,7 +15,7 @@ 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, out string? usedBaselinePath) + public VerificationBaseline SelectBaseline(VerificationTarget verificationTarget, out string? usedBaselinePath) { var baselinePath = settings.ReportSettings.BaselinePath; if (!string.IsNullOrEmpty(baselinePath)) @@ -49,14 +48,14 @@ public VerificationBaseline SelectBaseline(VerifyInstallationData installationDa } if (settings.Interactive) - return FindBaselineInteractive(installationData, out usedBaselinePath); + return FindBaselineInteractive(verificationTarget, 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, out usedBaselinePath); + return FindBaselineNonInteractive(verificationTarget.Location.TargetPath, out usedBaselinePath); } - private VerificationBaseline FindBaselineInteractive(VerifyInstallationData installationData, out string? baselinePath) + private VerificationBaseline FindBaselineInteractive(VerificationTarget verificationTarget, 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. @@ -66,21 +65,21 @@ private VerificationBaseline FindBaselineInteractive(VerifyInstallationData inst _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Searching for local baseline files..."); - if (!_baselineFactory.TryCreateBaseline(installationData.GameLocations.TargetPath, out var baseline, + if (!_baselineFactory.TryCreateBaseline(verificationTarget.Location.TargetPath, out var 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) - if (installationData.GameLocations.ModPaths.Count == 0) + if (verificationTarget.Location.ModPaths.Count == 0) { _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "No local baseline file found."); return VerificationBaseline.Empty; } Console.WriteLine("No baseline found locally."); - return TryGetDefaultBaseline(installationData.EngineType, out baselinePath); + return TryGetDefaultBaseline(verificationTarget.Engine, out baselinePath); } } diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index 427458d..fe15b1c 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -35,8 +35,8 @@ - - + + diff --git a/src/ModVerify/Reporting/Json/JsonBaselineParser.cs b/src/ModVerify/Reporting/Json/JsonBaselineParser.cs index ef2f5d1..d3b4acd 100644 --- a/src/ModVerify/Reporting/Json/JsonBaselineParser.cs +++ b/src/ModVerify/Reporting/Json/JsonBaselineParser.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Text.Json; -using System.Text.Json.Nodes; namespace AET.ModVerify.Reporting.Json; @@ -13,7 +12,7 @@ public static VerificationBaseline Parse(Stream dataStream) throw new ArgumentNullException(nameof(dataStream)); try { - var jsonNode = JsonNode.Parse(dataStream); + var jsonNode = JsonDocument.Parse(dataStream); var jsonBaseline = ParseCore(jsonNode); if (jsonBaseline is null) @@ -27,12 +26,12 @@ public static VerificationBaseline Parse(Stream dataStream) } } - private static JsonVerificationBaseline? ParseCore(JsonNode? jsonData) + private static JsonVerificationBaseline? ParseCore(JsonDocument? json) { - if (jsonData is null) + if (json is null) return null; - JsonBaselineSchema.Evaluate(jsonData); - return jsonData.Deserialize(); + JsonBaselineSchema.Evaluate(json.RootElement); + return json.Deserialize(); } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Json/JsonBaselineSchema.cs b/src/ModVerify/Reporting/Json/JsonBaselineSchema.cs index 7c8b02a..12e3705 100644 --- a/src/ModVerify/Reporting/Json/JsonBaselineSchema.cs +++ b/src/ModVerify/Reporting/Json/JsonBaselineSchema.cs @@ -3,8 +3,9 @@ using System.Diagnostics; using System.Linq; using System.Text; -using System.Text.Json.Nodes; +using System.Text.Json; using Json.Schema; +using Json.Schema.Keywords; namespace AET.ModVerify.Reporting.Json; @@ -12,18 +13,20 @@ public static class JsonBaselineSchema { private static readonly JsonSchema Schema; private static readonly EvaluationOptions EvaluationOptions; - + private static readonly BuildOptions BuildOptions; + static JsonBaselineSchema() { - var evalvOptions = new EvaluationOptions + BuildOptions = new BuildOptions { - EvaluateAs = SpecVersion.Draft202012, - OutputFormat = OutputFormat.Hierarchical, - AllowReferencesIntoUnknownKeywords = false + Dialect = Dialect.Draft202012 }; Schema = GetCurrentSchema(); - EvaluationOptions = evalvOptions; + EvaluationOptions = new EvaluationOptions + { + OutputFormat = OutputFormat.Hierarchical + }; } /// @@ -31,11 +34,8 @@ static JsonBaselineSchema() /// /// The JSON node to evaluate. /// is not valid against the baseline JSON schema. - /// is . - public static void Evaluate(JsonNode json) + public static void Evaluate(JsonElement json) { - if (json == null) - throw new ArgumentNullException(nameof(json)); var result = Schema.Evaluate(json, EvaluationOptions); ThrowOnValidationError(result); } @@ -58,13 +58,17 @@ private static void ThrowOnValidationError(EvaluationResults result) private static KeyValuePair? GetFirstError(EvaluationResults result) { - if (result.HasErrors) - return result.Errors!.First(); - foreach (var child in result.Details) + if (result.Errors is not null) + return result.Errors.First(); + + if (result.Details is not null) { - var error = GetFirstError(child); - if (error is not null) - return error; + foreach (var child in result.Details) + { + var error = GetFirstError(child); + if (error is not null) + return error; + } } return null; } @@ -75,10 +79,12 @@ private static JsonSchema GetCurrentSchema() .Assembly.GetManifestResourceStream($"AET.ModVerify.Resources.Schemas.{GetVersionedPath()}.baseline.json"); Debug.Assert(resourceStream is not null); - var schema = JsonSchema.FromStream(resourceStream!).GetAwaiter().GetResult(); + var json = JsonDocument.Parse(resourceStream!).RootElement; + var schema = JsonSchema.Build(json, BuildOptions); + - var id = schema.GetId(); - if (id is null || !UriContainsVersion(id, VerificationBaseline.LatestVersionString)) + if (schema.Root.Keywords.FirstOrDefault(x => x.Handler is IdKeyword)?.Value is not Uri id + || !UriContainsVersion(id, VerificationBaseline.LatestVersionString)) throw new InvalidOperationException("Internal error: The embedded schema version does not match the expected baseline version!"); return schema; From 6b00425ad0cec566242fff66894e3fe494d21e8c Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 13 Dec 2025 13:55:46 +0100 Subject: [PATCH 03/22] update sub --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 479a088..da19380 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 479a088a2b26dd4a3e2342b2e34f5359b0252e88 +Subproject commit da19380d04632302b3806af778e3e6696e14ddc3 From e1b7832793bf3c57a1683811548621664e2285b9 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 13 Dec 2025 14:07:51 +0100 Subject: [PATCH 04/22] make app running again --- ModVerify.slnx | 2 +- src/ModVerify.CliApp/ModVerifyApplication.cs | 5 ++-- .../EngineInitializeProgressReporter.cs | 27 ++++++++----------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/ModVerify.slnx b/ModVerify.slnx index 3527ff4..ebbde63 100644 --- a/ModVerify.slnx +++ b/ModVerify.slnx @@ -17,12 +17,12 @@ + - diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index 754523b..44d494e 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -112,15 +112,14 @@ private async Task> Verify( VerificationTarget verificationTarget, GlobalVerifyReportSettings reportSettings) { - var initProgressReporter = new EngineInitializeProgressReporter(null); var progressReporter = new VerifyConsoleProgressReporter(verificationTarget.Name); using var verifyPipeline = new NewGameVerifyPipeline( - null, + verificationTarget, settings.VerifyPipelineSettings, reportSettings, progressReporter, - null, + new EngineInitializeProgressReporter(verificationTarget.Engine), services); try diff --git a/src/ModVerify.CliApp/Reporting/EngineInitializeProgressReporter.cs b/src/ModVerify.CliApp/Reporting/EngineInitializeProgressReporter.cs index b994e97..d93462f 100644 --- a/src/ModVerify.CliApp/Reporting/EngineInitializeProgressReporter.cs +++ b/src/ModVerify.CliApp/Reporting/EngineInitializeProgressReporter.cs @@ -1,30 +1,25 @@ using System; +using PG.StarWarsGame.Engine; namespace AET.ModVerify.App.Reporting; -internal sealed class EngineInitializeProgressReporter : IDisposable -{ - private Progress? _progress; - - public EngineInitializeProgressReporter(Progress? progress) +internal sealed class EngineInitializeProgressReporter(GameEngineType engine) : IGameEngineInitializationReporter +{ + public void ReportProgress(string message) { - if (progress is null) - return; - progress.ProgressChanged += OnProgress; + Console.ForegroundColor = ConsoleColor.DarkGray; + Console.WriteLine(message); + Console.ResetColor(); } - private void OnProgress(object sender, string e) + public void ReportStarted() { - Console.ForegroundColor = ConsoleColor.DarkGray; - Console.WriteLine(e); - Console.ResetColor(); + Console.WriteLine($"Initializing game engine '{engine}'..."); } - public void Dispose() + public void ReportFinished() { + Console.WriteLine($"Game engine initialized."); Console.WriteLine(); - if (_progress is not null) - _progress.ProgressChanged -= OnProgress; - _progress = null; } } \ No newline at end of file From 2772bb5098566e11c16b11cd00ac16a7125b0f2e Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 13 Dec 2025 18:45:47 +0100 Subject: [PATCH 05/22] reorganize solution --- ModVerify.slnx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ModVerify.slnx b/ModVerify.slnx index ebbde63..3527ff4 100644 --- a/ModVerify.slnx +++ b/ModVerify.slnx @@ -17,12 +17,12 @@ - + From fdd9e38d944a178a89052b983977ef1da2e7a707 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 13 Dec 2025 18:46:36 +0100 Subject: [PATCH 06/22] basic support for VerificationTarget type --- modules/ModdingToolBase | 2 +- .../ModSelectors/AutomaticModSelector.cs | 2 +- .../ModSelectors/ConsoleModSelector.cs | 4 +- .../ModSelectors/ManualModSelector.cs | 5 +- .../ModSelectors/ModSelectorBase.cs | 2 +- .../ModSelectors/SettingsBasedModSelector.cs | 11 +- src/ModVerify.CliApp/ModVerifyApplication.cs | 5 +- .../Resources/Baselines/baseline-foc.json | 2045 ++++++++++++----- src/ModVerify/ModVerify.csproj | 4 +- .../Reporting/Json/JsonGameLocation.cs | 38 + .../Json/JsonVerificationBaseline.cs | 12 +- .../Reporting/Json/JsonVerificationTarget.cs | 51 + .../Reporting/VerificationBaseline.cs | 17 +- .../Schemas/{2.0 => 2.1}/baseline.json | 56 +- src/ModVerify/VerificationTarget.cs | 4 +- .../PG.StarWarsGame.Engine/GameLocations.cs | 7 +- 16 files changed, 1703 insertions(+), 562 deletions(-) create mode 100644 src/ModVerify/Reporting/Json/JsonGameLocation.cs create mode 100644 src/ModVerify/Reporting/Json/JsonVerificationTarget.cs rename src/ModVerify/Resources/Schemas/{2.0 => 2.1}/baseline.json (56%) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index da19380..a0ad12e 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit da19380d04632302b3806af778e3e6696e14ddc3 +Subproject commit a0ad12e651dcb92cd4f1067059824d8f2894c63f diff --git a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs index 717db7b..d12cd6b 100644 --- a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs @@ -39,7 +39,7 @@ internal class AutomaticModSelector(IServiceProvider serviceProvider) : ModSelec catch (GameNotFoundException) { Logger?.LogError(ModVerifyConstants.ConsoleEventId, "Unable to find games based of the given location '{SettingsGamePath}'. Consider specifying all paths manually.", settings.GamePath); - targetObject = null!; + targetObject = null; return null; } diff --git a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs index c776d6d..a04cbea 100644 --- a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs @@ -14,7 +14,9 @@ namespace AET.ModVerify.App.ModSelectors; internal class ConsoleModSelector(IServiceProvider serviceProvider) : ModSelectorBase(serviceProvider) { - public override GameLocations Select(GameInstallationsSettings settings, out IPhysicalPlayableObject targetObject, + public override GameLocations Select( + GameInstallationsSettings settings, + out IPhysicalPlayableObject targetObject, out GameEngineType? actualEngineType) { var gameResult = GameFinderService.FindGames(); diff --git a/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs index 34cf39d..a8b5ab7 100644 --- a/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using AET.ModVerify.App.Settings; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; @@ -22,8 +23,8 @@ public override GameLocations Select( throw new ArgumentException("Argument --game must be set."); return new GameLocations( - settings.ModPaths, + settings.ModPaths.ToList(), settings.GamePath!, - GetFallbackPaths(settings.FallbackGamePath, settings.AdditionalFallbackPaths)); + GetFallbackPaths(settings.FallbackGamePath, settings.AdditionalFallbackPaths).ToList()); } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs b/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs index 8dd1d90..53b4b20 100644 --- a/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs +++ b/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs @@ -34,7 +34,7 @@ protected GameLocations GetLocations(IPhysicalPlayableObject playableObject, Gam { var fallbacks = GetFallbackPaths(finderResult, playableObject, additionalFallbackPaths); var modPaths = GetModPaths(playableObject); - return new GameLocations(modPaths, playableObject.Game.Directory.FullName, fallbacks); + return new GameLocations(modPaths.ToList(), playableObject.Game.Directory.FullName, fallbacks.ToList()); } private static IList GetFallbackPaths(GameFinderResult finderResult, IPlayableObject gameOrMod, IList additionalFallbackPaths) diff --git a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs index f6538c5..8ff890a 100644 --- a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs @@ -25,11 +25,12 @@ public VerificationTarget CreateInstallationDataFromSettings(GameInstallationsSe { Engine = engineType.Value, Location = gameLocations, - Name = GetNameFromGameLocations(targetObject, gameLocations, engineType.Value) + Name = GetNameFromGameLocations(targetObject, gameLocations), + Version = GetTargetVersion(targetObject) }; } - private static string GetNameFromGameLocations(IPlayableObject? targetObject, GameLocations gameLocations, GameEngineType engineType) + private static string GetNameFromGameLocations(IPlayableObject? targetObject, GameLocations gameLocations) { if (targetObject is not null) return targetObject.Name; @@ -37,4 +38,10 @@ private static string GetNameFromGameLocations(IPlayableObject? targetObject, Ga var mod = gameLocations.ModPaths.FirstOrDefault(); return mod ?? gameLocations.GamePath; } + + private static string? GetTargetVersion(IPlayableObject? targetObject) + { + // TODO: Implement version retrieval from targetObject if possible + return null; + } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index 44d494e..cd59804 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -102,7 +102,7 @@ private async Task RunVerify() if (!settings.CreateNewBaseline) return 0; - await WriteBaseline(reportSettings, allErrors, settings.NewBaselinePath).ConfigureAwait(false); + await WriteBaseline(verificationTarget, reportSettings, allErrors, settings.NewBaselinePath).ConfigureAwait(false); _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Baseline successfully created."); return 0; @@ -167,11 +167,12 @@ private async Task ReportErrors(IReadOnlyCollection errors) } private async Task WriteBaseline( + VerificationTarget target, GlobalVerifyReportSettings reportSettings, IEnumerable errors, string baselineFile) { - var baseline = new VerificationBaseline(reportSettings.MinimumReportSeverity, errors); + var baseline = new VerificationBaseline(reportSettings.MinimumReportSeverity, errors, target); var fullPath = _fileSystem.Path.GetFullPath(baselineFile); _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Writing Baseline to '{FullPath}'", fullPath); diff --git a/src/ModVerify.CliApp/Resources/Baselines/baseline-foc.json b/src/ModVerify.CliApp/Resources/Baselines/baseline-foc.json index c94d121..2cb5cac 100644 --- a/src/ModVerify.CliApp/Resources/Baselines/baseline-foc.json +++ b/src/ModVerify.CliApp/Resources/Baselines/baseline-foc.json @@ -1,5 +1,16 @@ { - "version": "2.0", + "version": "2.1", + "target": { + "name": "Forces of Corruption (SteamGold)", + "engine": "Foc", + "location": { + "modPaths": [], + "gamePath": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Star Wars Empire at War\\corruption", + "fallbackPaths": [ + "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Star Wars Empire at War\\GameData" + ] + } + }, "minSeverity": "Information", "errors": [ { @@ -36,12 +47,10 @@ "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.", + "message": "Unable to find .ALO file \u0027CIN_Reb_CelebHall.alo\u0027", "severity": "Error", - "context": [ - "DATA\\ART\\MODELS\\UV_SKIPRAY.ALO" - ], - "asset": "Default.fx" + "context": [], + "asset": "CIN_Reb_CelebHall.alo" }, { "id": "FILE00", @@ -49,36 +58,26 @@ "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", + "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\\NB_PRISON.ALO" - ], - "asset": "p_smoke_small_thin2" - }, - { - "id": "FILE00", - "verifiers": [ - "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + "DATA\\ART\\MODELS\\UV_ECLIPSE_UC_DC.ALO" ], - "message": "Unable to find .ALO file \u0027W_Kamino_Reflect.ALO\u0027", - "severity": "Error", - "context": [], - "asset": "W_Kamino_Reflect.ALO" + "asset": "p_ssd_debris" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" ], - "message": "Proxy particle \u0027p_ssd_debris\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_ECLIPSE_UC_DC.ALO\u0027", + "message": "Could not find texture \u0027Cin_Reb_CelebHall_Wall.tga\u0027 for context: [W_SITH_LEFTHALL.ALO].", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\UV_ECLIPSE_UC_DC.ALO" + "W_SITH_LEFTHALL.ALO" ], - "asset": "p_ssd_debris" + "asset": "Cin_Reb_CelebHall_Wall.tga" }, { "id": "FILE00", @@ -86,10 +85,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_p_proton_torpedo.alo\u0027", + "message": "Unable to find .ALO file \u0027Cin_ImperialCraft.alo\u0027", "severity": "Error", "context": [], - "asset": "CIN_p_proton_torpedo.alo" + "asset": "Cin_ImperialCraft.alo" }, { "id": "FILE00", @@ -97,10 +96,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_DStar_LeverPanel.alo\u0027", + "message": "Unable to find .ALO file \u0027Cin_Officer.alo\u0027", "severity": "Error", "context": [], - "asset": "Cin_DStar_LeverPanel.alo" + "asset": "Cin_Officer.alo" }, { "id": "FILE00", @@ -108,12 +107,10 @@ "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", + "message": "Unable to find .ALO file \u0027Cin_DStar_protons.alo\u0027", "severity": "Error", - "context": [ - "DATA\\ART\\MODELS\\UV_ECLIPSE.ALO" - ], - "asset": "lookat" + "context": [], + "asset": "Cin_DStar_protons.alo" }, { "id": "FILE00", @@ -122,12 +119,12 @@ "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", "AET.ModVerify.Verifiers.Commons.TextureVeifier" ], - "message": "Could not find texture \u0027Cin_DeathStar.tga\u0027 for context: [ALTTEST.ALO].", + "message": "Could not find texture \u0027w_grenade.tga\u0027 for context: [W_GRENADE.ALO].", "severity": "Error", "context": [ - "ALTTEST.ALO" + "W_GRENADE.ALO" ], - "asset": "Cin_DeathStar.tga" + "asset": "w_grenade.tga" }, { "id": "FILE00", @@ -135,12 +132,12 @@ "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", + "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\\NB_PRISON.ALO" + "DATA\\ART\\MODELS\\UB_03_STATION_D.ALO" ], - "asset": "p_prison_light" + "asset": "p_uwstation_death" }, { "id": "FILE00", @@ -148,26 +145,21 @@ "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.", + "message": "Unable to find .ALO file \u0027Cin_EI_Vader.alo\u0027", "severity": "Error", - "context": [ - "DATA\\ART\\MODELS\\EV_MDU_SENSORNODE.ALO" - ], - "asset": "Default.fx" + "context": [], + "asset": "Cin_EI_Vader.alo" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", - "AET.ModVerify.Verifiers.Commons.TextureVeifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Could not find texture \u0027w_grenade.tga\u0027 for context: [W_GRENADE.ALO].", + "message": "Unable to find .ALO file \u0027MODELS\u0027", "severity": "Error", - "context": [ - "W_GRENADE.ALO" - ], - "asset": "w_grenade.tga" + "context": [], + "asset": "MODELS" }, { "id": "FILE00", @@ -175,10 +167,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_Rbel_NavyRow.alo\u0027", + "message": "Unable to find .ALO file \u0027Cin_DeathStar_Wall.alo\u0027", "severity": "Error", "context": [], - "asset": "CIN_Rbel_NavyRow.alo" + "asset": "Cin_DeathStar_Wall.alo" }, { "id": "FILE00", @@ -186,10 +178,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_Planet_Alderaan_High.alo\u0027", + "message": "Proxy particle \u0027p_smoke_small_thin2\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_PRISON.ALO\u0027", "severity": "Error", - "context": [], - "asset": "Cin_Planet_Alderaan_High.alo" + "context": [ + "DATA\\ART\\MODELS\\NB_PRISON.ALO" + ], + "asset": "p_smoke_small_thin2" }, { "id": "FILE00", @@ -197,12 +191,12 @@ "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", + "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\\UV_ECLIPSE_UC.ALO" + "DATA\\ART\\MODELS\\UB_01_STATION_D.ALO" ], - "asset": "lookat" + "asset": "p_uwstation_death" }, { "id": "FILE00", @@ -210,12 +204,23 @@ "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", + "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_MEDIUM.ALO\u0027", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\EI_MARAJADE.ALO" + "DATA\\ART\\MODELS\\W_STARS_MEDIUM.ALO" ], - "asset": "p_desert_ground_dust" + "asset": "Lensflare0" }, { "id": "FILE00", @@ -223,10 +228,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027p_splash_wake_lava.alo\u0027", + "message": "Unable to find .ALO file \u0027CIN_DeathStar_Hangar.alo\u0027", "severity": "Error", "context": [], - "asset": "p_splash_wake_lava.alo" + "asset": "CIN_DeathStar_Hangar.alo" }, { "id": "FILE00", @@ -234,10 +239,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_rv_XWingProp.alo\u0027", + "message": "Unable to find .ALO file \u0027Cin_EV_lambdaShuttle_150.alo\u0027", "severity": "Error", "context": [], - "asset": "Cin_rv_XWingProp.alo" + "asset": "Cin_EV_lambdaShuttle_150.alo" }, { "id": "FILE00", @@ -245,10 +250,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_Fire_Huge.alo\u0027", + "message": "Proxy particle \u0027p_smoke_small_thin4\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_PRISON.ALO\u0027", "severity": "Error", - "context": [], - "asset": "CIN_Fire_Huge.alo" + "context": [ + "DATA\\ART\\MODELS\\NB_PRISON.ALO" + ], + "asset": "p_smoke_small_thin4" }, { "id": "FILE00", @@ -256,10 +263,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_Probe_Droid.alo\u0027", + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_CINE.ALO\u0027", "severity": "Error", - "context": [], - "asset": "CIN_Probe_Droid.alo" + "context": [ + "DATA\\ART\\MODELS\\W_STARS_CINE.ALO" + ], + "asset": "Lensflare0" }, { "id": "FILE00", @@ -267,12 +276,12 @@ "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", + "message": "Shader effect \u0027Default.fx\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_SKIPRAY.ALO\u0027.", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\UB_05_STATION_D.ALO" + "DATA\\ART\\MODELS\\UV_SKIPRAY.ALO" ], - "asset": "p_uwstation_death" + "asset": "Default.fx" }, { "id": "FILE00", @@ -280,10 +289,10 @@ "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", + "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\\RI_KYLEKATARN.ALO" + "DATA\\ART\\MODELS\\UI_IG88.ALO" ], "asset": "p_desert_ground_dust" }, @@ -293,26 +302,34 @@ "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", + "message": "Unable to find .ALO file \u0027CIN_p_proton_torpedo.alo\u0027", "severity": "Error", - "context": [ - "DATA\\ART\\MODELS\\RB_HEAVYVEHICLEFACTORY.ALO" + "context": [], + "asset": "CIN_p_proton_torpedo.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "asset": "p_steam_small" + "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", - "AET.ModVerify.Verifiers.Commons.TextureVeifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Could not find texture \u0027p_particle_master\u0027 for context: [P_DIRT_EMITTER_TEST1.ALO].", + "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_04_STATION_D.ALO\u0027", "severity": "Error", "context": [ - "P_DIRT_EMITTER_TEST1.ALO" + "DATA\\ART\\MODELS\\UB_04_STATION_D.ALO" ], - "asset": "p_particle_master" + "asset": "p_uwstation_death" }, { "id": "FILE00", @@ -320,12 +337,23 @@ "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", + "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": "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\\NB_PRISON.ALO" + "DATA\\ART\\MODELS\\UB_02_STATION_D.ALO" ], - "asset": "p_smoke_small_thin4" + "asset": "p_uwstation_death" }, { "id": "FILE00", @@ -333,10 +361,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_EI_Vader.alo\u0027", + "message": "Unable to find .ALO file \u0027W_Kamino_Reflect.ALO\u0027", "severity": "Error", "context": [], - "asset": "Cin_EI_Vader.alo" + "asset": "W_Kamino_Reflect.ALO" }, { "id": "FILE00", @@ -344,10 +372,10 @@ "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", + "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\\RB_HYPERVELOCITYGUN.ALO" + "DATA\\ART\\MODELS\\NB_MONCAL_BUILDING.ALO" ], "asset": "p_smoke_small_thin2" }, @@ -357,12 +385,12 @@ "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.", + "message": "Proxy particle \u0027p_steam_small\u0027 not found for model \u0027DATA\\ART\\MODELS\\RB_HEAVYVEHICLEFACTORY.ALO\u0027", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\EV_TIE_LANCET.ALO" + "DATA\\ART\\MODELS\\RB_HEAVYVEHICLEFACTORY.ALO" ], - "asset": "Default.fx" + "asset": "p_steam_small" }, { "id": "FILE00", @@ -370,10 +398,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_DeathStar_Wall.alo\u0027", + "message": "Unable to find .ALO file \u0027Cin_EV_Stardestroyer_Warp.alo\u0027", "severity": "Error", "context": [], - "asset": "Cin_DeathStar_Wall.alo" + "asset": "Cin_EV_Stardestroyer_Warp.alo" }, { "id": "FILE00", @@ -381,10 +409,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027W_droid_steam.alo\u0027", + "message": "Unable to find .ALO file \u0027Cin_DStar_TurretLasers.alo\u0027", "severity": "Error", "context": [], - "asset": "W_droid_steam.alo" + "asset": "Cin_DStar_TurretLasers.alo" }, { "id": "FILE00", @@ -392,10 +420,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_DeathStar_High.alo\u0027", + "message": "Unable to find .ALO file \u0027CIN_Rbel_GreyGroup.alo\u0027", "severity": "Error", "context": [], - "asset": "Cin_DeathStar_High.alo" + "asset": "CIN_Rbel_GreyGroup.alo" }, { "id": "FILE00", @@ -403,10 +431,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027MODELS\u0027", + "message": "Unable to find .ALO file \u0027Cin_Planet_Hoth_High.alo\u0027", "severity": "Error", "context": [], - "asset": "MODELS" + "asset": "Cin_Planet_Hoth_High.alo" }, { "id": "FILE00", @@ -414,10 +442,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027W_AllShaders.ALO\u0027", + "message": "Unable to find .ALO file \u0027CIN_Trooper_Row.alo\u0027", "severity": "Error", "context": [], - "asset": "W_AllShaders.ALO" + "asset": "CIN_Trooper_Row.alo" }, { "id": "FILE00", @@ -425,12 +453,12 @@ "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", + "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\\W_THERMAL_DETONATOR_EMPIRE.ALO" + "DATA\\ART\\MODELS\\UI_SABOTEUR.ALO" ], - "asset": "p_bomb_spin" + "asset": "p_desert_ground_dust" }, { "id": "FILE00", @@ -438,12 +466,12 @@ "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", + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_CINE_LUA.ALO\u0027", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\UB_03_STATION_D.ALO" + "DATA\\ART\\MODELS\\W_STARS_CINE_LUA.ALO" ], - "asset": "p_uwstation_death" + "asset": "Lensflare0" }, { "id": "FILE00", @@ -451,10 +479,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_Rbel_GreyGroup.alo\u0027", + "message": "Unable to find .ALO file \u0027W_AllShaders.ALO\u0027", "severity": "Error", "context": [], - "asset": "CIN_Rbel_GreyGroup.alo" + "asset": "W_AllShaders.ALO" }, { "id": "FILE00", @@ -462,12 +490,12 @@ "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", + "message": "Shader effect \u0027Default.fx\u0027 not found for model \u0027DATA\\ART\\MODELS\\EV_TIE_LANCET.ALO\u0027.", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\NB_SCH.ALO" + "DATA\\ART\\MODELS\\EV_TIE_LANCET.ALO" ], - "asset": "p_cold_tiny01" + "asset": "Default.fx" }, { "id": "FILE00", @@ -486,34 +514,38 @@ "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", + "message": "Unable to find .ALO file \u0027Cin_EI_Palpatine.alo\u0027", "severity": "Error", - "context": [ - "DATA\\ART\\MODELS\\EV_ARCHAMMER.ALO" - ], - "asset": "p_hp_archammer-damage" + "context": [], + "asset": "Cin_EI_Palpatine.alo" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" ], - "message": "Unable to find .ALO file \u0027CIN_Rbel_grey.alo\u0027", + "message": "Could not find texture \u0027Cin_DeathStar.tga\u0027 for context: [ALTTEST.ALO].", "severity": "Error", - "context": [], - "asset": "CIN_Rbel_grey.alo" + "context": [ + "ALTTEST.ALO" + ], + "asset": "Cin_DeathStar.tga" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" ], - "message": "Unable to find .ALO file \u0027CIN_Reb_CelebHall.alo\u0027", + "message": "Could not find texture \u0027UB_girder_B.tga\u0027 for context: [UV_MDU_CAGE.ALO].", "severity": "Error", - "context": [], - "asset": "CIN_Reb_CelebHall.alo" + "context": [ + "UV_MDU_CAGE.ALO" + ], + "asset": "UB_girder_B.tga" }, { "id": "FILE00", @@ -534,10 +566,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_ImperialCraft.alo\u0027", + "message": "Shader effect \u0027Default.fx\u0027 not found for model \u0027DATA\\ART\\MODELS\\EV_MDU_SENSORNODE.ALO\u0027.", "severity": "Error", - "context": [], - "asset": "Cin_ImperialCraft.alo" + "context": [ + "DATA\\ART\\MODELS\\EV_MDU_SENSORNODE.ALO" + ], + "asset": "Default.fx" }, { "id": "FILE00", @@ -545,10 +579,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_DStar_Dish_close.alo\u0027", + "message": "Unable to find .ALO file \u0027Cin_Planet_Alderaan_High.alo\u0027", "severity": "Error", "context": [], - "asset": "Cin_DStar_Dish_close.alo" + "asset": "Cin_Planet_Alderaan_High.alo" }, { "id": "FILE00", @@ -556,12 +590,12 @@ "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", + "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\\RV_BWING.ALO" + "DATA\\ART\\MODELS\\EV_ARCHAMMER.ALO" ], - "asset": "pe_bwing_yellow" + "asset": "p_hp_archammer-damage" }, { "id": "FILE00", @@ -569,10 +603,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_bridge.alo\u0027", + "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_05_STATION_D.ALO\u0027", "severity": "Error", - "context": [], - "asset": "Cin_bridge.alo" + "context": [ + "DATA\\ART\\MODELS\\UB_05_STATION_D.ALO" + ], + "asset": "p_uwstation_death" }, { "id": "FILE00", @@ -580,12 +616,12 @@ "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", + "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\\UI_SABOTEUR.ALO" + "DATA\\ART\\MODELS\\NB_NOGHRI_HUT.ALO" ], - "asset": "p_desert_ground_dust" + "asset": "p_explosion_smoke_small_thin5" }, { "id": "FILE00", @@ -593,10 +629,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_Trooper_Row.alo\u0027", + "message": "Unable to find .ALO file \u0027CIN_Probe_Droid.alo\u0027", "severity": "Error", "context": [], - "asset": "CIN_Trooper_Row.alo" + "asset": "CIN_Probe_Droid.alo" }, { "id": "FILE00", @@ -604,10 +640,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_EV_TieAdvanced.alo\u0027", + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_HIGH.ALO\u0027", "severity": "Error", - "context": [], - "asset": "Cin_EV_TieAdvanced.alo" + "context": [ + "DATA\\ART\\MODELS\\W_STARS_HIGH.ALO" + ], + "asset": "Lensflare0" }, { "id": "FILE00", @@ -615,38 +653,58 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027w_sith_arch.alo\u0027", + "message": "Unable to find .ALO file \u0027W_Volcano_Rock02.ALO\u0027", "severity": "Error", "context": [], - "asset": "w_sith_arch.alo" + "asset": "W_Volcano_Rock02.ALO" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", - "AET.ModVerify.Verifiers.Commons.TextureVeifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Could not find texture \u0027NB_YsalamiriTree_B.tga\u0027 for context: [UV_MDU_CAGE.ALO].", + "message": "Proxy particle \u0027lookat\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_ECLIPSE.ALO\u0027", "severity": "Error", "context": [ - "UV_MDU_CAGE.ALO" + "DATA\\ART\\MODELS\\UV_ECLIPSE.ALO" ], - "asset": "NB_YsalamiriTree_B.tga" + "asset": "lookat" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", - "AET.ModVerify.Verifiers.Commons.TextureVeifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Could not find texture \u0027W_TE_Rock_f_02_b.tga\u0027 for context: [EV_TIE_PHANTOM.ALO].", + "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 \u0027W_SwampGasEmit.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_SwampGasEmit.ALO" + }, + { + "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": [ - "EV_TIE_PHANTOM.ALO" + "DATA\\ART\\MODELS\\NB_VCH.ALO" ], - "asset": "W_TE_Rock_f_02_b.tga" + "asset": "P_heat_small01" }, { "id": "FILE00", @@ -654,10 +712,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_EI_Palpatine.alo\u0027", + "message": "Unable to find .ALO file \u0027CIN_Rbel_NavyRow.alo\u0027", "severity": "Error", "context": [], - "asset": "Cin_EI_Palpatine.alo" + "asset": "CIN_Rbel_NavyRow.alo" }, { "id": "FILE00", @@ -665,10 +723,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_Rbel_Soldier.alo\u0027", + "message": "Unable to find .ALO file \u0027CIN_Fire_Medium.alo\u0027", "severity": "Error", "context": [], - "asset": "CIN_Rbel_Soldier.alo" + "asset": "CIN_Fire_Medium.alo" }, { "id": "FILE00", @@ -676,12 +734,12 @@ "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", + "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\\NB_MONCAL_BUILDING.ALO" + "DATA\\ART\\MODELS\\UI_EWOK_HANDLER.ALO" ], - "asset": "p_smoke_small_thin2" + "asset": "p_ewok_drag_dirt" }, { "id": "FILE00", @@ -700,10 +758,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_Officer_Row.alo\u0027", + "message": "Unable to find .ALO file \u0027W_droid_steam.alo\u0027", "severity": "Error", "context": [], - "asset": "CIN_Officer_Row.alo" + "asset": "W_droid_steam.alo" }, { "id": "FILE00", @@ -711,26 +769,21 @@ "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", + "message": "Unable to find .ALO file \u0027CIN_Biker_Row.alo\u0027", "severity": "Error", - "context": [ - "DATA\\ART\\MODELS\\W_STARS_HIGH.ALO" - ], - "asset": "Lensflare0" + "context": [], + "asset": "CIN_Biker_Row.alo" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", - "AET.ModVerify.Verifiers.Commons.TextureVeifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Could not find texture \u0027Cin_Reb_CelebHall_Wall_B.tga\u0027 for context: [W_SITH_LEFTHALL.ALO].", + "message": "Unable to find .ALO file \u0027w_planet_volcanic.alo\u0027", "severity": "Error", - "context": [ - "W_SITH_LEFTHALL.ALO" - ], - "asset": "Cin_Reb_CelebHall_Wall_B.tga" + "context": [], + "asset": "w_planet_volcanic.alo" }, { "id": "FILE00", @@ -738,10 +791,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_Coruscant.alo\u0027", + "message": "Proxy particle \u0027p_smoke_small_thin2\u0027 not found for model \u0027DATA\\ART\\MODELS\\RB_HYPERVELOCITYGUN.ALO\u0027", "severity": "Error", - "context": [], - "asset": "Cin_Coruscant.alo" + "context": [ + "DATA\\ART\\MODELS\\RB_HYPERVELOCITYGUN.ALO" + ], + "asset": "p_smoke_small_thin2" }, { "id": "FILE00", @@ -749,12 +804,12 @@ "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", + "message": "Proxy particle \u0027lookat\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_ECLIPSE_UC.ALO\u0027", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\UI_EWOK_HANDLER.ALO" + "DATA\\ART\\MODELS\\UV_ECLIPSE_UC.ALO" ], - "asset": "p_ewok_drag_dirt" + "asset": "lookat" }, { "id": "FILE00", @@ -762,12 +817,10 @@ "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", + "message": "Unable to find .ALO file \u0027CIN_REb_CelebCharacters.alo\u0027", "severity": "Error", - "context": [ - "DATA\\ART\\MODELS\\NB_VCH.ALO" - ], - "asset": "P_heat_small01" + "context": [], + "asset": "CIN_REb_CelebCharacters.alo" }, { "id": "FILE00", @@ -775,36 +828,38 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027W_Vol_Steam01.ALO\u0027", + "message": "Unable to find .ALO file \u0027Cin_DeathStar_High.alo\u0027", "severity": "Error", "context": [], - "asset": "W_Vol_Steam01.ALO" + "asset": "Cin_DeathStar_High.alo" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" ], - "message": "Proxy particle \u0027p_explosion_smoke_small_thin5\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_NOGHRI_HUT.ALO\u0027", + "message": "Could not find texture \u0027Cin_Reb_CelebHall_Wall_B.tga\u0027 for context: [W_SITH_LEFTHALL.ALO].", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\NB_NOGHRI_HUT.ALO" + "W_SITH_LEFTHALL.ALO" ], - "asset": "p_explosion_smoke_small_thin5" + "asset": "Cin_Reb_CelebHall_Wall_B.tga" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" ], - "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_MEDIUM.ALO\u0027", + "message": "Could not find texture \u0027NB_YsalamiriTree_B.tga\u0027 for context: [UV_MDU_CAGE.ALO].", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\W_STARS_MEDIUM.ALO" + "UV_MDU_CAGE.ALO" ], - "asset": "Lensflare0" + "asset": "NB_YsalamiriTree_B.tga" }, { "id": "FILE00", @@ -812,12 +867,10 @@ "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", + "message": "Unable to find .ALO file \u0027Cin_Coruscant.alo\u0027", "severity": "Error", - "context": [ - "DATA\\ART\\MODELS\\UB_02_STATION_D.ALO" - ], - "asset": "p_uwstation_death" + "context": [], + "asset": "Cin_Coruscant.alo" }, { "id": "FILE00", @@ -825,10 +878,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_Officer.alo\u0027", + "message": "Proxy particle \u0027p_prison_light\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_PRISON.ALO\u0027", "severity": "Error", - "context": [], - "asset": "Cin_Officer.alo" + "context": [ + "DATA\\ART\\MODELS\\NB_PRISON.ALO" + ], + "asset": "p_prison_light" }, { "id": "FILE00", @@ -836,12 +891,12 @@ "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", + "message": "Proxy particle \u0027p_cold_tiny01\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_SCH.ALO\u0027", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\UB_04_STATION_D.ALO" + "DATA\\ART\\MODELS\\NB_SCH.ALO" ], - "asset": "p_uwstation_death" + "asset": "p_cold_tiny01" }, { "id": "FILE00", @@ -849,10 +904,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_Lambda_Head.alo\u0027", + "message": "Unable to find .ALO file \u0027CIN_NavyTrooper_Row.alo\u0027", "severity": "Error", "context": [], - "asset": "CIN_Lambda_Head.alo" + "asset": "CIN_NavyTrooper_Row.alo" }, { "id": "FILE00", @@ -860,10 +915,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_Biker_Row.alo\u0027", + "message": "Shader effect \u0027Default.fx\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_CRUSADERCLASSCORVETTE.ALO\u0027.", "severity": "Error", - "context": [], - "asset": "CIN_Biker_Row.alo" + "context": [ + "DATA\\ART\\MODELS\\UV_CRUSADERCLASSCORVETTE.ALO" + ], + "asset": "Default.fx" }, { "id": "FILE00", @@ -871,10 +928,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_DStar_protons.alo\u0027", + "message": "Unable to find .ALO file \u0027CIN_Rbel_Soldier.alo\u0027", "severity": "Error", "context": [], - "asset": "Cin_DStar_protons.alo" + "asset": "CIN_Rbel_Soldier.alo" }, { "id": "FILE00", @@ -882,10 +939,10 @@ "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", + "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\\UI_IG88.ALO" + "DATA\\ART\\MODELS\\RI_KYLEKATARN.ALO" ], "asset": "p_desert_ground_dust" }, @@ -904,12 +961,15 @@ "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" ], - "message": "Unable to find .ALO file \u0027Cin_Planet_Hoth_High.alo\u0027", + "message": "Could not find texture \u0027p_particle_master\u0027 for context: [P_DIRT_EMITTER_TEST1.ALO].", "severity": "Error", - "context": [], - "asset": "Cin_Planet_Hoth_High.alo" + "context": [ + "P_DIRT_EMITTER_TEST1.ALO" + ], + "asset": "p_particle_master" }, { "id": "FILE00", @@ -917,12 +977,10 @@ "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", + "message": "Unable to find .ALO file \u0027Cin_bridge.alo\u0027", "severity": "Error", - "context": [ - "DATA\\ART\\MODELS\\W_STARS_LOW.ALO" - ], - "asset": "Lensflare0" + "context": [], + "asset": "Cin_bridge.alo" }, { "id": "FILE00", @@ -930,10 +988,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_NavyTrooper_Row.alo\u0027", + "message": "Unable to find .ALO file \u0027W_Vol_Steam01.ALO\u0027", "severity": "Error", "context": [], - "asset": "CIN_NavyTrooper_Row.alo" + "asset": "W_Vol_Steam01.ALO" }, { "id": "FILE00", @@ -941,10 +999,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_DStar_TurretLasers.alo\u0027", + "message": "Unable to find .ALO file \u0027CIN_Rbel_grey.alo\u0027", "severity": "Error", "context": [], - "asset": "Cin_DStar_TurretLasers.alo" + "asset": "CIN_Rbel_grey.alo" }, { "id": "FILE00", @@ -952,10 +1010,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027W_SwampGasEmit.ALO\u0027", + "message": "Unable to find .ALO file \u0027w_sith_arch.alo\u0027", "severity": "Error", "context": [], - "asset": "W_SwampGasEmit.ALO" + "asset": "w_sith_arch.alo" }, { "id": "FILE00", @@ -963,10 +1021,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_Shuttle_Tyderium.alo\u0027", + "message": "Unable to find .ALO file \u0027Cin_rv_XWingProp.alo\u0027", "severity": "Error", "context": [], - "asset": "Cin_Shuttle_Tyderium.alo" + "asset": "Cin_rv_XWingProp.alo" }, { "id": "FILE00", @@ -974,23 +1032,24 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_EV_Stardestroyer_Warp.alo\u0027", + "message": "Unable to find .ALO file \u0027Cin_DStar_Dish_close.alo\u0027", "severity": "Error", "context": [], - "asset": "Cin_EV_Stardestroyer_Warp.alo" + "asset": "Cin_DStar_Dish_close.alo" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", + "AET.ModVerify.Verifiers.Commons.TextureVeifier" ], - "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_CINE_LUA.ALO\u0027", + "message": "Could not find texture \u0027W_TE_Rock_f_02_b.tga\u0027 for context: [EV_TIE_PHANTOM.ALO].", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\W_STARS_CINE_LUA.ALO" + "EV_TIE_PHANTOM.ALO" ], - "asset": "Lensflare0" + "asset": "W_TE_Rock_f_02_b.tga" }, { "id": "FILE00", @@ -998,10 +1057,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_DeathStar_Hangar.alo\u0027", + "message": "Proxy particle \u0027pe_bwing_yellow\u0027 not found for model \u0027DATA\\ART\\MODELS\\RV_BWING.ALO\u0027", "severity": "Error", - "context": [], - "asset": "CIN_DeathStar_Hangar.alo" + "context": [ + "DATA\\ART\\MODELS\\RV_BWING.ALO" + ], + "asset": "pe_bwing_yellow" }, { "id": "FILE00", @@ -1009,10 +1070,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_Fire_Medium.alo\u0027", + "message": "Unable to find .ALO file \u0027CIN_Lambda_Head.alo\u0027", "severity": "Error", "context": [], - "asset": "CIN_Fire_Medium.alo" + "asset": "CIN_Lambda_Head.alo" }, { "id": "FILE00", @@ -1020,12 +1081,12 @@ "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", + "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\\W_STARS_CINE.ALO" + "DATA\\ART\\MODELS\\EB_COMMANDCENTER.ALO" ], - "asset": "Lensflare0" + "asset": "p_explosion_small_delay00" }, { "id": "FILE00", @@ -1033,10 +1094,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_Rbel_Soldier_Group.alo\u0027", + "message": "Unable to find .ALO file \u0027Cin_DStar_LeverPanel.alo\u0027", "severity": "Error", "context": [], - "asset": "CIN_Rbel_Soldier_Group.alo" + "asset": "Cin_DStar_LeverPanel.alo" }, { "id": "FILE00", @@ -1044,10 +1105,10 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027RV_nebulonb_D_death_00.ALO\u0027", + "message": "Unable to find .ALO file \u0027p_splash_wake_lava.alo\u0027", "severity": "Error", "context": [], - "asset": "RV_nebulonb_D_death_00.ALO" + "asset": "p_splash_wake_lava.alo" }, { "id": "FILE00", @@ -1055,10 +1116,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027W_Volcano_Rock02.ALO\u0027", + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_LOW.ALO\u0027", "severity": "Error", - "context": [], - "asset": "W_Volcano_Rock02.ALO" + "context": [ + "DATA\\ART\\MODELS\\W_STARS_LOW.ALO" + ], + "asset": "Lensflare0" }, { "id": "FILE00", @@ -1066,10 +1129,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027w_planet_volcanic.alo\u0027", + "message": "Proxy particle \u0027p_desert_ground_dust\u0027 not found for model \u0027DATA\\ART\\MODELS\\EI_MARAJADE.ALO\u0027", "severity": "Error", - "context": [], - "asset": "w_planet_volcanic.alo" + "context": [ + "DATA\\ART\\MODELS\\EI_MARAJADE.ALO" + ], + "asset": "p_desert_ground_dust" }, { "id": "FILE00", @@ -1077,10 +1142,12 @@ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Unable to find .ALO file \u0027CIN_REb_CelebCharacters.alo\u0027", + "message": "Proxy particle \u0027p_bomb_spin\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_THERMAL_DETONATOR_EMPIRE.ALO\u0027", "severity": "Error", - "context": [], - "asset": "CIN_REb_CelebCharacters.alo" + "context": [ + "DATA\\ART\\MODELS\\W_THERMAL_DETONATOR_EMPIRE.ALO" + ], + "asset": "p_bomb_spin" }, { "id": "FILE00", @@ -1088,257 +1155,249 @@ "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.", + "message": "Unable to find .ALO file \u0027Cin_EV_TieAdvanced.alo\u0027", "severity": "Error", - "context": [ - "DATA\\ART\\MODELS\\UV_CRUSADERCLASSCORVETTE.ALO" - ], - "asset": "Default.fx" + "context": [], + "asset": "Cin_EV_TieAdvanced.alo" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", - "AET.ModVerify.Verifiers.Commons.TextureVeifier" + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" ], - "message": "Could not find texture \u0027Cin_Reb_CelebHall_Wall.tga\u0027 for context: [W_SITH_LEFTHALL.ALO].", + "message": "Unable to find .ALO file \u0027CIN_Rbel_Soldier_Group.alo\u0027", "severity": "Error", - "context": [ - "W_SITH_LEFTHALL.ALO" - ], - "asset": "Cin_Reb_CelebHall_Wall.tga" + "context": [], + "asset": "CIN_Rbel_Soldier_Group.alo" }, { "id": "FILE00", "verifiers": [ - "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Unable to find .ALO file \u0027Cin_EV_lambdaShuttle_150.alo\u0027", + "message": "Audio file \u0027U000_LEI0213_ENG.WAV\u0027 could not be found.", "severity": "Error", - "context": [], - "asset": "Cin_EV_lambdaShuttle_150.alo" + "context": [ + "Unit_Move_Leia" + ], + "asset": "U000_LEI0213_ENG.WAV" }, { "id": "FILE00", "verifiers": [ - "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Proxy particle \u0027p_explosion_small_delay00\u0027 not found for model \u0027DATA\\ART\\MODELS\\EB_COMMANDCENTER.ALO\u0027", + "message": "Audio file \u0027U000_LEI0113_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\EB_COMMANDCENTER.ALO" + "Unit_Select_Leia" ], - "asset": "p_explosion_small_delay00" + "asset": "U000_LEI0113_ENG.WAV" }, { "id": "FILE00", "verifiers": [ - "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier", - "AET.ModVerify.Verifiers.Commons.TextureVeifier" + "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Could not find texture \u0027UB_girder_B.tga\u0027 for context: [UV_MDU_CAGE.ALO].", + "message": "Audio file \u0027U000_LEI0603_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "UV_MDU_CAGE.ALO" + "Unit_Increase_Production_Leia" ], - "asset": "UB_girder_B.tga" + "asset": "U000_LEI0603_ENG.WAV" }, { "id": "FILE00", "verifiers": [ - "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_01_STATION_D.ALO\u0027", + "message": "Audio file \u0027U000_LEI0309_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "DATA\\ART\\MODELS\\UB_01_STATION_D.ALO" + "Unit_Attack_Leia" ], - "asset": "p_uwstation_death" + "asset": "U000_LEI0309_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0206_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0212_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ "Unit_Move_Leia" ], - "asset": "U000_LEI0206_ENG.WAV" + "asset": "U000_LEI0212_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0204_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_MAL0503_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Move_Leia" + "Unit_Assist_Move_Missile_Launcher" ], - "asset": "U000_LEI0204_ENG.WAV" + "asset": "U000_MAL0503_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0102_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_MCF1601_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Select_Leia" + "Unit_StarDest_MC30_Frigate" ], - "asset": "U000_LEI0102_ENG.WAV" + "asset": "U000_MCF1601_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0215_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0111_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Move_Leia" + "Unit_Select_Leia" ], - "asset": "U000_LEI0215_ENG.WAV" + "asset": "U000_LEI0111_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0107_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_ARC3106_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Select_Leia" + "Unit_Complete_Troops_Arc_Hammer" ], - "asset": "U000_LEI0107_ENG.WAV" + "asset": "U000_ARC3106_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0504_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0303_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Remove_Corruption_Leia" + "Unit_Attack_Leia" ], - "asset": "U000_LEI0504_ENG.WAV" + "asset": "U000_LEI0303_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027AMB_DES_CLEAR_LOOP_1.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0404_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Weather_Ambient_Clear_Sandstorm_Loop" + "Unit_Guard_Leia" ], - "asset": "AMB_DES_CLEAR_LOOP_1.WAV" + "asset": "U000_LEI0404_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0105_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027TESTUNITMOVE_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Select_Leia" + "Unit_Move_Gneneric_Test" ], - "asset": "U000_LEI0105_ENG.WAV" + "asset": "TESTUNITMOVE_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0213_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0401_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Move_Leia" + "Unit_Guard_Leia" ], - "asset": "U000_LEI0213_ENG.WAV" + "asset": "U000_LEI0401_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0201_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027EGL_STAR_VIPER_SPINNING_1.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Move_Leia" + "Unit_Star_Viper_Spinning_By" ], - "asset": "U000_LEI0201_ENG.WAV" + "asset": "EGL_STAR_VIPER_SPINNING_1.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0303_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_TMC0212_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Attack_Leia" + "Unit_Move_Tie_Mauler" ], - "asset": "U000_LEI0303_ENG.WAV" + "asset": "U000_TMC0212_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0103_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0110_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ "Unit_Select_Leia" ], - "asset": "U000_LEI0103_ENG.WAV" + "asset": "U000_LEI0110_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0207_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0314_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Move_Leia" + "Unit_Attack_Leia" ], - "asset": "U000_LEI0207_ENG.WAV" + "asset": "U000_LEI0314_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_DEF3006_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0305_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Corrupt_Sabateur" + "Unit_Attack_Leia" ], - "asset": "U000_DEF3006_ENG.WAV" + "asset": "U000_LEI0305_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0309_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0112_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Attack_Leia" + "Unit_Select_Leia" ], - "asset": "U000_LEI0309_ENG.WAV" + "asset": "U000_LEI0112_ENG.WAV" }, { "id": "FILE00", @@ -1357,120 +1416,120 @@ "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_DEF3106_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0211_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Weaken_Sabateur" + "Unit_Move_Leia" ], - "asset": "U000_DEF3106_ENG.WAV" + "asset": "U000_LEI0211_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0503_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0205_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Remove_Corruption_Leia" + "Unit_Move_Leia" ], - "asset": "U000_LEI0503_ENG.WAV" + "asset": "U000_LEI0205_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0502_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0115_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Remove_Corruption_Leia" + "Unit_Select_Leia" ], - "asset": "U000_LEI0502_ENG.WAV" + "asset": "U000_LEI0115_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0212_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0604_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Move_Leia" + "Unit_Increase_Production_Leia" ], - "asset": "U000_LEI0212_ENG.WAV" + "asset": "U000_LEI0604_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027AMB_URB_CLEAR_LOOP_1.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0602_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Weather_Ambient_Clear_Urban_Loop" + "Unit_Increase_Production_Leia" ], - "asset": "AMB_URB_CLEAR_LOOP_1.WAV" + "asset": "U000_LEI0602_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0311_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0315_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ "Unit_Attack_Leia" ], - "asset": "U000_LEI0311_ENG.WAV" + "asset": "U000_LEI0315_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0115_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_DEF3006_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Select_Leia" + "Unit_Corrupt_Sabateur" ], - "asset": "U000_LEI0115_ENG.WAV" + "asset": "U000_DEF3006_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0101_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0210_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Select_Leia" + "Unit_Move_Leia" ], - "asset": "U000_LEI0101_ENG.WAV" + "asset": "U000_LEI0210_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0401_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0105_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Guard_Leia" + "Unit_Select_Leia" ], - "asset": "U000_LEI0401_ENG.WAV" + "asset": "U000_LEI0105_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0315_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0208_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Attack_Leia" + "Unit_Move_Leia" ], - "asset": "U000_LEI0315_ENG.WAV" + "asset": "U000_LEI0208_ENG.WAV" }, { "id": "FILE00", @@ -1489,593 +1548,1505 @@ "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0603_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0202_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Increase_Production_Leia" + "Unit_Move_Leia" ], - "asset": "U000_LEI0603_ENG.WAV" + "asset": "U000_LEI0202_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0104_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0306_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Select_Leia" + "Unit_Attack_Leia" ], - "asset": "U000_LEI0104_ENG.WAV" + "asset": "U000_LEI0306_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0501_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0101_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Remove_Corruption_Leia" + "Unit_Select_Leia" ], - "asset": "U000_LEI0501_ENG.WAV" + "asset": "U000_LEI0101_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027TESTUNITMOVE_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027C000_DST0102_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Move_Gneneric_Test" + "EHD_Death_Star_Activate" ], - "asset": "TESTUNITMOVE_ENG.WAV" + "asset": "C000_DST0102_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0108_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0103_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ "Unit_Select_Leia" ], - "asset": "U000_LEI0108_ENG.WAV" + "asset": "U000_LEI0103_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_MCF1601_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0403_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_StarDest_MC30_Frigate" + "Unit_Guard_Leia" ], - "asset": "U000_MCF1601_ENG.WAV" + "asset": "U000_LEI0403_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0111_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027FS_BEETLE_2.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Select_Leia" + "SFX_Anim_Beetle_Footsteps" ], - "asset": "U000_LEI0111_ENG.WAV" + "asset": "FS_BEETLE_2.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0211_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0201_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ "Unit_Move_Leia" ], - "asset": "U000_LEI0211_ENG.WAV" + "asset": "U000_LEI0201_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0110_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0203_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Select_Leia" + "Unit_Move_Leia" ], - "asset": "U000_LEI0110_ENG.WAV" + "asset": "U000_LEI0203_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0403_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0114_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Guard_Leia" + "Unit_Select_Leia" ], - "asset": "U000_LEI0403_ENG.WAV" + "asset": "U000_LEI0114_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0306_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027FS_BEETLE_1.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Attack_Leia" + "SFX_Anim_Beetle_Footsteps" ], - "asset": "U000_LEI0306_ENG.WAV" + "asset": "FS_BEETLE_1.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0308_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0304_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ "Unit_Attack_Leia" ], - "asset": "U000_LEI0308_ENG.WAV" + "asset": "U000_LEI0304_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0112_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0301_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Select_Leia" + "Unit_Attack_Leia" ], - "asset": "U000_LEI0112_ENG.WAV" + "asset": "U000_LEI0301_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0301_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0503_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Attack_Leia" + "Unit_Remove_Corruption_Leia" ], - "asset": "U000_LEI0301_ENG.WAV" + "asset": "U000_LEI0503_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0404_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0109_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Guard_Leia" + "Unit_Select_Leia" ], - "asset": "U000_LEI0404_ENG.WAV" + "asset": "U000_LEI0109_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_TMC0212_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0308_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Move_Tie_Mauler" + "Unit_Attack_Leia" ], - "asset": "U000_TMC0212_ENG.WAV" + "asset": "U000_LEI0308_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027EGL_STAR_VIPER_SPINNING_1.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0402_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Star_Viper_Spinning_By" + "Unit_Guard_Leia" ], - "asset": "EGL_STAR_VIPER_SPINNING_1.WAV" + "asset": "U000_LEI0402_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0208_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0108_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Move_Leia" + "Unit_Select_Leia" ], - "asset": "U000_LEI0208_ENG.WAV" + "asset": "U000_LEI0108_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0604_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0307_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Increase_Production_Leia" + "Unit_Attack_Leia" ], - "asset": "U000_LEI0604_ENG.WAV" + "asset": "U000_LEI0307_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027FS_BEETLE_3.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0311_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "SFX_Anim_Beetle_Footsteps" + "Unit_Attack_Leia" ], - "asset": "FS_BEETLE_3.WAV" + "asset": "U000_LEI0311_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0109_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0102_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ "Unit_Select_Leia" ], - "asset": "U000_LEI0109_ENG.WAV" + "asset": "U000_LEI0102_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0202_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0104_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Move_Leia" + "Unit_Select_Leia" ], - "asset": "U000_LEI0202_ENG.WAV" + "asset": "U000_LEI0104_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0602_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027FS_BEETLE_3.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Increase_Production_Leia" + "SFX_Anim_Beetle_Footsteps" ], - "asset": "U000_LEI0602_ENG.WAV" + "asset": "FS_BEETLE_3.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0305_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0313_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ "Unit_Attack_Leia" ], - "asset": "U000_LEI0305_ENG.WAV" + "asset": "U000_LEI0313_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_MAL0503_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0206_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Assist_Move_Missile_Launcher" + "Unit_Move_Leia" ], - "asset": "U000_MAL0503_ENG.WAV" + "asset": "U000_LEI0206_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0601_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_ARC3104_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Increase_Production_Leia" + "Unit_Produce_Troops_Arc_Hammer" ], - "asset": "U000_LEI0601_ENG.WAV" + "asset": "U000_ARC3104_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_ARC3106_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0312_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Complete_Troops_Arc_Hammer" + "Unit_Attack_Leia" ], - "asset": "U000_ARC3106_ENG.WAV" + "asset": "U000_LEI0312_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027FS_BEETLE_4.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0215_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "SFX_Anim_Beetle_Footsteps" + "Unit_Move_Leia" ], - "asset": "FS_BEETLE_4.WAV" + "asset": "U000_LEI0215_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027FS_BEETLE_1.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0107_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "SFX_Anim_Beetle_Footsteps" + "Unit_Select_Leia" ], - "asset": "FS_BEETLE_1.WAV" + "asset": "U000_LEI0107_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0205_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0501_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Move_Leia" + "Unit_Remove_Corruption_Leia" ], - "asset": "U000_LEI0205_ENG.WAV" + "asset": "U000_LEI0501_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0113_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027AMB_DES_CLEAR_LOOP_1.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Select_Leia" + "Weather_Ambient_Clear_Sandstorm_Loop" ], - "asset": "U000_LEI0113_ENG.WAV" + "asset": "AMB_DES_CLEAR_LOOP_1.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0314_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0504_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Attack_Leia" + "Unit_Remove_Corruption_Leia" ], - "asset": "U000_LEI0314_ENG.WAV" + "asset": "U000_LEI0504_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0304_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0502_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Attack_Leia" + "Unit_Remove_Corruption_Leia" ], - "asset": "U000_LEI0304_ENG.WAV" + "asset": "U000_LEI0502_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0203_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_DEF3106_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Move_Leia" + "Unit_Weaken_Sabateur" ], - "asset": "U000_LEI0203_ENG.WAV" + "asset": "U000_DEF3106_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027C000_DST0102_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_ARC3105_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "EHD_Death_Star_Activate" + "Unit_Complete_Troops_Arc_Hammer" ], - "asset": "C000_DST0102_ENG.WAV" + "asset": "U000_ARC3105_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0114_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0601_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Select_Leia" + "Unit_Increase_Production_Leia" ], - "asset": "U000_LEI0114_ENG.WAV" + "asset": "U000_LEI0601_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_ARC3104_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0204_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Produce_Troops_Arc_Hammer" + "Unit_Move_Leia" ], - "asset": "U000_ARC3104_ENG.WAV" + "asset": "U000_LEI0204_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027FS_BEETLE_2.WAV\u0027 could not be found.", + "message": "Audio file \u0027U000_LEI0207_ENG.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "SFX_Anim_Beetle_Footsteps" + "Unit_Move_Leia" ], - "asset": "FS_BEETLE_2.WAV" + "asset": "U000_LEI0207_ENG.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_ARC3105_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027FS_BEETLE_4.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Complete_Troops_Arc_Hammer" + "SFX_Anim_Beetle_Footsteps" ], - "asset": "U000_ARC3105_ENG.WAV" + "asset": "FS_BEETLE_4.WAV" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.AudioFilesVerifier" ], - "message": "Audio file \u0027U000_LEI0307_ENG.WAV\u0027 could not be found.", + "message": "Audio file \u0027AMB_URB_CLEAR_LOOP_1.WAV\u0027 could not be found.", "severity": "Error", "context": [ - "Unit_Attack_Leia" + "Weather_Ambient_Clear_Urban_Loop" ], - "asset": "U000_LEI0307_ENG.WAV" + "asset": "AMB_URB_CLEAR_LOOP_1.WAV" }, { "id": "FILE00", "verifiers": [ - "AET.ModVerify.Verifiers.AudioFilesVerifier" + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" ], - "message": "Audio file \u0027U000_LEI0402_ENG.WAV\u0027 could not be found.", + "message": "Could not find GUI texture \u0027i_dialogue_button_large_middle_off.tga\u0027 at location \u0027Repository\u0027.", "severity": "Error", "context": [ - "Unit_Guard_Leia" + "IDC_PLAY_FACTION_B_BUTTON_BIG", + "Repository" ], - "asset": "U000_LEI0402_ENG.WAV" + "asset": "i_dialogue_button_large_middle_off.tga" }, { "id": "FILE00", "verifiers": [ - "AET.ModVerify.Verifiers.AudioFilesVerifier" + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" ], - "message": "Audio file \u0027U000_LEI0312_ENG.WAV\u0027 could not be found.", + "message": "Could not find GUI texture \u0027underworld_logo_selected.tga\u0027 at location \u0027MegaTexture\u0027.", "severity": "Error", "context": [ - "Unit_Attack_Leia" + "IDC_PLAY_FACTION_A_BUTTON_BIG", + "MegaTexture" ], - "asset": "U000_LEI0312_ENG.WAV" + "asset": "underworld_logo_selected.tga" }, { "id": "FILE00", "verifiers": [ - "AET.ModVerify.Verifiers.AudioFilesVerifier" + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" ], - "message": "Audio file \u0027U000_LEI0210_ENG.WAV\u0027 could not be found.", + "message": "Could not find GUI texture \u0027underworld_logo_off.tga\u0027 at location \u0027MegaTexture\u0027.", "severity": "Error", "context": [ - "Unit_Move_Leia" + "IDC_PLAY_FACTION_A_BUTTON_BIG", + "MegaTexture" ], - "asset": "U000_LEI0210_ENG.WAV" + "asset": "underworld_logo_off.tga" }, { "id": "FILE00", "verifiers": [ - "AET.ModVerify.Verifiers.AudioFilesVerifier" + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" ], - "message": "Audio file \u0027U000_LEI0313_ENG.WAV\u0027 could not be found.", + "message": "Could not find GUI texture \u0027i_button_petro_sliver.tga\u0027 at location \u0027MegaTexture\u0027.", "severity": "Error", "context": [ - "Unit_Attack_Leia" + "IDC_MENU_PETRO_LOGO", + "MegaTexture" ], - "asset": "U000_LEI0313_ENG.WAV" + "asset": "i_button_petro_sliver.tga" }, { "id": "FILE00", "verifiers": [ "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" ], - "message": "Could not find GUI texture \u0027underworld_logo_selected.tga\u0027 at location \u0027MegaTexture\u0027.", + "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_selected.tga" + "asset": "underworld_logo_rollover.tga" }, { - "id": "FILE00", + "id": "CMDBAR05", "verifiers": [ - "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + "AET.ModVerify.Verifiers.CommandBarVerifier" ], - "message": "Could not find GUI texture \u0027i_button_petro_sliver.tga\u0027 at location \u0027MegaTexture\u0027.", - "severity": "Error", - "context": [ - "IDC_MENU_PETRO_LOGO", - "MegaTexture" + "message": "The CommandBar component \u0027g_planet_land_forces\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_planet_land_forces" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" ], - "asset": "i_button_petro_sliver.tga" + "message": "The CommandBar component \u0027g_ground_sell\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_ground_sell" }, { - "id": "FILE00", + "id": "CMDBAR05", "verifiers": [ - "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + "AET.ModVerify.Verifiers.CommandBarVerifier" ], - "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" + "message": "The CommandBar component \u0027st_bracket_medium\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_bracket_medium" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" ], - "asset": "i_dialogue_button_large_middle_off.tga" + "message": "The CommandBar component \u0027b_planet_right\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "b_planet_right" }, { - "id": "FILE00", + "id": "CMDBAR04", "verifiers": [ - "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + "AET.ModVerify.Verifiers.CommandBarVerifier" ], - "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" + "message": "The CommandBar component \u0027g_credit_bar\u0027 is not supported by the game.", + "severity": "Information", + "context": [], + "asset": "g_credit_bar" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" ], - "asset": "underworld_logo_rollover.tga" + "message": "The CommandBar component \u0027zoomed_header_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "zoomed_header_text" }, { - "id": "FILE00", + "id": "CMDBAR05", "verifiers": [ - "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + "AET.ModVerify.Verifiers.CommandBarVerifier" ], - "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" + "message": "The CommandBar component \u0027g_space_level_pips\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_space_level_pips" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" ], - "asset": "underworld_logo_off.tga" + "message": "The CommandBar component \u0027bribed_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "bribed_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027encyclopedia_header_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "encyclopedia_header_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027tutorial_text_back\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "tutorial_text_back" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027encyclopedia_back\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "encyclopedia_back" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027encyclopedia_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "encyclopedia_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027balance_pip\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "balance_pip" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027objective_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "objective_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027skirmish_upgrade\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "skirmish_upgrade" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027surface_mod_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "surface_mod_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_hero_health\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_hero_health" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027bribe_display\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "bribe_display" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_build\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_build" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027garrison_slot_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "garrison_slot_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_conflict\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_conflict" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027tooltip_name\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "tooltip_name" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027garrison_respawn_counter\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "garrison_respawn_counter" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_ability_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_ability_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_shields_medium\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_shields_medium" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_health\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_health" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_weather\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_weather" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_health_medium\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_health_medium" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_power\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_power" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_ground_level_pips\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_ground_level_pips" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027zoomed_cost_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "zoomed_cost_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027bm_title_4011\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "bm_title_4011" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_planet_name\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_planet_name" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_shields_large\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_shields_large" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_hero_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_hero_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027generic_flytext\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "generic_flytext" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027reinforcement_counter\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "reinforcement_counter" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_planet_value\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_planet_value" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027radar_blip\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "radar_blip" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_political_control\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_political_control" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_planet_ring\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_planet_ring" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_garrison_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_garrison_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027encyclopedia_right_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "encyclopedia_right_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027b_quick_ref\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "b_quick_ref" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027objective_back\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "objective_back" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027encyclopedia_center_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "encyclopedia_center_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_shields\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_shields" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_grab_bar\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_grab_bar" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_smuggler\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_smuggler" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_enemy_hero\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_enemy_hero" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027cs_ability_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "cs_ability_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027encyclopedia_cost_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "encyclopedia_cost_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_planet_ability\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_planet_ability" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_control_group\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_control_group" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027gui_dialog_tooltip\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "gui_dialog_tooltip" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027remote_bomb_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "remote_bomb_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027tutorial_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "tutorial_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_space_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_space_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_bracket_large\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_bracket_large" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027zoomed_back\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "zoomed_back" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027encyclopedia_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "encyclopedia_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027zoomed_right_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "zoomed_right_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027b_beacon_t\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "b_beacon_t" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_bounty_hunter\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_bounty_hunter" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_credit_bar\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_credit_bar" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_hero\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_hero" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027bm_title_4010\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "bm_title_4010" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_planet_fleet\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_planet_fleet" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_corruption_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_corruption_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_smuggled\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_smuggled" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027help_back\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "help_back" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_bracket_small\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_bracket_small" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027objective_header_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "objective_header_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_corruption_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_corruption_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_ground_level\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_ground_level" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027lt_weather_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "lt_weather_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027cs_ability_button\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "cs_ability_button" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_radar_view\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_radar_view" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027objective_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "objective_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027tooltip_back\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "tooltip_back" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027zoomed_center_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "zoomed_center_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_health_bar\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_health_bar" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027zoomed_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "zoomed_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027generic_collision\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "generic_collision" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027tooltip_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "tooltip_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_radar_blip\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_radar_blip" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_hero_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_hero_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_space_level\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_space_level" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027b_planet_left\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "b_planet_left" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027tooltip_icon_land\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "tooltip_icon_land" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_ground_icon\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_ground_icon" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027tooltip_price\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "tooltip_price" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027tooltip_left_text\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "tooltip_left_text" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027st_health_large\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "st_health_large" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027tactical_sell\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "tactical_sell" + }, + { + "id": "CMDBAR05", + "verifiers": [ + "AET.ModVerify.Verifiers.CommandBarVerifier" + ], + "message": "The CommandBar component \u0027g_special_ability\u0027 is not connected to a shell component.", + "severity": "Warning", + "context": [], + "asset": "g_special_ability" } ] } \ No newline at end of file diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index fe15b1c..89c4b64 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -25,11 +25,11 @@ - + - + diff --git a/src/ModVerify/Reporting/Json/JsonGameLocation.cs b/src/ModVerify/Reporting/Json/JsonGameLocation.cs new file mode 100644 index 0000000..3f088f1 --- /dev/null +++ b/src/ModVerify/Reporting/Json/JsonGameLocation.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using PG.StarWarsGame.Engine; + +namespace AET.ModVerify.Reporting.Json; + +internal class JsonGameLocation +{ + [JsonPropertyName("modPaths")] + public IReadOnlyList ModPaths { get; } + + [JsonPropertyName("gamePath")] + public string GamePath { get; } + + [JsonPropertyName("fallbackPaths")] + public IReadOnlyList FallbackPaths { get; } + + [JsonConstructor] + private JsonGameLocation(IReadOnlyList modPaths, string gamePath, IReadOnlyList fallbackPaths) + { + ModPaths = modPaths; + GamePath = gamePath; + FallbackPaths = fallbackPaths; + } + + public JsonGameLocation(GameLocations location) + { + ModPaths = location.ModPaths.ToArray(); + GamePath = location.GamePath; + FallbackPaths = location.FallbackPaths.ToArray(); + } + + public static GameLocations ToLocation(JsonGameLocation jsonLocation) + { + return new GameLocations(jsonLocation.ModPaths, jsonLocation.GamePath, jsonLocation.FallbackPaths); + } +} \ No newline at end of file diff --git a/src/ModVerify/Reporting/Json/JsonVerificationBaseline.cs b/src/ModVerify/Reporting/Json/JsonVerificationBaseline.cs index 4688c98..0d9a1b7 100644 --- a/src/ModVerify/Reporting/Json/JsonVerificationBaseline.cs +++ b/src/ModVerify/Reporting/Json/JsonVerificationBaseline.cs @@ -10,6 +10,10 @@ internal class JsonVerificationBaseline [JsonPropertyName("version")] public Version? Version { get; } + [JsonPropertyName("target")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public JsonVerificationTarget? Target { get; } + [JsonPropertyName("minSeverity")] [JsonConverter(typeof(JsonStringEnumConverter))] public VerificationSeverity MinimumSeverity { get; } @@ -22,11 +26,17 @@ public JsonVerificationBaseline(VerificationBaseline baseline) Errors = baseline.Select(x => new JsonVerificationError(x)); Version = baseline.Version; MinimumSeverity = baseline.MinimumSeverity; + Target = baseline.Target is not null ? new JsonVerificationTarget(baseline.Target) : null; } [JsonConstructor] - private JsonVerificationBaseline(Version version, VerificationSeverity minimumSeverity, IEnumerable errors) + private JsonVerificationBaseline( + JsonVerificationTarget target, + Version version, + VerificationSeverity minimumSeverity, + IEnumerable errors) { + Target = target; Errors = errors; Version = version; MinimumSeverity = minimumSeverity; diff --git a/src/ModVerify/Reporting/Json/JsonVerificationTarget.cs b/src/ModVerify/Reporting/Json/JsonVerificationTarget.cs new file mode 100644 index 0000000..4b74b6f --- /dev/null +++ b/src/ModVerify/Reporting/Json/JsonVerificationTarget.cs @@ -0,0 +1,51 @@ +using System.Text.Json.Serialization; +using PG.StarWarsGame.Engine; + +namespace AET.ModVerify.Reporting.Json; + +internal class JsonVerificationTarget +{ + [JsonPropertyName("name")] + public string Name { get; } + + [JsonPropertyName("engine")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public GameEngineType Engine { get; } + + [JsonPropertyName("version")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Version{ get; } + + [JsonPropertyName("location")] + public JsonGameLocation Location { get; } + + [JsonConstructor] + private JsonVerificationTarget(string name, string? version, JsonGameLocation location, GameEngineType engine) + { + Name = name; + Version = version; + Engine = engine; + Location = location; + } + + public JsonVerificationTarget(VerificationTarget target) + { + Name = target.Name; + Version = target.Version; + Engine = target.Engine; + Location = new JsonGameLocation(target.Location); + } + + public static VerificationTarget? ToTarget(JsonVerificationTarget? jsonTarget) + { + if (jsonTarget is null) + return null!; + return new VerificationTarget + { + Engine = jsonTarget.Engine, + Name = jsonTarget.Name, + Location = JsonGameLocation.ToLocation(jsonTarget.Location), + Version = jsonTarget.Version + }; + } +} \ No newline at end of file diff --git a/src/ModVerify/Reporting/VerificationBaseline.cs b/src/ModVerify/Reporting/VerificationBaseline.cs index c37539b..1cc8312 100644 --- a/src/ModVerify/Reporting/VerificationBaseline.cs +++ b/src/ModVerify/Reporting/VerificationBaseline.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Text.Json; using System.Threading.Tasks; using AET.ModVerify.Reporting.Json; @@ -11,13 +12,15 @@ namespace AET.ModVerify.Reporting; public sealed class VerificationBaseline : IReadOnlyCollection { - public static readonly Version LatestVersion = new(2, 0); + public static readonly Version LatestVersion = new(2, 1); public static readonly string LatestVersionString = LatestVersion.ToString(2); - public static readonly VerificationBaseline Empty = new(VerificationSeverity.Information, []); + public static readonly VerificationBaseline Empty = new(VerificationSeverity.Information, [], null); private readonly HashSet _errors; + public VerificationTarget? Target { get; } + public Version? Version { get; } public VerificationSeverity MinimumSeverity { get; } @@ -30,13 +33,15 @@ internal VerificationBaseline(JsonVerificationBaseline baseline) _errors = [..baseline.Errors.Select(x => new VerificationError(x))]; Version = baseline.Version; MinimumSeverity = baseline.MinimumSeverity; + Target = JsonVerificationTarget.ToTarget(baseline.Target); } - public VerificationBaseline(VerificationSeverity minimumSeverity, IEnumerable errors) + public VerificationBaseline(VerificationSeverity minimumSeverity, IEnumerable errors, VerificationTarget? target) { _errors = [..errors]; Version = LatestVersion; MinimumSeverity = minimumSeverity; + Target = target; } public bool Contains(VerificationError error) @@ -77,6 +82,10 @@ IEnumerator IEnumerable.GetEnumerator() public override string ToString() { - return $"Baseline [Version={Version}, MinSeverity={MinimumSeverity}, NumErrors={Count}]"; + var sb = new StringBuilder($"Baseline [Version={Version}, MinSeverity={MinimumSeverity}, NumErrors={Count}"); + if (Target is not null) + sb.Append($", Target={Target}"); + sb.Append(']'); + return sb.ToString(); } } \ No newline at end of file diff --git a/src/ModVerify/Resources/Schemas/2.0/baseline.json b/src/ModVerify/Resources/Schemas/2.1/baseline.json similarity index 56% rename from src/ModVerify/Resources/Schemas/2.0/baseline.json rename to src/ModVerify/Resources/Schemas/2.1/baseline.json index 2520a58..56bfb86 100644 --- a/src/ModVerify/Resources/Schemas/2.0/baseline.json +++ b/src/ModVerify/Resources/Schemas/2.1/baseline.json @@ -1,9 +1,58 @@ { - "$id": "https://AlamoEngine-Tools.github.io/schemas/mod-verify/2.0/baseline", + "$id": "https://AlamoEngine-Tools.github.io/schemas/mod-verify/2.1/baseline", "$schema": "https://json-schema.org/draft/2020-12/schema", "description": "Represents a baseline for AET ModVerify", "type": "object", "$defs": { + "location": { + "type": "object", + "properties": { + "modPaths": { + "type": "array", + "items": { + "type": "string" + } + }, + "gamePath": { + "type": "string" + }, + "fallbackPaths": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "modPaths", + "gamePath", + "fallbackPaths" + ], + "additionalProperties": false + }, + "target": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "engine": { + "enum": [ "Eaw", "Foc" ] + }, + "location": { + "$ref": "#/$defs/location" + } + }, + "required": [ + "name", + "engine", + "location" + ], + "additionalProperties": false + }, "severity": { "enum": [ "Information", "Warning", "Error", "Critical" ] }, @@ -48,7 +97,7 @@ }, "properties": { "version": { - "const": "2.0" + "const": "2.1" }, "minSeverity": { "$ref": "#/$defs/severity" @@ -59,6 +108,9 @@ "$ref": "#/$defs/error" }, "additionalItems": false + }, + "target": { + "$ref": "#/$defs/target" } }, "required": [ diff --git a/src/ModVerify/VerificationTarget.cs b/src/ModVerify/VerificationTarget.cs index d3afdc7..c0d5b97 100644 --- a/src/ModVerify/VerificationTarget.cs +++ b/src/ModVerify/VerificationTarget.cs @@ -26,7 +26,7 @@ public required GameLocations Location } public string? Version { get; init; } - + public bool IsGame => Location.ModPaths.Count == 0; public override string ToString() @@ -35,7 +35,7 @@ public override string ToString() if (!string.IsNullOrEmpty(Version)) sb.Append($"Version={Version};"); sb.Append($"Location={Location};"); - sb.Append("]"); + sb.Append(']'); return sb.ToString(); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs index 73aa400..f57b605 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Xml.Linq; namespace PG.StarWarsGame.Engine; @@ -29,13 +28,13 @@ public GameLocations(string modPath, string gamePath, string fallbackGamePath) : ThrowHelper.ThrowIfNullOrEmpty(modPath); } - public GameLocations(IList modPaths, string gamePath, string fallbackGamePath) : this(modPaths, - gamePath, [fallbackGamePath]) + public GameLocations(IReadOnlyList modPaths, string gamePath, string fallbackGamePath) + : this(modPaths, gamePath, [fallbackGamePath]) { ThrowHelper.ThrowIfNullOrEmpty(fallbackGamePath); } - public GameLocations(IList modPaths, string gamePath, IList fallbackPaths) + public GameLocations(IReadOnlyList modPaths, string gamePath, IReadOnlyList fallbackPaths) { if (modPaths == null) throw new ArgumentNullException(nameof(modPaths)); From 4e0425228f47527e7a5a0fd4f9f13435181e935d Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 14 Dec 2025 15:11:03 +0100 Subject: [PATCH 07/22] start refactoring verification target selection --- modules/ModdingToolBase | 2 +- .../GameFinder/GameFinderService.cs | 106 +++++++++++------- ...ticModSelector.cs => AutomaticSelector.cs} | 92 ++++++++------- ...nsoleModSelector.cs => ConsoleSelector.cs} | 25 +++-- .../ModSelectors/IModSelector.cs | 13 --- .../IVerificationTargetSelector.cs | 8 ++ .../ModSelectors/ManualModSelector.cs | 30 ----- .../ModSelectors/ManualSelector.cs | 91 +++++++++++++++ .../ModSelectors/ModSelectorBase.cs | 75 ------------- .../ModSelectors/ModSelectorFactory.cs | 18 --- .../ModSelectors/SettingsBasedModSelector.cs | 47 -------- .../SettingsBasedVerificationTargetCreator.cs | 14 +++ .../VerificationTargetSelectorBase.cs | 96 ++++++++++++++++ .../VerificationTargetSelectorFactory.cs | 18 +++ src/ModVerify.CliApp/ModVerifyApplication.cs | 6 +- .../Properties/launchSettings.json | 2 +- .../CommandLine/BaseModVerifyOptions.cs | 6 +- .../Settings/CommandLine/VerifyVerbOption.cs | 2 + .../Settings/GameInstallationsSettings.cs | 6 +- .../Settings/SettingsBuilder.cs | 4 +- .../Utilities/ExtensionMethods.cs | 4 +- .../ModVerifyOptionsParserTest.cs | 3 + 22 files changed, 380 insertions(+), 288 deletions(-) rename src/ModVerify.CliApp/ModSelectors/{AutomaticModSelector.cs => AutomaticSelector.cs} (59%) rename src/ModVerify.CliApp/ModSelectors/{ConsoleModSelector.cs => ConsoleSelector.cs} (80%) delete mode 100644 src/ModVerify.CliApp/ModSelectors/IModSelector.cs create mode 100644 src/ModVerify.CliApp/ModSelectors/IVerificationTargetSelector.cs delete mode 100644 src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs create mode 100644 src/ModVerify.CliApp/ModSelectors/ManualSelector.cs delete mode 100644 src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs delete mode 100644 src/ModVerify.CliApp/ModSelectors/ModSelectorFactory.cs delete mode 100644 src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs create mode 100644 src/ModVerify.CliApp/ModSelectors/SettingsBasedVerificationTargetCreator.cs create mode 100644 src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorBase.cs create mode 100644 src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorFactory.cs diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index a0ad12e..a6a05f7 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit a0ad12e651dcb92cd4f1067059824d8f2894c63f +Subproject commit a6a05f7c3ecc91d796afff91b9686bcded0f8030 diff --git a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs index a88ebd9..8cc2739 100644 --- a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs +++ b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs @@ -4,6 +4,7 @@ using System.IO.Abstractions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure.Clients.Steam; using PG.StarWarsGame.Infrastructure.Games; using PG.StarWarsGame.Infrastructure.Mods; @@ -12,6 +13,17 @@ namespace AET.ModVerify.App.GameFinder; +internal class GameFinderSettings +{ + internal static readonly GameFinderSettings Default = new(); + + public bool InitMods { get; init; } = true; + + public bool SearchFallbackGame { get; init; } = true; + + public GameEngineType? Engine { get; init; } = null; +} + internal class GameFinderService { private readonly IServiceProvider _serviceProvider; @@ -29,7 +41,7 @@ public GameFinderService(IServiceProvider serviceProvider) _logger = _serviceProvider.GetService()?.CreateLogger(GetType()); } - public GameFinderResult FindGames() + public GameFinderResult FindGames(GameFinderSettings settings) { var detectors = new List { @@ -37,10 +49,19 @@ public GameFinderResult FindGames() new SteamPetroglyphStarWarsGameDetector(_serviceProvider), }; - return FindGames(detectors); + return FindGames(detectors, settings); + } + + public IGame FindGame(string gamePath, GameFinderSettings settings) + { + var detectors = new List + { + new DirectoryGameDetector(_fileSystem.DirectoryInfo.New(gamePath), _serviceProvider), + }; + return FindGames(detectors, settings).Game; } - public GameFinderResult FindGamesFromPathOrGlobal(string path) + public GameFinderResult FindGamesFromPathOrGlobal(string path, GameFinderSettings settings) { // There are four common situations: // 1. path points to the actual game directory @@ -62,29 +83,10 @@ public GameFinderResult FindGamesFromPathOrGlobal(string path) // Cases 3 & 4 detectors.Add(new SteamPetroglyphStarWarsGameDetector(_serviceProvider)); - return FindGames(detectors); - } - - private bool TryDetectGame(GameType gameType, IList detectors, out GameDetectionResult result) - { - var gd = new CompositeGameDetector(detectors, _serviceProvider); - - try - { - result = gd.Detect(gameType); - if (result.GameLocation is null) - return false; - return true; - } - catch (Exception e) - { - result = GameDetectionResult.NotInstalled(gameType); - _logger?.LogTrace("Unable to find game installation: {Message}", e.Message); - return false; - } + return FindGames(detectors, settings); } - - private GameFinderResult FindGames(IList detectors) + + private GameFinderResult FindGames(IList detectors, GameFinderSettings settings) { // FoC needs to be tried first if (!TryDetectGame(GameType.Foc, detectors, out var result)) @@ -102,34 +104,58 @@ private GameFinderResult FindGames(IList detectors) var game = _gameFactory.CreateGame(result, CultureInfo.InvariantCulture); - SetupMods(game); + if (settings.InitMods) + SetupMods(game); IGame? fallbackGame = null; - // If the game is Foc we want to set up Eaw as well as the fallbackGame - if (game.Type == GameType.Foc) + if (settings.SearchFallbackGame) { - var fallbackDetectors = new List(); - - if (game.Platform == GamePlatform.SteamGold) - fallbackDetectors.Add(new SteamPetroglyphStarWarsGameDetector(_serviceProvider)); - else - throw new NotImplementedException("Searching fallback game for non-Steam games is currently is not yet implemented."); + // If the game is Foc we want to set up Eaw as well as the fallbackGame + if (game.Type == GameType.Foc) + { + var fallbackDetectors = new List(); + + if (game.Platform == GamePlatform.SteamGold) + fallbackDetectors.Add(new SteamPetroglyphStarWarsGameDetector(_serviceProvider)); + else + throw new NotImplementedException("Searching fallback game for non-Steam games is currently is not yet implemented."); - if (!TryDetectGame(GameType.Eaw, fallbackDetectors, out var fallbackResult) || fallbackResult.GameLocation is null) - throw new GameNotFoundException("Unable to find fallback game installation: Wrong install path?"); + 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(ModVerifyConstants.ConsoleEventId, - "Found fallback game installation: {FallbackResultGameIdentity} at {GameLocationFullName}", fallbackResult.GameIdentity, fallbackResult.GameLocation.FullName); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, + "Found fallback game installation: {FallbackResultGameIdentity} at {GameLocationFullName}", fallbackResult.GameIdentity, fallbackResult.GameLocation.FullName); - fallbackGame = _gameFactory.CreateGame(fallbackResult, CultureInfo.InvariantCulture); + fallbackGame = _gameFactory.CreateGame(fallbackResult, CultureInfo.InvariantCulture); - SetupMods(fallbackGame); + if (settings.InitMods) + SetupMods(fallbackGame); + } } return new GameFinderResult(game, fallbackGame); } + private bool TryDetectGame(GameType gameType, IList detectors, out GameDetectionResult result) + { + var gd = new CompositeGameDetector(detectors, _serviceProvider); + + try + { + result = gd.Detect(gameType); + if (result.GameLocation is null) + return false; + return true; + } + catch (Exception e) + { + result = GameDetectionResult.NotInstalled(gameType); + _logger?.LogTrace("Unable to find game installation: {Message}", e.Message); + return false; + } + } + private void SetupMods(IGame game) { var modFinder = _serviceProvider.GetRequiredService(); diff --git a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs b/src/ModVerify.CliApp/ModSelectors/AutomaticSelector.cs similarity index 59% rename from src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs rename to src/ModVerify.CliApp/ModSelectors/AutomaticSelector.cs index d12cd6b..216598a 100644 --- a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/AutomaticSelector.cs @@ -16,92 +16,102 @@ namespace AET.ModVerify.App.ModSelectors; -internal class AutomaticModSelector(IServiceProvider serviceProvider) : ModSelectorBase(serviceProvider) +internal class AutomaticSelector(IServiceProvider serviceProvider) : VerificationTargetSelectorBase(serviceProvider) { private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); - public override GameLocations? Select( - GameInstallationsSettings settings, - out IPhysicalPlayableObject? targetObject, - out GameEngineType? actualEngineType) + public override VerificationTarget Select(GameInstallationsSettings settings) { - var pathToVerify = settings.AutoPath; - if (pathToVerify is null) + var targetPath = settings.AutoPath; + if (targetPath is null) throw new InvalidOperationException("path to verify cannot be null."); - actualEngineType = settings.EngineType; + var engine = settings.Engine; GameFinderResult finderResult; try { - finderResult = GameFinderService.FindGamesFromPathOrGlobal(pathToVerify); + finderResult = GameFinderService.FindGamesFromPathOrGlobal(targetPath, GameFinderSettings.Default); } catch (GameNotFoundException) { - 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; + Logger?.LogError(ModVerifyConstants.ConsoleEventId, "Unable to find games based of the specified target path '{Path}'. Consider specifying all paths manually.", settings.GamePath); + throw; } - var modOrGame = GetAttachedModOrGame(finderResult, actualEngineType, pathToVerify); + GameLocations locations; + + var targetObject = GetAttachedModOrGame(finderResult, engine, targetPath); - if (modOrGame is not null) + + if (targetObject is null) { - var actualType = modOrGame.Game.Type.ToEngineType(); - actualEngineType ??= actualType; - if (actualEngineType != actualType) - throw new ArgumentException($"The specified game type '{actualEngineType}' does not match the actual type of the game or mod to verify."); + if (!settings.Engine.HasValue) + throw new ArgumentException("Game engine not specified. Use --engine argument to set it."); - targetObject = modOrGame; - return GetLocations(targetObject, finderResult, settings.AdditionalFallbackPaths); - } + Logger?.LogDebug("The requested mod at '{TargetPath}' is detached from its games.", targetPath); - if (!settings.EngineType.HasValue) - throw new ArgumentException("Unable to determine game type. Use --type argument to set the game type."); + // The path is a detached mod, that exists on a different location than the game. + locations = GetDetachedModLocations(targetPath, finderResult, settings, out var mod); + targetObject = mod; + } + else + { + var actualType = targetObject.Game.Type.ToEngineType(); + engine ??= actualType; + if (engine != actualType) + throw new ArgumentException($"The specified game type '{engine}' does not match the actual type of the game or mod to verify."); + locations = GetLocations(targetObject, finderResult.FallbackGame, settings.AdditionalFallbackPaths); + } - Logger?.LogDebug("The requested mod at '{PathToVerify}' is detached from its games.", pathToVerify); + return new VerificationTarget + { + Engine = engine.Value, + Location = locations, + Name = GetTargetName(targetObject, locations), + Version = GetTargetVersion(targetObject) + }; - // The path is a detached mod, that exists on a different location than the game. - var result = GetDetachedModLocations(pathToVerify, finderResult, settings, out var mod); - targetObject = mod; - return result; } - private IPhysicalPlayableObject? GetAttachedModOrGame(GameFinderResult finderResult, GameEngineType? requestedEngineType, string searchPath) + private IPhysicalPlayableObject? GetAttachedModOrGame(GameFinderResult finderResult, GameEngineType? requestedEngineType, string targetPath) { - var fullSearchPath = _fileSystem.Path.GetFullPath(searchPath); + var targetFullPath = _fileSystem.Path.GetFullPath(targetPath); - if (finderResult.Game.Directory.FullName.Equals(fullSearchPath, StringComparison.OrdinalIgnoreCase)) + // If the target is the game directory itself. + if (targetFullPath.Equals(finderResult.Game.Directory.FullName, StringComparison.OrdinalIgnoreCase)) { if (finderResult.Game.Type.ToEngineType() != requestedEngineType) - throw new ArgumentException($"The specified game type '{requestedEngineType}' does not match the actual type of the game '{searchPath}' to verify."); + throw new ArgumentException($"The specified game type '{requestedEngineType}' does not match the actual type of the game '{targetPath}' to verify."); return finderResult.Game; } if (finderResult.FallbackGame is not null && - finderResult.FallbackGame.Directory.FullName.Equals(fullSearchPath, StringComparison.OrdinalIgnoreCase)) + targetFullPath.Equals(finderResult.FallbackGame.Directory.FullName, StringComparison.OrdinalIgnoreCase)) { + throw new NotImplementedException("When does this actually happen???"); + if (finderResult.FallbackGame.Type.ToEngineType() != requestedEngineType) - throw new ArgumentException($"The specified game type '{requestedEngineType}' does not match the actual type of the game '{searchPath}' to verify."); + throw new ArgumentException($"The specified game type '{requestedEngineType}' does not match the actual type of the game '{targetPath}' to verify."); return finderResult.FallbackGame; } - return GetMatchingModFromGame(finderResult.Game, requestedEngineType, fullSearchPath) ?? - GetMatchingModFromGame(finderResult.FallbackGame, requestedEngineType, fullSearchPath); + return GetMatchingModFromGame(finderResult.Game, requestedEngineType, targetFullPath) ?? + GetMatchingModFromGame(finderResult.FallbackGame, requestedEngineType, targetFullPath); } private GameLocations GetDetachedModLocations(string modPath, GameFinderResult gameResult, GameInstallationsSettings settings, out IPhysicalMod mod) { IGame game = null!; - if (gameResult.Game.Type.ToEngineType() == settings.EngineType) + if (gameResult.Game.Type.ToEngineType() == settings.Engine) game = gameResult.Game; - if (gameResult.FallbackGame is not null && gameResult.FallbackGame.Type.ToEngineType() == settings.EngineType) + if (gameResult.FallbackGame is not null && gameResult.FallbackGame.Type.ToEngineType() == settings.Engine) game = gameResult.FallbackGame; if (game is null) - throw new GameNotFoundException($"Unable to find game of type '{settings.EngineType}'"); + throw new GameNotFoundException($"Unable to find game of type '{settings.Engine}'"); var modFinder = ServiceProvider.GetRequiredService(); var modRef = modFinder.FindMods(game, _fileSystem.DirectoryInfo.New(modPath)).FirstOrDefault(); @@ -116,7 +126,7 @@ private GameLocations GetDetachedModLocations(string modPath, GameFinderResult g mod.ResolveDependencies(); - return GetLocations(mod, gameResult, settings.AdditionalFallbackPaths); + return GetLocations(mod, gameResult.FallbackGame, settings.AdditionalFallbackPaths); } private static IPhysicalMod? GetMatchingModFromGame(IGame? game, GameEngineType? requestedEngineType, string modPath) @@ -132,7 +142,7 @@ private GameLocations GetDetachedModLocations(string modPath, GameFinderResult g if (physicalMod.Directory.FullName.Equals(modPath, StringComparison.OrdinalIgnoreCase)) { if (!isGameSupported) - throw new ArgumentException($"The specified game type '{requestedEngineType}' does not match the actual type of the mod '{modPath}' to verify."); + throw new ArgumentException($"The specified engine type '{requestedEngineType}' does not match the required of the mod '{modPath}' to verify."); return physicalMod; } } diff --git a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ConsoleSelector.cs similarity index 80% rename from src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs rename to src/ModVerify.CliApp/ModSelectors/ConsoleSelector.cs index a04cbea..a1701f3 100644 --- a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ConsoleSelector.cs @@ -5,24 +5,29 @@ 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.ModVerify.App.ModSelectors; -internal class ConsoleModSelector(IServiceProvider serviceProvider) : ModSelectorBase(serviceProvider) +internal class ConsoleSelector(IServiceProvider serviceProvider) : VerificationTargetSelectorBase(serviceProvider) { - public override GameLocations Select( - GameInstallationsSettings settings, - out IPhysicalPlayableObject targetObject, - out GameEngineType? actualEngineType) + public override VerificationTarget Select(GameInstallationsSettings settings) { - var gameResult = GameFinderService.FindGames(); - targetObject = SelectPlayableObject(gameResult); - actualEngineType = targetObject.Game.Type.ToEngineType(); - return GetLocations(targetObject, gameResult, settings.AdditionalFallbackPaths); + var gameResult = GameFinderService.FindGames(GameFinderSettings.Default); + var targetObject = SelectPlayableObject(gameResult); + var engine = targetObject.Game.Type.ToEngineType(); + + var gameLocations = GetLocations(targetObject, gameResult.FallbackGame, settings.AdditionalFallbackPaths); + + return new VerificationTarget + { + Engine = engine, + Location = gameLocations, + Name = GetTargetName(targetObject, gameLocations), + Version = GetTargetVersion(targetObject) + }; } private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult finderResult) diff --git a/src/ModVerify.CliApp/ModSelectors/IModSelector.cs b/src/ModVerify.CliApp/ModSelectors/IModSelector.cs deleted file mode 100644 index a04858c..0000000 --- a/src/ModVerify.CliApp/ModSelectors/IModSelector.cs +++ /dev/null @@ -1,13 +0,0 @@ -using AET.ModVerify.App.Settings; -using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Infrastructure; - -namespace AET.ModVerify.App.ModSelectors; - -internal interface IModSelector -{ - GameLocations? Select( - GameInstallationsSettings settings, - out IPhysicalPlayableObject? targetObject, - out GameEngineType? actualEngineType); -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/IVerificationTargetSelector.cs b/src/ModVerify.CliApp/ModSelectors/IVerificationTargetSelector.cs new file mode 100644 index 0000000..d9fe438 --- /dev/null +++ b/src/ModVerify.CliApp/ModSelectors/IVerificationTargetSelector.cs @@ -0,0 +1,8 @@ +using AET.ModVerify.App.Settings; + +namespace AET.ModVerify.App.ModSelectors; + +internal interface IVerificationTargetSelector +{ + VerificationTarget Select(GameInstallationsSettings settings); +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs deleted file mode 100644 index a8b5ab7..0000000 --- a/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Linq; -using AET.ModVerify.App.Settings; -using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Infrastructure; - -namespace AET.ModVerify.App.ModSelectors; - -internal class ManualModSelector(IServiceProvider serviceProvider) : ModSelectorBase(serviceProvider) -{ - public override GameLocations Select( - GameInstallationsSettings settings, - out IPhysicalPlayableObject? targetObject, - out GameEngineType? actualEngineType) - { - actualEngineType = settings.EngineType; - targetObject = null; - - if (!actualEngineType.HasValue) - throw new ArgumentException("Unable to determine game type. Use --type argument to set the game type."); - - if (string.IsNullOrEmpty(settings.GamePath)) - throw new ArgumentException("Argument --game must be set."); - - return new GameLocations( - settings.ModPaths.ToList(), - settings.GamePath!, - GetFallbackPaths(settings.FallbackGamePath, settings.AdditionalFallbackPaths).ToList()); - } -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/ManualSelector.cs b/src/ModVerify.CliApp/ModSelectors/ManualSelector.cs new file mode 100644 index 0000000..0cbd5e4 --- /dev/null +++ b/src/ModVerify.CliApp/ModSelectors/ManualSelector.cs @@ -0,0 +1,91 @@ +using System; +using System.Globalization; +using System.Linq; +using AET.ModVerify.App.GameFinder; +using AET.ModVerify.App.Settings; +using Microsoft.Extensions.DependencyInjection; +using PG.StarWarsGame.Engine; +using PG.StarWarsGame.Infrastructure; +using PG.StarWarsGame.Infrastructure.Games; +using PG.StarWarsGame.Infrastructure.Services; +using PG.StarWarsGame.Infrastructure.Services.Detection; + +namespace AET.ModVerify.App.ModSelectors; + +internal class ManualSelector(IServiceProvider serviceProvider) : VerificationTargetSelectorBase(serviceProvider) +{ + public override VerificationTarget Select(GameInstallationsSettings settings) + { + if (string.IsNullOrEmpty(settings.GamePath)) + throw new ArgumentException("Argument --game must be set."); + if (!settings.Engine.HasValue) + throw new ArgumentException("Unable to determine game type. Use --engine argument to set the game type."); + + var engine = settings.Engine.Value; + + var gameLocations = new GameLocations( + settings.ModPaths.ToList(), + settings.GamePath!, + GetFallbackPaths(settings.FallbackGamePath, settings.AdditionalFallbackPaths).ToList()); + + + IPlayableObject? target = null; + + // For the manual selector the whole game and mod detection is optional. + // This allows user to use the application for unusual scenarios, + // not known to the detection service. + try + { + var game = GameFinderService.FindGame(gameLocations.GamePath, new GameFinderSettings + { + Engine = engine, + InitMods = false, + SearchFallbackGame = false + }); + target = TryGetPlayableObject(game, gameLocations.ModPaths.FirstOrDefault()); + } + catch (GameNotFoundException e) + { + // TODO: Log + } + + // If the fallback game path is specified we simply try to detect the game and report a warning to the user if not found. + var fallbackGamePath = settings.FallbackGamePath; + if (!string.IsNullOrEmpty(fallbackGamePath)) + { + try + { + GameFinderService.FindGame(fallbackGamePath, new GameFinderSettings + { + InitMods = false, + SearchFallbackGame = false + }); + } + catch (GameNotFoundException e) + { + // TODO: Log + } + } + + return new VerificationTarget + { + Engine = engine, + Location = gameLocations, + Name = GetTargetName(target, gameLocations), + Version = GetTargetVersion(target) + }; + } + + private IPlayableObject TryGetPlayableObject(IGame game, string? modPath) + { + if (string.IsNullOrEmpty(modPath)) + return game; + + var modFinder = ServiceProvider.GetRequiredService(); + var modFactory = ServiceProvider.GetRequiredService(); + + var mods = modFinder.FindMods(game, FileSystem.DirectoryInfo.New(modPath)); + var mod = modFactory.CreatePhysicalMod(game, mods.First(), CultureInfo.InvariantCulture); + return mod; + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs b/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs deleted file mode 100644 index 53b4b20..0000000 --- a/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using AET.ModVerify.App.GameFinder; -using AET.ModVerify.App.Settings; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Infrastructure; -using PG.StarWarsGame.Infrastructure.Mods; -using PG.StarWarsGame.Infrastructure.Services.Dependencies; - -namespace AET.ModVerify.App.ModSelectors; - -internal abstract class ModSelectorBase : IModSelector -{ - protected readonly ILogger? Logger; - protected readonly GameFinderService GameFinderService; - protected readonly IServiceProvider ServiceProvider; - - protected ModSelectorBase(IServiceProvider serviceProvider) - { - ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - Logger = serviceProvider.GetService()?.CreateLogger(GetType()); - GameFinderService = new GameFinderService(serviceProvider); - } - - public abstract GameLocations? Select( - GameInstallationsSettings settings, - out IPhysicalPlayableObject? targetObject, - out GameEngineType? actualEngineType); - - protected GameLocations GetLocations(IPhysicalPlayableObject playableObject, GameFinderResult finderResult, IList additionalFallbackPaths) - { - var fallbacks = GetFallbackPaths(finderResult, playableObject, additionalFallbackPaths); - var modPaths = GetModPaths(playableObject); - return new GameLocations(modPaths.ToList(), playableObject.Game.Directory.FullName, fallbacks.ToList()); - } - - private static IList GetFallbackPaths(GameFinderResult finderResult, IPlayableObject gameOrMod, IList additionalFallbackPaths) - { - var coercedFallbackGame = finderResult.FallbackGame; - if (gameOrMod.Equals(finderResult.FallbackGame)) - coercedFallbackGame = null; - else if (gameOrMod.Game.Equals(finderResult.FallbackGame)) - coercedFallbackGame = null; - - return GetFallbackPaths(coercedFallbackGame?.Directory.FullName, additionalFallbackPaths); - } - - - protected static IList GetFallbackPaths(string? fallbackGame, IList additionalFallbackPaths) - { - var fallbacks = new List(); - if (fallbackGame is not null) - fallbacks.Add(fallbackGame); - foreach (var fallback in additionalFallbackPaths) - fallbacks.Add(fallback); - - return fallbacks; - } - - - private IList GetModPaths(IPhysicalPlayableObject modOrGame) - { - if (modOrGame is not IMod mod) - return Array.Empty(); - - var traverser = ServiceProvider.GetRequiredService(); - return traverser.Traverse(mod) - .OfType().Select(x => x.Directory.FullName) - .ToList(); - } - -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/ModSelectorFactory.cs b/src/ModVerify.CliApp/ModSelectors/ModSelectorFactory.cs deleted file mode 100644 index 07ef263..0000000 --- a/src/ModVerify.CliApp/ModSelectors/ModSelectorFactory.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using AET.ModVerify.App.Settings; - -namespace AET.ModVerify.App.ModSelectors; - -internal class ModSelectorFactory(IServiceProvider serviceProvider) -{ - public IModSelector CreateSelector(GameInstallationsSettings settings) - { - if (settings.Interactive) - return new ConsoleModSelector(serviceProvider); - if (settings.UseAutoDetection) - return new AutomaticModSelector(serviceProvider); - if (settings.ManualSetup) - return new ManualModSelector(serviceProvider); - throw new ArgumentException("Unknown option configuration provided."); - } -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs deleted file mode 100644 index 8ff890a..0000000 --- a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Linq; -using AET.ModVerify.App.GameFinder; -using AET.ModVerify.App.Settings; -using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Infrastructure; - -namespace AET.ModVerify.App.ModSelectors; - -internal class SettingsBasedModSelector(IServiceProvider serviceProvider) -{ - public VerificationTarget CreateInstallationDataFromSettings(GameInstallationsSettings settings) - { - var gameLocations = new ModSelectorFactory(serviceProvider) - .CreateSelector(settings) - .Select(settings, out var targetObject, out var engineType); - - if (gameLocations is null) - throw new GameNotFoundException("Unable to get game locations"); - - if (engineType is null) - throw new InvalidOperationException("Engine type not specified."); - - return new VerificationTarget - { - Engine = engineType.Value, - Location = gameLocations, - Name = GetNameFromGameLocations(targetObject, gameLocations), - Version = GetTargetVersion(targetObject) - }; - } - - private static string GetNameFromGameLocations(IPlayableObject? targetObject, GameLocations gameLocations) - { - if (targetObject is not null) - return targetObject.Name; - - var mod = gameLocations.ModPaths.FirstOrDefault(); - return mod ?? gameLocations.GamePath; - } - - private static string? GetTargetVersion(IPlayableObject? targetObject) - { - // TODO: Implement version retrieval from targetObject if possible - return null; - } -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/SettingsBasedVerificationTargetCreator.cs b/src/ModVerify.CliApp/ModSelectors/SettingsBasedVerificationTargetCreator.cs new file mode 100644 index 0000000..9bb48cf --- /dev/null +++ b/src/ModVerify.CliApp/ModSelectors/SettingsBasedVerificationTargetCreator.cs @@ -0,0 +1,14 @@ +using System; +using AET.ModVerify.App.Settings; + +namespace AET.ModVerify.App.ModSelectors; + +internal class SettingsBasedVerificationTargetCreator(IServiceProvider serviceProvider) +{ + public VerificationTarget CreateFromSettings(GameInstallationsSettings settings) + { + return new VerificationTargetSelectorFactory(serviceProvider) + .CreateSelector(settings) + .Select(settings); + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorBase.cs b/src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorBase.cs new file mode 100644 index 0000000..5afbec2 --- /dev/null +++ b/src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorBase.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Linq; +using AET.ModVerify.App.GameFinder; +using AET.ModVerify.App.Settings; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.StarWarsGame.Engine; +using PG.StarWarsGame.Infrastructure; +using PG.StarWarsGame.Infrastructure.Games; +using PG.StarWarsGame.Infrastructure.Mods; +using PG.StarWarsGame.Infrastructure.Services.Dependencies; + +namespace AET.ModVerify.App.ModSelectors; + +internal abstract class VerificationTargetSelectorBase : IVerificationTargetSelector +{ + protected readonly ILogger? Logger; + protected readonly GameFinderService GameFinderService; + protected readonly IServiceProvider ServiceProvider; + protected readonly IFileSystem FileSystem; + + protected VerificationTargetSelectorBase(IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + Logger = serviceProvider.GetService()?.CreateLogger(GetType()); + GameFinderService = new GameFinderService(serviceProvider); + FileSystem = serviceProvider.GetRequiredService(); + } + + public abstract VerificationTarget Select(GameInstallationsSettings settings); + + protected GameLocations GetLocations( + IPhysicalPlayableObject target, + IGame? fallbackGame, + IReadOnlyList additionalFallbackPaths) + { + var fallbacks = GetFallbackPaths(target, fallbackGame, additionalFallbackPaths); + var modPaths = GetModPaths(target); + return new GameLocations(modPaths, target.Game.Directory.FullName, fallbacks); + } + + private static IReadOnlyList GetFallbackPaths(IPlayableObject target, IGame? fallbackGame, IReadOnlyList additionalFallbackPaths) + { + var coercedFallbackGame = fallbackGame; + if (target is IGame tGame && tGame.Equals(fallbackGame)) + coercedFallbackGame = null; + else if (target.Game.Equals(fallbackGame)) + coercedFallbackGame = null; + return GetFallbackPaths(coercedFallbackGame?.Directory.FullName, additionalFallbackPaths); + } + + + protected static IReadOnlyList GetFallbackPaths(string? fallbackGame, IReadOnlyList additionalFallbackPaths) + { + var fallbacks = new List(); + if (fallbackGame is not null) + fallbacks.Add(fallbackGame); + foreach (var fallback in additionalFallbackPaths) + fallbacks.Add(fallback); + return fallbacks; + } + + + private IReadOnlyList GetModPaths(IPhysicalPlayableObject modOrGame) + { + if (modOrGame is not IMod mod) + return []; + + var traverser = ServiceProvider.GetRequiredService(); + return traverser.Traverse(mod) + .OfType().Select(x => x.Directory.FullName) + .ToList(); + } + + protected static string GetTargetName(IPlayableObject? targetObject, GameLocations gameLocations) + { + if (targetObject is not null) + return targetObject.Name; + + // TODO: Reuse name beautifier from GameInfrastructure lib + var mod = gameLocations.ModPaths.FirstOrDefault(); + return mod ?? gameLocations.GamePath; + } + + protected static string? GetTargetVersion(IPlayableObject? targetObject) + { + if (targetObject is null) + return null; + + // TODO: Implement version retrieval from targetObject if possible + return null; + } + +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorFactory.cs b/src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorFactory.cs new file mode 100644 index 0000000..3dd6b9a --- /dev/null +++ b/src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorFactory.cs @@ -0,0 +1,18 @@ +using System; +using AET.ModVerify.App.Settings; + +namespace AET.ModVerify.App.ModSelectors; + +internal sealed class VerificationTargetSelectorFactory(IServiceProvider serviceProvider) +{ + public IVerificationTargetSelector CreateSelector(GameInstallationsSettings settings) + { + if (settings.Interactive) + return new ConsoleSelector(serviceProvider); + if (settings.UseAutoDetection) + return new AutomaticSelector(serviceProvider); + if (settings.ManualSetup) + return new ManualSelector(serviceProvider); + throw new ArgumentException("Unknown option configuration provided."); + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index cd59804..2c3b602 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -71,8 +71,10 @@ private async Task RunVerify() VerificationTarget verificationTarget; try { - verificationTarget = new SettingsBasedModSelector(services) - .CreateInstallationDataFromSettings(settings.GameInstallationsSettings); + var targetSettings = settings.GameInstallationsSettings; + verificationTarget = new VerificationTargetSelectorFactory(services) + .CreateSelector(targetSettings) + .Select(targetSettings); } catch (GameNotFoundException ex) { diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index e47583a..bafb11d 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -15,7 +15,7 @@ "FromModPath": { "commandName": "Project", - "commandLineArgs": "-o verifyResults --baseline focBaseline.json --path C:/test --type Foc" + "commandLineArgs": "verify -o verifyResults --path \"C:\\Program Files (x86)\\Steam\\steamapps\\common\\Star Wars Empire at War\\corruption\"" } } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs b/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs index 0e5e203..4590a9d 100644 --- a/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs @@ -41,10 +41,10 @@ internal abstract class BaseModVerifyOptions 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'. " + + [Option("engine", Required = false, Default = null, + HelpText = "The game engine of the target 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; init; } + public GameEngineType? Engine { get; init; } [Option("additionalFallbackPaths", Required = false, Separator = ';', diff --git a/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs b/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs index 97f1536..d833f12 100644 --- a/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs @@ -27,10 +27,12 @@ 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", SetName = "baselineSelection", Required = false, HelpText = "Path to a JSON baseline file. Cannot be used together with --searchBaseline.")] public string? Baseline { get; init; } + // TODO: Ignore, if baseline is set [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; } diff --git a/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs b/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs index 00bc4f0..d928774 100644 --- a/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs +++ b/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs @@ -16,13 +16,13 @@ internal sealed record GameInstallationsSettings public string? AutoPath { get; init; } - public IList ModPaths { get; init; } = []; + public IReadOnlyList ModPaths { get; init; } = []; public string? GamePath { get; init; } public string? FallbackGamePath { get; init; } - public IList AdditionalFallbackPaths { get; init; } = []; + public IReadOnlyList AdditionalFallbackPaths { get; init; } = []; - public GameEngineType? EngineType { get; init; } + public GameEngineType? Engine { 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 3c5535f..61f690b 100644 --- a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs @@ -107,7 +107,7 @@ static bool SearchLocally(BaseModVerifyOptions o) { if (o is not VerifyVerbOption v) return false; - return v.SearchBaselineLocally || v.LaunchedWithoutArguments(); + return v.SearchBaselineLocally; } } @@ -153,7 +153,7 @@ private GameInstallationsSettings BuildInstallationSettings(BaseModVerifyOptions GamePath = gamePath, FallbackGamePath = fallbackGamePath, AdditionalFallbackPaths = fallbackPaths, - EngineType = options.GameType + Engine = options.Engine }; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs index b2e7ab0..fb3c68a 100644 --- a/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs +++ b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs @@ -10,12 +10,12 @@ internal static class ExtensionMethods { public static GameEngineType ToEngineType(this GameType type) { - return type == GameType.Foc ? GameEngineType.Foc : GameEngineType.Eaw; + return (GameEngineType)(int)type; } public static GameType FromEngineType(this GameEngineType type) { - return type == GameEngineType.Foc ? GameType.Foc : GameType.Eaw; + return (GameType)(int)type; } extension(ApplicationEnvironment modVerifyEnvironment) diff --git a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs index 294baf7..8d28949 100644 --- a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs +++ b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs @@ -4,7 +4,10 @@ using System.IO.Abstractions; using ModVerify.CliApp.Test.TestData; using Testably.Abstractions; +#if NETFRAMEWORK using ModVerify.CliApp.Test.Utilities; +#endif + namespace ModVerify.CliApp.Test; From 3f799643aab87c492c3c81fc490f35ff250fd74b Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 14 Dec 2025 15:38:32 +0100 Subject: [PATCH 08/22] rename stuff --- .../IVerificationTargetSelector.cs | 8 ----- .../SettingsBasedVerificationTargetCreator.cs | 14 --------- src/ModVerify.CliApp/ModVerifyApplication.cs | 6 ++-- .../CommandLine/BaseModVerifyOptions.cs | 4 +-- .../Settings/ModVerifyAppSettings.cs | 4 +-- .../Settings/SettingsBuilder.cs | 16 +++++----- ...tings.cs => VerificationTargetSettings.cs} | 10 +++---- .../AutomaticSelector.cs | 22 +++++--------- .../ConsoleSelector.cs | 18 ++++------- .../IVerificationTargetSelector.cs | 8 +++++ .../ManualSelector.cs | 22 +++++--------- .../VerificationTargetSelectorBase.cs | 30 +++++++++++++++---- .../VerificationTargetSelectorFactory.cs | 4 +-- .../EmbeddedBaselineTest.cs | 2 +- 14 files changed, 77 insertions(+), 91 deletions(-) delete mode 100644 src/ModVerify.CliApp/ModSelectors/IVerificationTargetSelector.cs delete mode 100644 src/ModVerify.CliApp/ModSelectors/SettingsBasedVerificationTargetCreator.cs rename src/ModVerify.CliApp/Settings/{GameInstallationsSettings.cs => VerificationTargetSettings.cs} (59%) rename src/ModVerify.CliApp/{ModSelectors => TargetSelectors}/AutomaticSelector.cs (92%) rename src/ModVerify.CliApp/{ModSelectors => TargetSelectors}/ConsoleSelector.cs (85%) create mode 100644 src/ModVerify.CliApp/TargetSelectors/IVerificationTargetSelector.cs rename src/ModVerify.CliApp/{ModSelectors => TargetSelectors}/ManualSelector.cs (83%) rename src/ModVerify.CliApp/{ModSelectors => TargetSelectors}/VerificationTargetSelectorBase.cs (74%) rename src/ModVerify.CliApp/{ModSelectors => TargetSelectors}/VerificationTargetSelectorFactory.cs (79%) diff --git a/src/ModVerify.CliApp/ModSelectors/IVerificationTargetSelector.cs b/src/ModVerify.CliApp/ModSelectors/IVerificationTargetSelector.cs deleted file mode 100644 index d9fe438..0000000 --- a/src/ModVerify.CliApp/ModSelectors/IVerificationTargetSelector.cs +++ /dev/null @@ -1,8 +0,0 @@ -using AET.ModVerify.App.Settings; - -namespace AET.ModVerify.App.ModSelectors; - -internal interface IVerificationTargetSelector -{ - VerificationTarget Select(GameInstallationsSettings settings); -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/SettingsBasedVerificationTargetCreator.cs b/src/ModVerify.CliApp/ModSelectors/SettingsBasedVerificationTargetCreator.cs deleted file mode 100644 index 9bb48cf..0000000 --- a/src/ModVerify.CliApp/ModSelectors/SettingsBasedVerificationTargetCreator.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using AET.ModVerify.App.Settings; - -namespace AET.ModVerify.App.ModSelectors; - -internal class SettingsBasedVerificationTargetCreator(IServiceProvider serviceProvider) -{ - public VerificationTarget CreateFromSettings(GameInstallationsSettings settings) - { - return new VerificationTargetSelectorFactory(serviceProvider) - .CreateSelector(settings) - .Select(settings); - } -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index 2c3b602..69d0f68 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -1,5 +1,4 @@ -using AET.ModVerify.App.ModSelectors; -using AET.ModVerify.App.Reporting; +using AET.ModVerify.App.Reporting; using AET.ModVerify.App.Settings; using AET.ModVerify.Pipeline; using AET.ModVerify.Reporting; @@ -16,6 +15,7 @@ using System.Linq; using System.Threading.Tasks; using AET.ModVerify.App.GameFinder; +using AET.ModVerify.App.TargetSelectors; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace AET.ModVerify.App; @@ -71,7 +71,7 @@ private async Task RunVerify() VerificationTarget verificationTarget; try { - var targetSettings = settings.GameInstallationsSettings; + var targetSettings = settings.VerificationTargetSettings; verificationTarget = new VerificationTargetSelectorFactory(services) .CreateSelector(targetSettings) .Select(targetSettings); diff --git a/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs b/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs index 4590a9d..36c8509 100644 --- a/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs @@ -21,9 +21,9 @@ internal abstract class BaseModVerifyOptions 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 sub-mods 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 and base games itself. " + "The argument cannot be combined with any of --mods, --game or --fallbackGame")] - public string? AutoPath { get; init; } + public string? TargetPath { 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. " + diff --git a/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs b/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs index 3376354..e65464c 100644 --- a/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs +++ b/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs @@ -6,13 +6,13 @@ namespace AET.ModVerify.App.Settings; internal sealed class ModVerifyAppSettings { - public bool Interactive => GameInstallationsSettings.Interactive; + public bool Interactive => VerificationTargetSettings.Interactive; public required VerifyPipelineSettings VerifyPipelineSettings { get; init; } public required ModVerifyReportSettings ReportSettings { get; init; } - public required GameInstallationsSettings GameInstallationsSettings { get; init; } + public required VerificationTargetSettings VerificationTargetSettings { get; init; } public VerificationSeverity? AppThrowsOnMinimumSeverity { get; init; } diff --git a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs index 61f690b..984099b 100644 --- a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs @@ -44,7 +44,7 @@ private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) } }, AppThrowsOnMinimumSeverity = verifyOptions.MinimumFailureSeverity, - GameInstallationsSettings = BuildInstallationSettings(verifyOptions), + VerificationTargetSettings = BuildInstallationSettings(verifyOptions), ReportSettings = BuildReportSettings(verifyOptions), }; @@ -85,7 +85,7 @@ private ModVerifyAppSettings BuildFromCreateBaselineVerb(CreateBaselineVerbOptio FailFast = false, }, AppThrowsOnMinimumSeverity = null, - GameInstallationsSettings = BuildInstallationSettings(baselineVerb), + VerificationTargetSettings = BuildInstallationSettings(baselineVerb), ReportSettings = BuildReportSettings(baselineVerb), NewBaselinePath = baselineVerb.OutputFile, }; @@ -111,7 +111,7 @@ static bool SearchLocally(BaseModVerifyOptions o) } } - private GameInstallationsSettings BuildInstallationSettings(BaseModVerifyOptions options) + private VerificationTargetSettings BuildInstallationSettings(BaseModVerifyOptions options) { var modPaths = new List(); if (options.ModPaths is not null) @@ -142,13 +142,13 @@ private GameInstallationsSettings BuildInstallationSettings(BaseModVerifyOptions if (!string.IsNullOrEmpty(gamePath) && !string.IsNullOrEmpty(options.FallbackGamePath)) fallbackGamePath = _fileSystem.Path.GetFullPath(options.FallbackGamePath!); - var autoPath = options.AutoPath; - if (!string.IsNullOrEmpty(autoPath)) - autoPath = _fileSystem.Path.GetFullPath(autoPath!); + var targetPath = options.TargetPath; + if (!string.IsNullOrEmpty(targetPath)) + targetPath = _fileSystem.Path.GetFullPath(targetPath!); - return new GameInstallationsSettings + return new VerificationTargetSettings { - AutoPath = autoPath, + TargetPath = targetPath, ModPaths = modPaths, GamePath = gamePath, FallbackGamePath = fallbackGamePath, diff --git a/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs b/src/ModVerify.CliApp/Settings/VerificationTargetSettings.cs similarity index 59% rename from src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs rename to src/ModVerify.CliApp/Settings/VerificationTargetSettings.cs index d928774..6e205de 100644 --- a/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs +++ b/src/ModVerify.CliApp/Settings/VerificationTargetSettings.cs @@ -4,17 +4,17 @@ namespace AET.ModVerify.App.Settings; -internal sealed record GameInstallationsSettings +internal sealed record VerificationTargetSettings { - public bool Interactive => string.IsNullOrEmpty(AutoPath) && ModPaths.Count == 0 && string.IsNullOrEmpty(GamePath); + public bool Interactive => string.IsNullOrEmpty(TargetPath) && ModPaths.Count == 0 && string.IsNullOrEmpty(GamePath) && string.IsNullOrEmpty(FallbackGamePath); - [MemberNotNullWhen(true, nameof(AutoPath))] - public bool UseAutoDetection => !string.IsNullOrEmpty(AutoPath); + [MemberNotNullWhen(true, nameof(TargetPath))] + public bool UseAutoDetection => !string.IsNullOrEmpty(TargetPath); [MemberNotNullWhen(true, nameof(GamePath))] public bool ManualSetup => !string.IsNullOrEmpty(GamePath); - public string? AutoPath { get; init; } + public string? TargetPath { get; init; } public IReadOnlyList ModPaths { get; init; } = []; diff --git a/src/ModVerify.CliApp/ModSelectors/AutomaticSelector.cs b/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs similarity index 92% rename from src/ModVerify.CliApp/ModSelectors/AutomaticSelector.cs rename to src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs index 216598a..b301da4 100644 --- a/src/ModVerify.CliApp/ModSelectors/AutomaticSelector.cs +++ b/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs @@ -14,15 +14,16 @@ using PG.StarWarsGame.Infrastructure.Services; using PG.StarWarsGame.Infrastructure.Services.Detection; -namespace AET.ModVerify.App.ModSelectors; +namespace AET.ModVerify.App.TargetSelectors; internal class AutomaticSelector(IServiceProvider serviceProvider) : VerificationTargetSelectorBase(serviceProvider) { private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); - public override VerificationTarget Select(GameInstallationsSettings settings) + internal override SelectionResult SelectTarget( + VerificationTargetSettings settings) { - var targetPath = settings.AutoPath; + var targetPath = settings.TargetPath; if (targetPath is null) throw new InvalidOperationException("path to verify cannot be null."); @@ -44,7 +45,7 @@ public override VerificationTarget Select(GameInstallationsSettings settings) var targetObject = GetAttachedModOrGame(finderResult, engine, targetPath); - + if (targetObject is null) { if (!settings.Engine.HasValue) @@ -64,15 +65,8 @@ public override VerificationTarget Select(GameInstallationsSettings settings) throw new ArgumentException($"The specified game type '{engine}' does not match the actual type of the game or mod to verify."); locations = GetLocations(targetObject, finderResult.FallbackGame, settings.AdditionalFallbackPaths); } - - return new VerificationTarget - { - Engine = engine.Value, - Location = locations, - Name = GetTargetName(targetObject, locations), - Version = GetTargetVersion(targetObject) - }; - + + return new(locations, engine.Value, targetObject); } private IPhysicalPlayableObject? GetAttachedModOrGame(GameFinderResult finderResult, GameEngineType? requestedEngineType, string targetPath) @@ -101,7 +95,7 @@ public override VerificationTarget Select(GameInstallationsSettings settings) GetMatchingModFromGame(finderResult.FallbackGame, requestedEngineType, targetFullPath); } - private GameLocations GetDetachedModLocations(string modPath, GameFinderResult gameResult, GameInstallationsSettings settings, out IPhysicalMod mod) + private GameLocations GetDetachedModLocations(string modPath, GameFinderResult gameResult, VerificationTargetSettings settings, out IPhysicalMod mod) { IGame game = null!; diff --git a/src/ModVerify.CliApp/ModSelectors/ConsoleSelector.cs b/src/ModVerify.CliApp/TargetSelectors/ConsoleSelector.cs similarity index 85% rename from src/ModVerify.CliApp/ModSelectors/ConsoleSelector.cs rename to src/ModVerify.CliApp/TargetSelectors/ConsoleSelector.cs index a1701f3..8c54cef 100644 --- a/src/ModVerify.CliApp/ModSelectors/ConsoleSelector.cs +++ b/src/ModVerify.CliApp/TargetSelectors/ConsoleSelector.cs @@ -9,25 +9,17 @@ using PG.StarWarsGame.Infrastructure.Games; using PG.StarWarsGame.Infrastructure.Mods; -namespace AET.ModVerify.App.ModSelectors; +namespace AET.ModVerify.App.TargetSelectors; internal class ConsoleSelector(IServiceProvider serviceProvider) : VerificationTargetSelectorBase(serviceProvider) { - public override VerificationTarget Select(GameInstallationsSettings settings) + internal override SelectionResult SelectTarget(VerificationTargetSettings settings) { var gameResult = GameFinderService.FindGames(GameFinderSettings.Default); - var targetObject = SelectPlayableObject(gameResult); + var targetObject = SelectPlayableObject(gameResult); var engine = targetObject.Game.Type.ToEngineType(); - - var gameLocations = GetLocations(targetObject, gameResult.FallbackGame, settings.AdditionalFallbackPaths); - - return new VerificationTarget - { - Engine = engine, - Location = gameLocations, - Name = GetTargetName(targetObject, gameLocations), - Version = GetTargetVersion(targetObject) - }; + var locations = GetLocations(targetObject, gameResult.FallbackGame, settings.AdditionalFallbackPaths); + return new(locations, engine, targetObject); } private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult finderResult) diff --git a/src/ModVerify.CliApp/TargetSelectors/IVerificationTargetSelector.cs b/src/ModVerify.CliApp/TargetSelectors/IVerificationTargetSelector.cs new file mode 100644 index 0000000..6d452e8 --- /dev/null +++ b/src/ModVerify.CliApp/TargetSelectors/IVerificationTargetSelector.cs @@ -0,0 +1,8 @@ +using AET.ModVerify.App.Settings; + +namespace AET.ModVerify.App.TargetSelectors; + +internal interface IVerificationTargetSelector +{ + VerificationTarget Select(VerificationTargetSettings settings); +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/ManualSelector.cs b/src/ModVerify.CliApp/TargetSelectors/ManualSelector.cs similarity index 83% rename from src/ModVerify.CliApp/ModSelectors/ManualSelector.cs rename to src/ModVerify.CliApp/TargetSelectors/ManualSelector.cs index 0cbd5e4..410d10d 100644 --- a/src/ModVerify.CliApp/ModSelectors/ManualSelector.cs +++ b/src/ModVerify.CliApp/TargetSelectors/ManualSelector.cs @@ -10,11 +10,11 @@ using PG.StarWarsGame.Infrastructure.Services; using PG.StarWarsGame.Infrastructure.Services.Detection; -namespace AET.ModVerify.App.ModSelectors; +namespace AET.ModVerify.App.TargetSelectors; internal class ManualSelector(IServiceProvider serviceProvider) : VerificationTargetSelectorBase(serviceProvider) { - public override VerificationTarget Select(GameInstallationsSettings settings) + internal override SelectionResult SelectTarget(VerificationTargetSettings settings) { if (string.IsNullOrEmpty(settings.GamePath)) throw new ArgumentException("Argument --game must be set."); @@ -23,13 +23,13 @@ public override VerificationTarget Select(GameInstallationsSettings settings) var engine = settings.Engine.Value; - var gameLocations = new GameLocations( + var gameLocations = new GameLocations( settings.ModPaths.ToList(), settings.GamePath!, GetFallbackPaths(settings.FallbackGamePath, settings.AdditionalFallbackPaths).ToList()); - IPlayableObject? target = null; + IPhysicalPlayableObject? target = null; // For the manual selector the whole game and mod detection is optional. // This allows user to use the application for unusual scenarios, @@ -54,7 +54,7 @@ public override VerificationTarget Select(GameInstallationsSettings settings) if (!string.IsNullOrEmpty(fallbackGamePath)) { try - { + { GameFinderService.FindGame(fallbackGamePath, new GameFinderSettings { InitMods = false, @@ -67,16 +67,10 @@ public override VerificationTarget Select(GameInstallationsSettings settings) } } - return new VerificationTarget - { - Engine = engine, - Location = gameLocations, - Name = GetTargetName(target, gameLocations), - Version = GetTargetVersion(target) - }; + return new SelectionResult(gameLocations, engine, target); } - - private IPlayableObject TryGetPlayableObject(IGame game, string? modPath) + + private IPhysicalPlayableObject TryGetPlayableObject(IGame game, string? modPath) { if (string.IsNullOrEmpty(modPath)) return game; diff --git a/src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorBase.cs b/src/ModVerify.CliApp/TargetSelectors/VerificationTargetSelectorBase.cs similarity index 74% rename from src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorBase.cs rename to src/ModVerify.CliApp/TargetSelectors/VerificationTargetSelectorBase.cs index 5afbec2..7d353fa 100644 --- a/src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorBase.cs +++ b/src/ModVerify.CliApp/TargetSelectors/VerificationTargetSelectorBase.cs @@ -12,10 +12,15 @@ using PG.StarWarsGame.Infrastructure.Mods; using PG.StarWarsGame.Infrastructure.Services.Dependencies; -namespace AET.ModVerify.App.ModSelectors; +namespace AET.ModVerify.App.TargetSelectors; internal abstract class VerificationTargetSelectorBase : IVerificationTargetSelector { + internal sealed record SelectionResult( + GameLocations Locations, + GameEngineType Engine, + IPhysicalPlayableObject? Target); + protected readonly ILogger? Logger; protected readonly GameFinderService GameFinderService; protected readonly IServiceProvider ServiceProvider; @@ -29,7 +34,22 @@ protected VerificationTargetSelectorBase(IServiceProvider serviceProvider) FileSystem = serviceProvider.GetRequiredService(); } - public abstract VerificationTarget Select(GameInstallationsSettings settings); + public VerificationTarget Select(VerificationTargetSettings settings) + { + var selectedTarget = SelectTarget(settings); + + return new VerificationTarget + { + Location = selectedTarget.Locations, + Engine = selectedTarget.Engine, + Name = GetTargetName(selectedTarget.Target, selectedTarget.Locations), + Version = GetTargetVersion(selectedTarget.Target) + }; + } + + + internal abstract SelectionResult SelectTarget(VerificationTargetSettings settings); + protected GameLocations GetLocations( IPhysicalPlayableObject target, @@ -41,7 +61,7 @@ protected GameLocations GetLocations( return new GameLocations(modPaths, target.Game.Directory.FullName, fallbacks); } - private static IReadOnlyList GetFallbackPaths(IPlayableObject target, IGame? fallbackGame, IReadOnlyList additionalFallbackPaths) + private static IReadOnlyList GetFallbackPaths(IPhysicalPlayableObject target, IGame? fallbackGame, IReadOnlyList additionalFallbackPaths) { var coercedFallbackGame = fallbackGame; if (target is IGame tGame && tGame.Equals(fallbackGame)) @@ -74,7 +94,7 @@ private IReadOnlyList GetModPaths(IPhysicalPlayableObject modOrGame) .ToList(); } - protected static string GetTargetName(IPlayableObject? targetObject, GameLocations gameLocations) + protected static string GetTargetName(IPhysicalPlayableObject? targetObject, GameLocations gameLocations) { if (targetObject is not null) return targetObject.Name; @@ -84,7 +104,7 @@ protected static string GetTargetName(IPlayableObject? targetObject, GameLocatio return mod ?? gameLocations.GamePath; } - protected static string? GetTargetVersion(IPlayableObject? targetObject) + protected static string? GetTargetVersion(IPhysicalPlayableObject? targetObject) { if (targetObject is null) return null; diff --git a/src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorFactory.cs b/src/ModVerify.CliApp/TargetSelectors/VerificationTargetSelectorFactory.cs similarity index 79% rename from src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorFactory.cs rename to src/ModVerify.CliApp/TargetSelectors/VerificationTargetSelectorFactory.cs index 3dd6b9a..5e106eb 100644 --- a/src/ModVerify.CliApp/ModSelectors/VerificationTargetSelectorFactory.cs +++ b/src/ModVerify.CliApp/TargetSelectors/VerificationTargetSelectorFactory.cs @@ -1,11 +1,11 @@ using System; using AET.ModVerify.App.Settings; -namespace AET.ModVerify.App.ModSelectors; +namespace AET.ModVerify.App.TargetSelectors; internal sealed class VerificationTargetSelectorFactory(IServiceProvider serviceProvider) { - public IVerificationTargetSelector CreateSelector(GameInstallationsSettings settings) + public IVerificationTargetSelector CreateSelector(VerificationTargetSettings settings) { if (settings.Interactive) return new ConsoleSelector(serviceProvider); diff --git a/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs index cde1dad..6bb8bb4 100644 --- a/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs +++ b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs @@ -18,7 +18,7 @@ public class BaselineSelectorTest private static readonly ModVerifyAppSettings TestSettings = new() { ReportSettings = new(), - GameInstallationsSettings = new (), + VerificationTargetSettings = new (), VerifyPipelineSettings = new() { GameVerifySettings = new GameVerifySettings(), From 0a674e6545a3e0045acc7448d24d37d146ca5241 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 16 Dec 2025 14:06:27 +0100 Subject: [PATCH 09/22] start testing automatic selector --- .../TargetSelectors/AutomaticSelector.cs | 10 ++- test/ModVerify.CliApp.Test/CommonTestBase.cs | 10 ++- .../TargetSelectors/AutomaticSelectorTest.cs | 77 +++++++++++++++++++ 3 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 test/ModVerify.CliApp.Test/TargetSelectors/AutomaticSelectorTest.cs diff --git a/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs b/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs index b301da4..2915fc0 100644 --- a/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs +++ b/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs @@ -20,8 +20,7 @@ internal class AutomaticSelector(IServiceProvider serviceProvider) : Verificatio { private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); - internal override SelectionResult SelectTarget( - VerificationTargetSettings settings) + internal override SelectionResult SelectTarget(VerificationTargetSettings settings) { var targetPath = settings.TargetPath; if (targetPath is null) @@ -76,7 +75,7 @@ internal override SelectionResult SelectTarget( // If the target is the game directory itself. if (targetFullPath.Equals(finderResult.Game.Directory.FullName, StringComparison.OrdinalIgnoreCase)) { - if (finderResult.Game.Type.ToEngineType() != requestedEngineType) + if (!IsEngineTypeSupported(requestedEngineType, finderResult.Game)) throw new ArgumentException($"The specified game type '{requestedEngineType}' does not match the actual type of the game '{targetPath}' to verify."); return finderResult.Game; } @@ -144,4 +143,9 @@ private GameLocations GetDetachedModLocations(string modPath, GameFinderResult g return null; } + + private static bool IsEngineTypeSupported(GameEngineType? requestedEngineType, IPhysicalPlayableObject target) + { + return !requestedEngineType.HasValue || target.Game.Type.ToEngineType() == requestedEngineType; + } } \ No newline at end of file diff --git a/test/ModVerify.CliApp.Test/CommonTestBase.cs b/test/ModVerify.CliApp.Test/CommonTestBase.cs index 7d6dc18..20325ab 100644 --- a/test/ModVerify.CliApp.Test/CommonTestBase.cs +++ b/test/ModVerify.CliApp.Test/CommonTestBase.cs @@ -1,8 +1,11 @@ -using System; -using System.IO.Abstractions; +using AET.SteamAbstraction; using AnakinRaW.CommonUtilities.Hashing; using Microsoft.Extensions.DependencyInjection; using PG.Commons; +using PG.StarWarsGame.Infrastructure; +using PG.StarWarsGame.Infrastructure.Clients.Steam; +using System; +using System.IO.Abstractions; using Testably.Abstractions.Testing; namespace ModVerify.CliApp.Test; @@ -18,6 +21,9 @@ protected CommonTestBase() sc.AddSingleton(sp => new HashingService(sp)); sc.AddSingleton(FileSystem); PetroglyphCommons.ContributeServices(sc); + PetroglyphGameInfrastructure.InitializeServices(sc); + SteamAbstractionLayer.InitializeServices(sc); + SteamPetroglyphStarWarsGameClients.InitializeServices(sc); // ReSharper disable once VirtualMemberCallInConstructor SetupServices(sc); ServiceProvider = sc.BuildServiceProvider(); diff --git a/test/ModVerify.CliApp.Test/TargetSelectors/AutomaticSelectorTest.cs b/test/ModVerify.CliApp.Test/TargetSelectors/AutomaticSelectorTest.cs new file mode 100644 index 0000000..d376010 --- /dev/null +++ b/test/ModVerify.CliApp.Test/TargetSelectors/AutomaticSelectorTest.cs @@ -0,0 +1,77 @@ +namespace ModVerify.CliApp.Test.TargetSelectors; + +public class AutomaticSelectorTest : CommonTestBase +{ + //private readonly AutomaticSelector _selector; + //private readonly IRegistry _registry = new InMemoryRegistry(InMemoryRegistryCreationFlags.WindowsLike); + + //public AutomaticSelectorTest() + //{ + // _selector = new AutomaticSelector(ServiceProvider); + //} + + //protected override void SetupServices(ServiceCollection serviceCollection) + //{ + // base.SetupServices(serviceCollection); + // serviceCollection.AddSingleton(_registry); + //} + + //[Fact] + //public void Test_Select_GameNotInstalled() + //{ + // var settings = new VerificationTargetSettings + // { + // TargetPath = "/test", + // }; + + // Assert.Throws(() => _selector.SelectTarget(settings)); + //} + + //[Theory] + //[MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))] + //public void Test_Select_Game(GameIdentity identity) + //{ + // var (eaw, foc) = InstallGames(identity.Platform); + // var game = identity.Type switch + // { + // GameType.Eaw => eaw, + // GameType.Foc => foc, + // _ => throw new ArgumentOutOfRangeException() + // }; + + // var settings = new VerificationTargetSettings + // { + // TargetPath = game.Directory.FullName, + // }; + + // var result = _selector.SelectTarget(settings); + // Assert.Equal(identity.Type, result.Engine.FromEngineType()); + // Assert.Equal(game, result.Target); + // Assert.Equal(game.Directory.FullName, result.Locations.GamePath); + //} + + //private (IGame eaw, IGame foc) InstallGames(GamePlatform platform) + //{ + // var eaw = FileSystem.InstallGame(new GameIdentity(GameType.Eaw, platform), ServiceProvider); + // var foc = FileSystem.InstallGame(new GameIdentity(GameType.Eaw, platform), ServiceProvider); + + + + // if (platform == GamePlatform.SteamGold) + // { + // InstallSteam(eaw, foc); + // } + + // return (eaw, foc); + //} + + //private void InstallSteam(IGame eaw, IGame foc) + //{ + // var registry = ServiceProvider.GetRequiredService().CreateRegistry(); + // FileSystem.InstallSteam(registry); + + // // Register Game to Steam + // var lib = FileSystem.InstallDefaultLibrary(ServiceProvider); + // lib.InstallGame(32470, "Star Wars Empire at War", [32472], SteamAppState.StateFullyInstalled); + //} +} \ No newline at end of file From 0887111e506f527abb2a74a08e4d1ddb68ac04e2 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 23 Dec 2025 19:21:38 +0100 Subject: [PATCH 10/22] fix and test automatic selector --- .../GameFinder/GameFinderService.cs | 92 +++- src/ModVerify.CliApp/ModVerifyApplication.cs | 13 + .../Settings/VerificationTargetSettings.cs | 2 +- .../TargetSelectors/AutomaticSelector.cs | 178 +++--- .../TargetSelectors/ManualSelector.cs | 40 +- .../TargetNotFoundException.cs | 6 + test/ModVerify.CliApp.Test/CommonTestBase.cs | 30 +- .../EmbeddedBaselineTest.cs | 1 + .../ModVerify.CliApp.Test.csproj | 29 +- .../ModVerifyOptionsParserTest.cs | 1 + .../TargetSelectors/AutomaticSelectorTest.cs | 508 +++++++++++++++--- 11 files changed, 678 insertions(+), 222 deletions(-) create mode 100644 src/ModVerify.CliApp/TargetSelectors/TargetNotFoundException.cs diff --git a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs index 8cc2739..aca0533 100644 --- a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs +++ b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO.Abstractions; using Microsoft.Extensions.DependencyInjection; @@ -51,7 +52,7 @@ public GameFinderResult FindGames(GameFinderSettings settings) return FindGames(detectors, settings); } - + public IGame FindGame(string gamePath, GameFinderSettings settings) { var detectors = new List @@ -61,6 +62,25 @@ public IGame FindGame(string gamePath, GameFinderSettings settings) return FindGames(detectors, settings).Game; } + public bool TryFindGame(string gamePath, GameFinderSettings settings, [NotNullWhen(true)]out IGame? game) + { + var detectors = new List + { + new DirectoryGameDetector(_fileSystem.DirectoryInfo.New(gamePath), _serviceProvider), + }; + + try + { + game = FindGames(detectors, settings).Game; + return true; + } + catch (GameNotFoundException) + { + game = null; + return false; + } + } + public GameFinderResult FindGamesFromPathOrGlobal(string path, GameFinderSettings settings) { // There are four common situations: @@ -88,55 +108,79 @@ public GameFinderResult FindGamesFromPathOrGlobal(string path, GameFinderSetting private GameFinderResult FindGames(IList detectors, GameFinderSettings settings) { - // FoC needs to be tried first - if (!TryDetectGame(GameType.Foc, detectors, out var result)) + GameDetectionResult? detectionResult = null; + if (settings.Engine is GameEngineType.Eaw) + { + _logger?.LogTrace("Trying to find requested EaW installation."); + if (!TryDetectGame(GameType.Eaw, detectors, out detectionResult)) + { + var e = new GameNotFoundException($"Unable to find requested game installation '{settings.Engine}'. Wrong install path?"); + _logger?.LogTrace(e, e.Message); + throw e; + } + } + + if (detectionResult is null && !TryDetectGame(GameType.Foc, detectors, out detectionResult)) { + if (settings.Engine is GameEngineType.Foc) + { + var e = new GameNotFoundException($"Unable to find requested game installation '{settings.Engine}'. Wrong install path?"); + _logger?.LogTrace(e, e.Message); + throw e; + } + + // If the engine is unspecified, we also need to check for EaW. _logger?.LogTrace("Unable to find FoC installation. Trying again with EaW..."); - if (!TryDetectGame(GameType.Eaw, detectors, out result)) + if (!TryDetectGame(GameType.Eaw, detectors, out detectionResult)) throw new GameNotFoundException("Unable to find game installation: Wrong install path?"); } - if (result.GameLocation is null) + if (detectionResult.GameLocation is null) throw new GameNotFoundException("Unable to find game installation: Wrong install path?"); _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, - "Found game installation: {ResultGameIdentity} at {GameLocationFullName}", result.GameIdentity, result.GameLocation.FullName); + "Found game installation: {ResultGameIdentity} at {GameLocationFullName}", detectionResult.GameIdentity, detectionResult.GameLocation.FullName); - var game = _gameFactory.CreateGame(result, CultureInfo.InvariantCulture); + var game = _gameFactory.CreateGame(detectionResult, CultureInfo.InvariantCulture); if (settings.InitMods) SetupMods(game); IGame? fallbackGame = null; - if (settings.SearchFallbackGame) + if (SearchForFallbackGame(settings, detectionResult)) { - // If the game is Foc we want to set up Eaw as well as the fallbackGame - if (game.Type == GameType.Foc) - { - var fallbackDetectors = new List(); + var fallbackDetectors = new List(); - if (game.Platform == GamePlatform.SteamGold) - fallbackDetectors.Add(new SteamPetroglyphStarWarsGameDetector(_serviceProvider)); - else - throw new NotImplementedException("Searching fallback game for non-Steam games is currently is not yet implemented."); + if (game.Platform == GamePlatform.SteamGold) + fallbackDetectors.Add(new SteamPetroglyphStarWarsGameDetector(_serviceProvider)); + else + throw new NotImplementedException("Searching fallback game for non-Steam games is currently is not yet implemented."); - if (!TryDetectGame(GameType.Eaw, fallbackDetectors, out var fallbackResult) || fallbackResult.GameLocation is null) - throw new GameNotFoundException("Unable to find fallback game installation: Wrong install path?"); + 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(ModVerifyConstants.ConsoleEventId, - "Found fallback game installation: {FallbackResultGameIdentity} at {GameLocationFullName}", fallbackResult.GameIdentity, fallbackResult.GameLocation.FullName); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, + "Found fallback game installation: {FallbackResultGameIdentity} at {GameLocationFullName}", fallbackResult.GameIdentity, fallbackResult.GameLocation.FullName); - fallbackGame = _gameFactory.CreateGame(fallbackResult, CultureInfo.InvariantCulture); + fallbackGame = _gameFactory.CreateGame(fallbackResult, CultureInfo.InvariantCulture); - if (settings.InitMods) - SetupMods(fallbackGame); - } + if (settings.InitMods) + SetupMods(fallbackGame); } return new GameFinderResult(game, fallbackGame); } + private static bool SearchForFallbackGame(GameFinderSettings settings, GameDetectionResult? foundGame) + { + if (settings.Engine is GameEngineType.Eaw) + return false; + if (foundGame is { Installed: true, GameIdentity.Type: GameType.Eaw }) + return false; + return settings.SearchFallbackGame; + } + private bool TryDetectGame(GameType gameType, IList detectors, out GameDetectionResult result) { var gd = new CompositeGameDetector(detectors, _serviceProvider); diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index 69d0f68..fb3ebab 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -76,6 +76,19 @@ private async Task RunVerify() .CreateSelector(targetSettings) .Select(targetSettings); } + catch (ArgumentException ex) + { + ConsoleUtilities.WriteApplicationFatalError(_appEnvironment.ApplicationName, + $"The specified arguments are not correct: {ex.Message}"); + _logger?.LogError(ex, "Invalid application arguments: {Message}", ex.Message); + return ex.HResult; + } + catch (TargetNotFoundException ex) + { + ConsoleUtilities.WriteApplicationFatalError(_appEnvironment.ApplicationName, ex.Message); + _logger?.LogError(ex, ex.Message); + return ex.HResult; + } catch (GameNotFoundException ex) { ConsoleUtilities.WriteApplicationFatalError(_appEnvironment.ApplicationName, diff --git a/src/ModVerify.CliApp/Settings/VerificationTargetSettings.cs b/src/ModVerify.CliApp/Settings/VerificationTargetSettings.cs index 6e205de..b5e1a40 100644 --- a/src/ModVerify.CliApp/Settings/VerificationTargetSettings.cs +++ b/src/ModVerify.CliApp/Settings/VerificationTargetSettings.cs @@ -9,7 +9,7 @@ internal sealed record VerificationTargetSettings public bool Interactive => string.IsNullOrEmpty(TargetPath) && ModPaths.Count == 0 && string.IsNullOrEmpty(GamePath) && string.IsNullOrEmpty(FallbackGamePath); [MemberNotNullWhen(true, nameof(TargetPath))] - public bool UseAutoDetection => !string.IsNullOrEmpty(TargetPath); + public bool UseAutoDetection => !string.IsNullOrEmpty(TargetPath) && ModPaths.Count == 0 && string.IsNullOrEmpty(GamePath) && string.IsNullOrEmpty(FallbackGamePath); [MemberNotNullWhen(true, nameof(GamePath))] public bool ManualSetup => !string.IsNullOrEmpty(GamePath); diff --git a/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs b/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs index 2915fc0..f6b2b0c 100644 --- a/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs +++ b/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs @@ -1,8 +1,4 @@ -using System; -using System.Globalization; -using System.IO.Abstractions; -using System.Linq; -using AET.ModVerify.App.GameFinder; +using AET.ModVerify.App.GameFinder; using AET.ModVerify.App.Settings; using AET.ModVerify.App.Utilities; using Microsoft.Extensions.DependencyInjection; @@ -13,6 +9,13 @@ using PG.StarWarsGame.Infrastructure.Mods; using PG.StarWarsGame.Infrastructure.Services; using PG.StarWarsGame.Infrastructure.Services.Detection; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO.Abstractions; +using System.Linq; namespace AET.ModVerify.App.TargetSelectors; @@ -22,95 +25,130 @@ internal class AutomaticSelector(IServiceProvider serviceProvider) : Verificatio internal override SelectionResult SelectTarget(VerificationTargetSettings settings) { + if (!settings.UseAutoDetection) + throw new ArgumentException("wrong settings format provided.", nameof(settings)); + var targetPath = settings.TargetPath; - if (targetPath is null) - throw new InvalidOperationException("path to verify cannot be null."); + if (!_fileSystem.Directory.Exists(targetPath)) + { + Logger?.LogError(ModVerifyConstants.ConsoleEventId, "The specified path '{Path}' does not exist.", targetPath); + throw new TargetNotFoundException(targetPath); + } var engine = settings.Engine; GameFinderResult finderResult; try { - finderResult = GameFinderService.FindGamesFromPathOrGlobal(targetPath, GameFinderSettings.Default); + var finderSettings = new GameFinderSettings + { + Engine = engine, + InitMods = true, + SearchFallbackGame = true + }; + finderResult = GameFinderService.FindGamesFromPathOrGlobal(targetPath, finderSettings); } catch (GameNotFoundException) { - Logger?.LogError(ModVerifyConstants.ConsoleEventId, "Unable to find games based of the specified target path '{Path}'. Consider specifying all paths manually.", settings.GamePath); + Logger?.LogError(ModVerifyConstants.ConsoleEventId, + "Unable to find games based of the specified target path '{Path}'. Consider specifying all paths manually.", targetPath); throw; } + // In a Steam scenario, there is a chance that the user specified a FoC targetPath, + // but requested EaW engine. This does not make sense, and we need to check against this. + if (finderResult.Game.Platform == GamePlatform.SteamGold && engine is GameEngineType.Eaw) + { + var targetIsFoc = GameFinderService.TryFindGame(targetPath, + new GameFinderSettings { Engine = GameEngineType.Foc, InitMods = false, SearchFallbackGame = false }, + out _); + if (targetIsFoc) + ThrowEngineNotSupported(engine.Value, targetPath); + } - GameLocations locations; + if (engine.HasValue) + { + if (finderResult.Game.Type.ToEngineType() != engine.Value) + { + if (finderResult.FallbackGame?.Type.ToEngineType() != engine) + { + throw new InvalidOperationException(); + } + } + } + - var targetObject = GetAttachedModOrGame(finderResult, engine, targetPath); + GameLocations locations; + var targetObject = GetAttachedModOrGame(finderResult, targetPath, engine); - if (targetObject is null) + if (targetObject is not null) { - if (!settings.Engine.HasValue) + var actualType = targetObject.Game.Type; + Debug.Assert(IsEngineTypeSupported(engine, actualType)); + engine ??= actualType.ToEngineType(); + locations = GetLocations(targetObject, finderResult.FallbackGame, settings.AdditionalFallbackPaths); + } + else + { + if (!engine.HasValue) throw new ArgumentException("Game engine not specified. Use --engine argument to set it."); Logger?.LogDebug("The requested mod at '{TargetPath}' is detached from its games.", targetPath); // The path is a detached mod, that exists on a different location than the game. - locations = GetDetachedModLocations(targetPath, finderResult, settings, out var mod); + locations = GetDetachedModLocations(targetPath, finderResult, engine.Value, settings.AdditionalFallbackPaths, out var mod); targetObject = mod; } - else - { - var actualType = targetObject.Game.Type.ToEngineType(); - engine ??= actualType; - if (engine != actualType) - throw new ArgumentException($"The specified game type '{engine}' does not match the actual type of the game or mod to verify."); - locations = GetLocations(targetObject, finderResult.FallbackGame, settings.AdditionalFallbackPaths); - } - + return new(locations, engine.Value, targetObject); } - private IPhysicalPlayableObject? GetAttachedModOrGame(GameFinderResult finderResult, GameEngineType? requestedEngineType, string targetPath) + private IPhysicalPlayableObject? GetAttachedModOrGame(GameFinderResult finderResult, string targetPath, GameEngineType? requestedEngineType) { var targetFullPath = _fileSystem.Path.GetFullPath(targetPath); + IPhysicalPlayableObject? target = null; + // If the target is the game directory itself. - if (targetFullPath.Equals(finderResult.Game.Directory.FullName, StringComparison.OrdinalIgnoreCase)) + if (targetFullPath.Equals(finderResult.Game.Directory.FullName, StringComparison.OrdinalIgnoreCase)) + target = finderResult.Game; + else if (finderResult.FallbackGame is not null && targetFullPath.Equals(finderResult.FallbackGame.Directory.FullName, StringComparison.OrdinalIgnoreCase)) { - if (!IsEngineTypeSupported(requestedEngineType, finderResult.Game)) - throw new ArgumentException($"The specified game type '{requestedEngineType}' does not match the actual type of the game '{targetPath}' to verify."); - return finderResult.Game; + // The game detection identified both Foc and Eaw because either Steam is installed or requestedEngineType was not specified. + // The requested path points to a EaW installation. + Debug.Assert(finderResult.FallbackGame.Type is GameType.Eaw); + target = finderResult.FallbackGame; } - if (finderResult.FallbackGame is not null && - targetFullPath.Equals(finderResult.FallbackGame.Directory.FullName, StringComparison.OrdinalIgnoreCase)) - { - throw new NotImplementedException("When does this actually happen???"); + target ??= GetMatchingModFromGame(finderResult.Game, targetFullPath, requestedEngineType) ?? + GetMatchingModFromGame(finderResult.FallbackGame, targetFullPath, requestedEngineType); - if (finderResult.FallbackGame.Type.ToEngineType() != requestedEngineType) - throw new ArgumentException($"The specified game type '{requestedEngineType}' does not match the actual type of the game '{targetPath}' to verify."); - return finderResult.FallbackGame; + + if (target is not null) + { + if (!IsEngineTypeSupported(requestedEngineType, target.Game.Type)) + ThrowEngineNotSupported(requestedEngineType.Value, targetPath); } - return GetMatchingModFromGame(finderResult.Game, requestedEngineType, targetFullPath) ?? - GetMatchingModFromGame(finderResult.FallbackGame, requestedEngineType, targetFullPath); + return target; } - private GameLocations GetDetachedModLocations(string modPath, GameFinderResult gameResult, VerificationTargetSettings settings, out IPhysicalMod mod) + private GameLocations GetDetachedModLocations( + string modPath, + GameFinderResult gameResult, + GameEngineType requestedGameEngine, + IReadOnlyList additionalFallbackPaths, + out IPhysicalMod mod) { - IGame game = null!; - - if (gameResult.Game.Type.ToEngineType() == settings.Engine) - game = gameResult.Game; - if (gameResult.FallbackGame is not null && gameResult.FallbackGame.Type.ToEngineType() == settings.Engine) - game = gameResult.FallbackGame; - - if (game is null) - throw new GameNotFoundException($"Unable to find game of type '{settings.Engine}'"); + var game = GetTargetGame(gameResult, requestedGameEngine); + Debug.Assert(game is not null); var modFinder = ServiceProvider.GetRequiredService(); var modRef = modFinder.FindMods(game, _fileSystem.DirectoryInfo.New(modPath)).FirstOrDefault(); if (modRef is null) - throw new NotSupportedException($"The mod at '{modPath}' is not compatible to the found game '{game}'."); + ThrowEngineNotSupported(requestedGameEngine, modPath); var modFactory = ServiceProvider.GetRequiredService(); mod = modFactory.CreatePhysicalMod(game, modRef, CultureInfo.InvariantCulture); @@ -119,33 +157,45 @@ private GameLocations GetDetachedModLocations(string modPath, GameFinderResult g mod.ResolveDependencies(); - return GetLocations(mod, gameResult.FallbackGame, settings.AdditionalFallbackPaths); + return GetLocations(mod, gameResult.FallbackGame, additionalFallbackPaths); } - private static IPhysicalMod? GetMatchingModFromGame(IGame? game, GameEngineType? requestedEngineType, string modPath) + private static IPhysicalMod? GetMatchingModFromGame(IGame? game, string modPath, GameEngineType? requestedEngineType) { - if (game is null) + if (game is null || !IsEngineTypeSupported(requestedEngineType, game.Type)) return null; - var isGameSupported = !requestedEngineType.HasValue || game.Type.ToEngineType() == requestedEngineType; foreach (var mod in game.Game.Mods) { - if (mod is IPhysicalMod physicalMod) - { - if (physicalMod.Directory.FullName.Equals(modPath, StringComparison.OrdinalIgnoreCase)) - { - if (!isGameSupported) - throw new ArgumentException($"The specified engine type '{requestedEngineType}' does not match the required of the mod '{modPath}' to verify."); - return physicalMod; - } - } + if (mod is not IPhysicalMod physicalMod) + continue; + + if (physicalMod.Directory.FullName.Equals(modPath, StringComparison.OrdinalIgnoreCase)) + return physicalMod; } return null; } - - private static bool IsEngineTypeSupported(GameEngineType? requestedEngineType, IPhysicalPlayableObject target) + + private static IGame? GetTargetGame(GameFinderResult finderResult, GameEngineType? requestedEngine) + { + if (!requestedEngine.HasValue) + return null; + if (finderResult.Game.Type.ToEngineType() == requestedEngine) + return finderResult.Game; + if (finderResult.FallbackGame is not null && finderResult.FallbackGame.Type.ToEngineType() == requestedEngine) + return finderResult.FallbackGame; + return null; + } + + private static bool IsEngineTypeSupported([NotNullWhen(false)] GameEngineType? requestedEngineType, GameType actualGameType) + { + return !requestedEngineType.HasValue || actualGameType.ToEngineType() == requestedEngineType; + } + + [DoesNotReturn] + private static void ThrowEngineNotSupported(GameEngineType requested, string targetPath) { - return !requestedEngineType.HasValue || target.Game.Type.ToEngineType() == requestedEngineType; + throw new ArgumentException($"The specified game engine '{requested}' does not match engine of the verification target '{targetPath}'."); } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/TargetSelectors/ManualSelector.cs b/src/ModVerify.CliApp/TargetSelectors/ManualSelector.cs index 410d10d..f963b4f 100644 --- a/src/ModVerify.CliApp/TargetSelectors/ManualSelector.cs +++ b/src/ModVerify.CliApp/TargetSelectors/ManualSelector.cs @@ -20,7 +20,7 @@ internal override SelectionResult SelectTarget(VerificationTargetSettings settin throw new ArgumentException("Argument --game must be set."); if (!settings.Engine.HasValue) throw new ArgumentException("Unable to determine game type. Use --engine argument to set the game type."); - + var engine = settings.Engine.Value; var gameLocations = new GameLocations( @@ -29,49 +29,37 @@ internal override SelectionResult SelectTarget(VerificationTargetSettings settin GetFallbackPaths(settings.FallbackGamePath, settings.AdditionalFallbackPaths).ToList()); - IPhysicalPlayableObject? target = null; - // For the manual selector the whole game and mod detection is optional. // This allows user to use the application for unusual scenarios, // not known to the detection service. - try - { - var game = GameFinderService.FindGame(gameLocations.GamePath, new GameFinderSettings - { - Engine = engine, - InitMods = false, - SearchFallbackGame = false - }); - target = TryGetPlayableObject(game, gameLocations.ModPaths.FirstOrDefault()); - } - catch (GameNotFoundException e) + if (!GameFinderService.TryFindGame(gameLocations.GamePath, + new GameFinderSettings { Engine = engine, InitMods = false, SearchFallbackGame = false }, + out var game)) { // TODO: Log } - + // If the fallback game path is specified we simply try to detect the game and report a warning to the user if not found. var fallbackGamePath = settings.FallbackGamePath; if (!string.IsNullOrEmpty(fallbackGamePath)) { - try - { - GameFinderService.FindGame(fallbackGamePath, new GameFinderSettings - { - InitMods = false, - SearchFallbackGame = false - }); - } - catch (GameNotFoundException e) + + if (!GameFinderService.TryFindGame(fallbackGamePath, + new GameFinderSettings { InitMods = false, SearchFallbackGame = false }, + out _)) { // TODO: Log } } - + + var target = TryGetPlayableObject(game, gameLocations.ModPaths.FirstOrDefault()); return new SelectionResult(gameLocations, engine, target); } - private IPhysicalPlayableObject TryGetPlayableObject(IGame game, string? modPath) + private IPhysicalPlayableObject? TryGetPlayableObject(IGame? game, string? modPath) { + if (game is null) + return null; if (string.IsNullOrEmpty(modPath)) return game; diff --git a/src/ModVerify.CliApp/TargetSelectors/TargetNotFoundException.cs b/src/ModVerify.CliApp/TargetSelectors/TargetNotFoundException.cs new file mode 100644 index 0000000..d6b052e --- /dev/null +++ b/src/ModVerify.CliApp/TargetSelectors/TargetNotFoundException.cs @@ -0,0 +1,6 @@ +using System.IO; + +namespace AET.ModVerify.App.TargetSelectors; + +internal class TargetNotFoundException(string path) + : DirectoryNotFoundException($"The target path '{path}' does not exist"); \ No newline at end of file diff --git a/test/ModVerify.CliApp.Test/CommonTestBase.cs b/test/ModVerify.CliApp.Test/CommonTestBase.cs index 20325ab..a93b970 100644 --- a/test/ModVerify.CliApp.Test/CommonTestBase.cs +++ b/test/ModVerify.CliApp.Test/CommonTestBase.cs @@ -4,32 +4,18 @@ using PG.Commons; using PG.StarWarsGame.Infrastructure; using PG.StarWarsGame.Infrastructure.Clients.Steam; -using System; -using System.IO.Abstractions; -using Testably.Abstractions.Testing; namespace ModVerify.CliApp.Test; -public abstract class CommonTestBase +public abstract class CommonTestBase : AET.Testing.TestBaseWithFileSystem { - 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); - PetroglyphGameInfrastructure.InitializeServices(sc); - SteamAbstractionLayer.InitializeServices(sc); - SteamPetroglyphStarWarsGameClients.InitializeServices(sc); - // ReSharper disable once VirtualMemberCallInConstructor - SetupServices(sc); - ServiceProvider = sc.BuildServiceProvider(); - } - - protected virtual void SetupServices(ServiceCollection serviceCollection) + protected override void SetupServices(IServiceCollection serviceCollection) { + base.SetupServices(serviceCollection); + serviceCollection.AddSingleton(sp => new HashingService(sp)); + PetroglyphCommons.ContributeServices(serviceCollection); + PetroglyphGameInfrastructure.InitializeServices(serviceCollection); + SteamAbstractionLayer.InitializeServices(serviceCollection); + SteamPetroglyphStarWarsGameClients.InitializeServices(serviceCollection); } } \ No newline at end of file diff --git a/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs index 6bb8bb4..3272261 100644 --- a/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs +++ b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs @@ -9,6 +9,7 @@ using System.IO.Abstractions; using ModVerify.CliApp.Test.TestData; using Testably.Abstractions; +using Xunit; namespace ModVerify.CliApp.Test; diff --git a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj index 84d1bba..2a54398 100644 --- a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj +++ b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj @@ -3,31 +3,36 @@ net10.0 $(TargetFrameworks);net481 - false preview + + false + true + Exe + + - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + + - - - + + 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 index 8d28949..c5caf92 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 Xunit; #if NETFRAMEWORK using ModVerify.CliApp.Test.Utilities; #endif diff --git a/test/ModVerify.CliApp.Test/TargetSelectors/AutomaticSelectorTest.cs b/test/ModVerify.CliApp.Test/TargetSelectors/AutomaticSelectorTest.cs index d376010..10156ac 100644 --- a/test/ModVerify.CliApp.Test/TargetSelectors/AutomaticSelectorTest.cs +++ b/test/ModVerify.CliApp.Test/TargetSelectors/AutomaticSelectorTest.cs @@ -1,77 +1,439 @@ -namespace ModVerify.CliApp.Test.TargetSelectors; +using AET.Modinfo.Model; +using AET.Modinfo.Spec.Steam; +using AET.ModVerify.App.GameFinder; +using AET.ModVerify.App.Settings; +using AET.ModVerify.App.TargetSelectors; +using AET.ModVerify.App.Utilities; +using AET.SteamAbstraction.Testing; +using AnakinRaW.CommonUtilities.Registry; +using Microsoft.Extensions.DependencyInjection; +using PG.StarWarsGame.Engine; +using PG.StarWarsGame.Infrastructure.Games; +using PG.StarWarsGame.Infrastructure.Testing; +using PG.StarWarsGame.Infrastructure.Testing.Installations; +using PG.StarWarsGame.Infrastructure.Testing.Installations.Game; +using System; +using Xunit; + +namespace ModVerify.CliApp.Test.TargetSelectors; public class AutomaticSelectorTest : CommonTestBase { - //private readonly AutomaticSelector _selector; - //private readonly IRegistry _registry = new InMemoryRegistry(InMemoryRegistryCreationFlags.WindowsLike); - - //public AutomaticSelectorTest() - //{ - // _selector = new AutomaticSelector(ServiceProvider); - //} - - //protected override void SetupServices(ServiceCollection serviceCollection) - //{ - // base.SetupServices(serviceCollection); - // serviceCollection.AddSingleton(_registry); - //} - - //[Fact] - //public void Test_Select_GameNotInstalled() - //{ - // var settings = new VerificationTargetSettings - // { - // TargetPath = "/test", - // }; - - // Assert.Throws(() => _selector.SelectTarget(settings)); - //} - - //[Theory] - //[MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))] - //public void Test_Select_Game(GameIdentity identity) - //{ - // var (eaw, foc) = InstallGames(identity.Platform); - // var game = identity.Type switch - // { - // GameType.Eaw => eaw, - // GameType.Foc => foc, - // _ => throw new ArgumentOutOfRangeException() - // }; - - // var settings = new VerificationTargetSettings - // { - // TargetPath = game.Directory.FullName, - // }; - - // var result = _selector.SelectTarget(settings); - // Assert.Equal(identity.Type, result.Engine.FromEngineType()); - // Assert.Equal(game, result.Target); - // Assert.Equal(game.Directory.FullName, result.Locations.GamePath); - //} - - //private (IGame eaw, IGame foc) InstallGames(GamePlatform platform) - //{ - // var eaw = FileSystem.InstallGame(new GameIdentity(GameType.Eaw, platform), ServiceProvider); - // var foc = FileSystem.InstallGame(new GameIdentity(GameType.Eaw, platform), ServiceProvider); - - - - // if (platform == GamePlatform.SteamGold) - // { - // InstallSteam(eaw, foc); - // } - - // return (eaw, foc); - //} - - //private void InstallSteam(IGame eaw, IGame foc) - //{ - // var registry = ServiceProvider.GetRequiredService().CreateRegistry(); - // FileSystem.InstallSteam(registry); - - // // Register Game to Steam - // var lib = FileSystem.InstallDefaultLibrary(ServiceProvider); - // lib.InstallGame(32470, "Star Wars Empire at War", [32472], SteamAppState.StateFullyInstalled); - //} + private readonly AutomaticSelector _selector; + private readonly IRegistry _registry = new InMemoryRegistry(InMemoryRegistryCreationFlags.WindowsLike); + + public AutomaticSelectorTest() + { + _selector = new AutomaticSelector(ServiceProvider); + } + + protected override void SetupServices(IServiceCollection serviceCollection) + { + base.SetupServices(serviceCollection); + serviceCollection.AddSingleton(_registry); + } + + [Fact] + public void Test_SelectTarget_GameNotInstalled() + { + var settings = new VerificationTargetSettings + { + TargetPath = "/test", + }; + Assert.Throws(() => _selector.SelectTarget(settings)); + } + + [Fact] + public void Test_SelectTarget_WrongSettings() + { + var settings = new VerificationTargetSettings + { + TargetPath = "/test", + FallbackGamePath = "does/not/exist", + ModPaths = ["also/does/not/exist"], + GamePath = "not/found" + }; + Assert.Throws(() => _selector.SelectTarget(settings)); + } + + [Theory] + [MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))] + public void Test_SelectTarget_FromGamePath(IGameIdentity identity) + { + TestSelectTarget( + identity, + i => i, + gi => new VerificationTargetSettings + { + TargetPath = gi.PlayableObject.Directory.FullName, + Engine = null + }); + TestSelectTarget( + identity, + i => i, + gi => new VerificationTargetSettings + { + TargetPath = gi.PlayableObject.Directory.FullName, + Engine = gi.PlayableObject.Game.Type.ToEngineType() + }); + } + + [Theory] + [MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))] + public void Test_SelectTarget_FromGamePath_OppositeEngine(IGameIdentity identity) + { + if (identity.Platform is GamePlatform.SteamGold) + { + TestSelectTarget(identity, + i => i, + gi => new VerificationTargetSettings + { + TargetPath = gi.PlayableObject.Directory.FullName, + Engine = gi.PlayableObject.Game.Type.Opposite().ToEngineType(), + }, + typeof(ArgumentException)); + } + else + { + TestSelectTarget(identity, + i => i, + gi => new VerificationTargetSettings + { + TargetPath = gi.PlayableObject.Directory.FullName, + Engine = gi.PlayableObject.Game.Type.Opposite().ToEngineType(), + }, + typeof(GameNotFoundException)); + } + } + + [Theory] + [MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))] + public void Test_SelectTarget_ModInModsDir(IGameIdentity identity) + { + TestSelectTarget( + identity, + gameInstallation => gameInstallation.InstallMod("MyMod", false), + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = null + }); + TestSelectTarget( + identity, + gameInstallation => gameInstallation.InstallMod("MyMod", false), + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = ti.GameInstallation.Game.Type.ToEngineType() + }); + } + + [Theory] + [MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))] + public void Test_SelectTarget_ModInModsDir_WrongGameEngine_Throws(IGameIdentity identity) + { + var expectedExceptionType = identity.Platform == GamePlatform.SteamGold + ? typeof(ArgumentException) + : typeof(GameNotFoundException); + + TestSelectTarget( + identity, + gameInstallation => gameInstallation.InstallMod("MyMod", false), + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = ti.GameInstallation.Game.Type.Opposite().ToEngineType() + }, expectedExceptionType); + } + + + [Theory] + [InlineData(GameType.Eaw)] + [InlineData(GameType.Foc)] + public void Test_SelectTarget_Workshops_UnknownModEngineType(GameType gameType) + { + var identity = new GameIdentity(gameType, GamePlatform.SteamGold); + TestSelectTarget( + identity, + gameInstallation => gameInstallation.InstallMod("MyMod", true), + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = ti.GameInstallation.Game.Type.ToEngineType() + }); + + TestSelectTarget( + identity, + gameInstallation => gameInstallation.InstallMod("MyMod", true), + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = null // Causes fallback to FoC + }, + overrideAssertData: new OverrideAssertData { GameType = GameType.Foc }); + } + + [Theory] + [InlineData(GameType.Eaw)] + [InlineData(GameType.Foc)] + public void Test_SelectTarget_Workshops_UnspecifiedEngineIsCorrectEngineTyp_KnownModEngineType(GameType gameType) + { + var identity = new GameIdentity(gameType, GamePlatform.SteamGold); + + var modinfo = new ModinfoData("MyMod") + { + SteamData = new SteamData("123456", "123456", SteamWorkshopVisibility.Public, "MyMod", + [gameType.ToString().ToUpper()]) + }; + + TestSelectTarget( + identity, + gameInstallation => + { + var modInstallation = gameInstallation.InstallMod(modinfo, true); + modInstallation.InstallModinfoFile(modinfo); + return modInstallation; + }, + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = null + }); + } + + [Theory] + [InlineData(GameType.Eaw)] + [InlineData(GameType.Foc)] + public void Test_SelectTarget_Workshops_IncompatibleKnownModEngineType_Throws(GameType gameType) + { + var identity = new GameIdentity(gameType, GamePlatform.SteamGold); + + var modinfo = new ModinfoData("MyMod") + { + SteamData = new SteamData("123456", "123456", SteamWorkshopVisibility.Public, "MyMod", + [gameType.ToString().ToUpper()]) + }; + + TestSelectTarget( + identity, + gameInstallation => + { + var modInstallation = gameInstallation.InstallMod(modinfo, true); + modInstallation.InstallModinfoFile(modinfo); + return modInstallation; + }, + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = gameType.Opposite().ToEngineType() + }, + typeof(ArgumentException)); + } + + [Theory] + [InlineData(GameType.Eaw)] + [InlineData(GameType.Foc)] + public void Test_SelectTarget_Workshops_MultipleKnownModEngineTypes(GameType gameType) + { + var identity = new GameIdentity(gameType, GamePlatform.SteamGold); + + var modinfo = new ModinfoData("MyMod") + { + SteamData = new SteamData("123456", "123456", SteamWorkshopVisibility.Public, "MyMod", + [gameType.ToString().ToUpper(), gameType.Opposite().ToString().ToUpper()]) + }; + + TestSelectTarget( + identity, + gameInstallation => + { + var modInstallation = gameInstallation.InstallMod(modinfo, true); + modInstallation.InstallModinfoFile(modinfo); + return modInstallation; + }, + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = gameType.ToEngineType() + }); + + TestSelectTarget( + identity, + gameInstallation => + { + var modInstallation = gameInstallation.InstallMod(modinfo, true); + modInstallation.InstallModinfoFile(modinfo); + return modInstallation; + }, + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = null // Causes fallback to FoC + }, + overrideAssertData: new OverrideAssertData{GameType = GameType.Foc}); + } + + [Theory] + [MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))] + public void Test_SelectTarget_DetachedMod_NoEngineSpecified_Throws(IGameIdentity identity) + { + var exceptionType = identity.Platform is not GamePlatform.SteamGold + ? typeof(GameNotFoundException) + : typeof(ArgumentException); + + TestSelectTarget( + identity, + gameInstallation => + { + var modinfo = new ModinfoData("DetachedMod"); + var modPath = FileSystem.Directory.CreateDirectory("/detachedMod"); + return gameInstallation.InstallMod(modinfo, modPath, false); + }, + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = null // No Engine means we cannot proceed + }, + expectedExceptionType: exceptionType); + } + + [Theory] + [MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))] + public void Test_SelectTarget_DetachedMod(IGameIdentity identity) + { + // Currently, only Steam is supported for detached mods. + var exceptionType = identity.Platform is not GamePlatform.SteamGold + ? typeof(GameNotFoundException) + : null; + + TestSelectTarget( + identity, + gameInstallation => + { + var modinfo = new ModinfoData("DetachedMod"); + var modPath = FileSystem.Directory.CreateDirectory("/detachedMod"); + return gameInstallation.InstallMod(modinfo, modPath, false); + }, + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = identity.Type.ToEngineType() + }, + expectedExceptionType: exceptionType); + } + + [Theory] + [MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))] + public void Test_SelectTarget_AttachedMod_DoesNotExist(IGameIdentity identity) + { + // Currently, only Steam is supported for detached mods. + TestSelectTarget( + identity, + gameInstallation => + { + var modInstallation = gameInstallation.InstallMod("MyMod", GITestUtilities.GetRandomWorkshopFlag(identity)); + modInstallation.Mod.Directory.Delete(true); + return modInstallation; + }, + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = identity.Type.ToEngineType() + }, + typeof(TargetNotFoundException)); + } + + [Theory] + [MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))] + public void Test_SelectTarget_DetachedMod_DoesNotExist(IGameIdentity identity) + { + TestSelectTarget( + identity, + gameInstallation => + { + var modinfo = new ModinfoData("DetachedMod"); + var modPath = FileSystem.DirectoryInfo.New("/detachedMod"); + var modInstallation = gameInstallation.InstallMod(modinfo, modPath, false); + modPath.Delete(true); + return modInstallation; + }, + ti => new VerificationTargetSettings + { + TargetPath = ti.PlayableObject.Directory.FullName, + Engine = identity.Type.ToEngineType() + }, + typeof(TargetNotFoundException)); + } + + private void TestSelectTarget( + IGameIdentity identity, + Func targetFactory, + Func settingsFactory, + Type? expectedExceptionType = null, + OverrideAssertData? overrideAssertData = null) + { + var (eaw, foc) = InstallGames(identity.Platform); + var gameInstallation = identity.Type switch + { + GameType.Eaw => eaw, + GameType.Foc => foc, + _ => throw new ArgumentOutOfRangeException() + }; + + var targetInstallation = targetFactory(gameInstallation); + + var settings = settingsFactory(targetInstallation); + + if (expectedExceptionType is not null) + { + Assert.Throws(expectedExceptionType, () => _selector.SelectTarget(settings)); + return; + } + + if (identity.Type == GameType.Foc && identity.Platform != GamePlatform.SteamGold) + Assert.Throws(() => _selector.SelectTarget(settings)); + else + { + var result = _selector.SelectTarget(settings); + Assert.Equal(overrideAssertData?.GameType ?? identity.Type, result.Engine.FromEngineType()); + Assert.Equal(targetInstallation.PlayableObject.GetType(), result.Target!.GetType()); + Assert.Equal(targetInstallation.PlayableObject.Directory.FullName, result.Locations.TargetPath); + + if (result.Engine == GameEngineType.Foc) + Assert.NotEmpty(result.Locations.FallbackPaths); + else + Assert.Empty(result.Locations.FallbackPaths); + } + } + + private (ITestingGameInstallation eaw, ITestingGameInstallation foc) InstallGames(GamePlatform platform) + { + var eaw = GameInfrastructureTesting.Game(new GameIdentity(GameType.Eaw, platform), ServiceProvider); + var foc = GameInfrastructureTesting.Game(new GameIdentity(GameType.Foc, platform), ServiceProvider); + + GameInfrastructureTesting.Registry(ServiceProvider).CreateInstalled(eaw.Game); + GameInfrastructureTesting.Registry(ServiceProvider).CreateInstalled(foc.Game); + + eaw.InstallMod("OtherEawMod"); + foc.InstallMod("OtherFocMod"); + + if (platform == GamePlatform.SteamGold) + InstallSteam(); + + return (eaw, foc); + } + + private void InstallSteam() + { + var steam = SteamTesting.Steam(ServiceProvider); + steam.Install(); + // Register Game to Steam + var lib = steam.InstallDefaultLibrary(); + lib.InstallGame(32470, "Star Wars Empire at War", [32472]); + } + + private class OverrideAssertData + { + public GameType? GameType { get; init; } + } } \ No newline at end of file From 327c0267e70faee4a1c9cb4c615fd59f727ae69c Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 16 Jan 2026 16:22:46 +0100 Subject: [PATCH 11/22] update to new deps --- .../CommandBar/CommandBarGameManager.cs | 4 ++-- .../PG.StarWarsGame.Engine/GameManagerBase.cs | 5 ++--- .../GuiDialog/Xml/XmlComponentTextureData.cs | 6 +++--- .../PG.StarWarsGame.Engine/IGameManager.cs | 2 +- .../PG.StarWarsGame.Engine.csproj | 11 ++++------- .../Rendering/Animations/AnimationCollection.cs | 11 +++++------ .../Xml/Parsers/Data/CommandBarComponentParser.cs | 4 ++-- .../Xml/Parsers/Data/GameObjectParser.cs | 4 ++-- .../Xml/Parsers/Data/SfxEventParser.cs | 4 ++-- .../Xml/Parsers/File/CommandBarComponentFileParser.cs | 4 ++-- .../Xml/Parsers/File/GameObjectFileParser.cs | 4 ++-- .../Xml/Parsers/File/GuiDialogParser.cs | 4 ++-- .../Xml/Parsers/File/SfxEventFileParser.cs | 4 ++-- .../Xml/Parsers/XmlContainerContentParser.cs | 4 ++-- .../Xml/Parsers/XmlObjectParser.cs | 8 ++++---- .../PG.StarWarsGame.Files.ALO.csproj | 3 --- .../PG.StarWarsGame.Files.ChunkFiles.csproj | 5 +---- .../PG.StarWarsGame.Files.XML.csproj | 3 +-- .../Parsers/Base/IPetroglyphXmlFileContainerParser.cs | 4 ++-- .../Parsers/PetroglyphXmlFileContainerParser.cs | 6 +++--- 20 files changed, 44 insertions(+), 56 deletions(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index 311c212..a007472 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using PG.Commons.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.CommandBar.Components; using PG.StarWarsGame.Engine.CommandBar.Xml; @@ -18,6 +17,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using AnakinRaW.CommonUtilities.Collections; namespace PG.StarWarsGame.Engine.CommandBar; @@ -77,7 +77,7 @@ protected override async Task InitializeCoreAsync(CancellationToken token) var contentParser = new XmlContainerContentParser(ServiceProvider, ErrorReporter); contentParser.XmlParseError += OnParseError; - var parsedCommandBarComponents = new ValueListDictionary(); + var parsedCommandBarComponents = new FrugalValueListDictionary(); try { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs index 130fb50..607121d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs @@ -6,7 +6,6 @@ using AnakinRaW.CommonUtilities.Collections; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using PG.Commons.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; @@ -16,13 +15,13 @@ namespace PG.StarWarsGame.Engine; internal abstract class GameManagerBase(GameRepository repository, GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), IGameManager { - protected readonly ValueListDictionary NamedEntries = new(); + protected readonly FrugalValueListDictionary NamedEntries = new(); public ICollection Entries => NamedEntries.Values; public ICollection EntryKeys => NamedEntries.Keys; - public ReadOnlyFrugalList GetEntries(Crc32 key) + public ImmutableFrugalList GetEntries(Crc32 key) { return NamedEntries.GetValues(key); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/XmlComponentTextureData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/XmlComponentTextureData.cs index 2ed95ce..f102457 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/XmlComponentTextureData.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/XmlComponentTextureData.cs @@ -1,14 +1,14 @@ using System; -using PG.Commons.Collections; +using AnakinRaW.CommonUtilities.Collections; using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.XML; namespace PG.StarWarsGame.Engine.GuiDialog.Xml; -public class XmlComponentTextureData(string componentId, IReadOnlyValueListDictionary textures, XmlLocationInfo location) +public class XmlComponentTextureData(string componentId, IReadOnlyFrugalValueListDictionary textures, XmlLocationInfo location) : XmlObject(location) { public string Component { get; } = componentId ?? throw new ArgumentNullException(componentId); - public IReadOnlyValueListDictionary Textures { get; } = textures ?? throw new ArgumentNullException(nameof(textures)); + public IReadOnlyFrugalValueListDictionary Textures { get; } = textures ?? throw new ArgumentNullException(nameof(textures)); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IGameManager.cs index b9f60a7..750f00c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IGameManager.cs @@ -10,5 +10,5 @@ public interface IGameManager ICollection EntryKeys { get; } - ReadOnlyFrugalList GetEntries(Crc32 key); + ImmutableFrugalList GetEntries(Crc32 key); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index e54e6f0..19b4c1a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -23,10 +23,10 @@ - - - - + + + + @@ -39,7 +39,4 @@ - - - \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/AnimationCollection.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/AnimationCollection.cs index 03916fe..dfd43aa 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/AnimationCollection.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/AnimationCollection.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using AnakinRaW.CommonUtilities; using AnakinRaW.CommonUtilities.Collections; -using PG.Commons.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Files.ALO.Files.Animations; @@ -13,10 +12,10 @@ public sealed class AnimationCollection : DisposableObject, IEnumerable _animations = new(); - private readonly ValueListDictionary _animationCrc = new(); + private readonly FrugalValueListDictionary _animations = new(); + private readonly FrugalValueListDictionary _animationCrc = new(); - public int Cout => _animations.Count; + public int Cout => _animations.ValueCount; public Crc32 GetAnimationCrc(ModelAnimationType type, int subIndex) { @@ -28,12 +27,12 @@ public Crc32 GetAnimationCrc(ModelAnimationType type, int subIndex) return checksumsForType[subIndex]; } - public ReadOnlyFrugalList GetAnimations(ModelAnimationType type) + public ImmutableFrugalList GetAnimations(ModelAnimationType type) { return _animations.GetValues(type); } - public bool TryGetAnimations(ModelAnimationType type, out ReadOnlyFrugalList animations) + public bool TryGetAnimations(ModelAnimationType type, out ImmutableFrugalList animations) { return _animations.TryGetValues(type, out animations); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs index 1e20a8b..297b474 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs @@ -1,7 +1,7 @@ using System; using System.Collections.ObjectModel; using System.Xml.Linq; -using PG.Commons.Collections; +using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.CommandBar.Xml; using PG.StarWarsGame.Engine.Xml.Tags; @@ -12,7 +12,7 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; public sealed class CommandBarComponentParser( - IReadOnlyValueListDictionary parsedElements, + IReadOnlyFrugalValueListDictionary parsedElements, IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : XmlObjectParser(parsedElements, serviceProvider, errorReporter) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs index 4f445a7..5cac3e2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs @@ -1,6 +1,6 @@ using System; using System.Xml.Linq; -using PG.Commons.Collections; +using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Files.XML; @@ -26,7 +26,7 @@ public static class GameObjectXmlTags } public sealed class GameObjectParser( - IReadOnlyValueListDictionary parsedElements, + IReadOnlyFrugalValueListDictionary parsedElements, IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : XmlObjectParser(parsedElements, serviceProvider, errorReporter) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs index 4e3a5cb..bc3439f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs @@ -1,7 +1,7 @@ using System; using System.Collections.ObjectModel; using System.Xml.Linq; -using PG.Commons.Collections; +using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.Audio.Sfx; using PG.StarWarsGame.Engine.Xml.Tags; @@ -12,7 +12,7 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; public sealed class SfxEventParser( - IReadOnlyValueListDictionary parsedElements, + IReadOnlyFrugalValueListDictionary parsedElements, IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : XmlObjectParser(parsedElements, serviceProvider, errorReporter) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs index a32ff09..763d244 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs @@ -1,6 +1,6 @@ using System; using System.Xml.Linq; -using PG.Commons.Collections; +using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.CommandBar.Xml; using PG.StarWarsGame.Engine.Xml.Parsers.Data; @@ -12,7 +12,7 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers.File; internal class CommandBarComponentFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : PetroglyphXmlFileContainerParser(serviceProvider, errorReporter) { - protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) + protected override void Parse(XElement element, IFrugalValueListDictionary parsedElements, string fileName) { var parser = new CommandBarComponentParser(parsedElements, ServiceProvider, ErrorReporter); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameObjectFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameObjectFileParser.cs index d2abfed..ffeafff 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameObjectFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameObjectFileParser.cs @@ -1,6 +1,6 @@ using System; using System.Xml.Linq; -using PG.Commons.Collections; +using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Engine.Xml.Parsers.Data; @@ -12,7 +12,7 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers.File; internal class GameObjectFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : PetroglyphXmlFileContainerParser(serviceProvider, errorReporter) { - protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) + protected override void Parse(XElement element, IFrugalValueListDictionary parsedElements, string fileName) { var parser = new GameObjectParser(parsedElements, ServiceProvider, ErrorReporter); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GuiDialogParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GuiDialogParser.cs index 7c23dd6..851aa9f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GuiDialogParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GuiDialogParser.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Xml.Linq; -using PG.Commons.Collections; +using AnakinRaW.CommonUtilities.Collections; using PG.StarWarsGame.Engine.GuiDialog.Xml; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; @@ -49,7 +49,7 @@ private GuiDialogsXmlTextureData ParseTextures(XElement? element, string fileNam private XmlComponentTextureData ParseTexture(XElement texture) { var componentId = GetTagName(texture); - var textures = new ValueListDictionary(); + var textures = new FrugalValueListDictionary(); foreach (var entry in texture.Elements()) textures.Add(entry.Name.ToString(), PetroglyphXmlStringParser.Instance.Parse(entry)); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/SfxEventFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/SfxEventFileParser.cs index f40dd1a..841d805 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/SfxEventFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/SfxEventFileParser.cs @@ -1,6 +1,6 @@ using System; using System.Xml.Linq; -using PG.Commons.Collections; +using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.Audio.Sfx; using PG.StarWarsGame.Engine.Xml.Parsers.Data; @@ -12,7 +12,7 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers.File; internal class SfxEventFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : PetroglyphXmlFileContainerParser(serviceProvider, errorReporter) { - protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) + protected override void Parse(XElement element, IFrugalValueListDictionary parsedElements, string fileName) { var parser = new SfxEventParser(parsedElements, ServiceProvider, ErrorReporter); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs index 0c911e2..00f4f61 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs @@ -1,9 +1,9 @@ using System; using System.Linq; using System.Xml; +using AnakinRaW.CommonUtilities.Collections; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using PG.Commons.Collections; using PG.Commons.Hashing; using PG.Commons.Services; using PG.StarWarsGame.Engine.IO; @@ -34,7 +34,7 @@ public void ParseEntriesFromFileListXml( string xmlFile, IGameRepository gameRepository, string lookupPath, - ValueListDictionary entries, + FrugalValueListDictionary entries, Action? onFileParseAction = null) where T : notnull { Logger.LogDebug("Parsing container data '{XmlFile}'", xmlFile); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs index fead02d..1565e9e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs @@ -1,7 +1,7 @@ using System; using System.Xml.Linq; +using AnakinRaW.CommonUtilities.Collections; using Microsoft.Extensions.DependencyInjection; -using PG.Commons.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; @@ -9,7 +9,7 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers; public abstract class XmlObjectParser( - IReadOnlyValueListDictionary parsedElements, + IReadOnlyFrugalValueListDictionary parsedElements, IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : XmlObjectParser(parsedElements, serviceProvider, errorReporter) where TObject : XmlObject @@ -34,12 +34,12 @@ public readonly struct EmptyParseState public abstract class XmlObjectParser( - IReadOnlyValueListDictionary parsedElements, + IReadOnlyFrugalValueListDictionary parsedElements, IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : PetroglyphXmlElementParser(errorReporter) where TObject : XmlObject { - protected IReadOnlyValueListDictionary ParsedElements { get; } = + protected IReadOnlyFrugalValueListDictionary ParsedElements { get; } = parsedElements ?? throw new ArgumentNullException(nameof(parsedElements)); protected ICrc32HashingService HashingService { get; } = serviceProvider.GetRequiredService(); 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 c653074..052190c 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 @@ -24,7 +24,4 @@ - - - \ 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 36221be..f49643c 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,9 +17,6 @@ preview - - - - + \ No newline at end of file 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 fbb055c..75fecfd 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,8 +18,7 @@ preview - - + diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs index f42a9a9..cd21e00 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs @@ -1,10 +1,10 @@ using System.IO; -using PG.Commons.Collections; +using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; namespace PG.StarWarsGame.Files.XML.Parsers; public interface IPetroglyphXmlFileContainerParser : IPetroglyphXmlParser where T : notnull { - void ParseFile(Stream xmlStream, IValueListDictionary parsedEntries); + void ParseFile(Stream xmlStream, IFrugalValueListDictionary parsedEntries); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs index 4e371e2..d2b862a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs @@ -1,7 +1,7 @@ using System; using System.IO; using System.Xml.Linq; -using PG.Commons.Collections; +using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Files.XML.ErrorHandling; @@ -10,12 +10,12 @@ namespace PG.StarWarsGame.Files.XML.Parsers; public abstract class PetroglyphXmlFileContainerParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? listener = null) : PetroglyphXmlFileParserBase(serviceProvider, listener), IPetroglyphXmlFileContainerParser where T : notnull { - public void ParseFile(Stream xmlStream, IValueListDictionary parsedEntries) + public void ParseFile(Stream xmlStream, IFrugalValueListDictionary parsedEntries) { var root = GetRootElement(xmlStream, out var fileName); if (root is not null) Parse(root, parsedEntries, fileName); } - protected abstract void Parse(XElement element, IValueListDictionary parsedElements, string fileName); + protected abstract void Parse(XElement element, IFrugalValueListDictionary parsedElements, string fileName); } \ No newline at end of file From fd808567eff30308d9529a0cc054611eb4442179 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 16 Jan 2026 16:38:24 +0100 Subject: [PATCH 12/22] update to new deps (just to make it compile) --- src/ModVerify/ModVerify.csproj | 12 +- .../Pipeline/GameVerifierPipelineStep.cs | 35 +-- src/ModVerify/Pipeline/GameVerifyPipeline.cs | 240 ++++++++++-------- .../Verifiers/DuplicateNameFinder.cs | 10 +- 4 files changed, 158 insertions(+), 139 deletions(-) diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index 89c4b64..905b020 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -33,10 +33,10 @@ - - - - + + + + @@ -51,8 +51,4 @@ - - - - diff --git a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs index 6405dbd..f201d6b 100644 --- a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs +++ b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging; using System; using System.Threading; +using System.Threading.Tasks; using AET.ModVerify.Pipeline.Progress; namespace AET.ModVerify.Pipeline; @@ -22,23 +23,27 @@ public sealed class GameVerifierPipelineStep( public long Size => 1; - protected override void RunCore(CancellationToken token) + protected override async Task RunCoreAsync(CancellationToken token) { - try + await Task.Run(() => { - Logger?.LogDebug("Running verifier '{Name}'...", GameVerifier.FriendlyName); - ReportProgress(new ProgressEventArgs(0.0, "Started")); - - GameVerifier.Progress += OnVerifyProgress; - GameVerifier.Verify(token); - - Logger?.LogDebug("Finished verifier '{Name}'", GameVerifier.FriendlyName); - ReportProgress(new ProgressEventArgs(1.0, "Finished")); - } - finally - { - GameVerifier.Progress += OnVerifyProgress; - } + try + { + Logger?.LogDebug("Running verifier '{Name}'...", GameVerifier.FriendlyName); + ReportProgress(new ProgressEventArgs(0.0, "Started")); + + GameVerifier.Progress += OnVerifyProgress; + GameVerifier.Verify(token); + + Logger?.LogDebug("Finished verifier '{Name}'", GameVerifier.FriendlyName); + ReportProgress(new ProgressEventArgs(1.0, "Finished")); + } + finally + { + GameVerifier.Progress += OnVerifyProgress; + } + }, CancellationToken.None).ConfigureAwait(false); + } private void OnVerifyProgress(object _, ProgressEventArgs e) diff --git a/src/ModVerify/Pipeline/GameVerifyPipeline.cs b/src/ModVerify/Pipeline/GameVerifyPipeline.cs index ab62511..a450813 100644 --- a/src/ModVerify/Pipeline/GameVerifyPipeline.cs +++ b/src/ModVerify/Pipeline/GameVerifyPipeline.cs @@ -2,7 +2,6 @@ using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Settings; -using AET.ModVerify.Utilities; using AET.ModVerify.Verifiers; using AnakinRaW.CommonUtilities.SimplePipeline; using AnakinRaW.CommonUtilities.SimplePipeline.Runners; @@ -10,20 +9,19 @@ using PG.StarWarsGame.Engine; using System; using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; namespace AET.ModVerify.Pipeline; -public sealed class NewGameVerifyPipeline : AnakinRaW.CommonUtilities.SimplePipeline.Pipeline +public sealed class NewGameVerifyPipeline : StepRunnerPipelineBase { private readonly List _verifiers = new(); private readonly List _verificationSteps = new(); private readonly ConcurrentGameEngineErrorReporter _engineErrorReporter = new(); - private readonly StepRunnerBase _verifyRunner; + private readonly AsyncStepRunner _verifyRunner; private readonly VerificationTarget _verificationTarget; private readonly VerifyPipelineSettings _pipelineSettings; private readonly GlobalVerifyReportSettings _reportSettings; @@ -32,9 +30,24 @@ public sealed class NewGameVerifyPipeline : AnakinRaW.CommonUtilities.SimplePipe private readonly IPetroglyphStarWarsGameEngineService _gameEngineService; private readonly ILogger? _logger; - protected override bool FailFast { get; } + protected override Task PrepareCoreAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + protected override AsyncStepRunner CreateRunner() + { + throw new NotImplementedException(); + } - public IReadOnlyCollection FilteredErrors { get; private set; } = []; + //protected override IStepRunner CreateRunner() + //{ + // throw new NotImplementedException(); + //} + + //protected override bool FailFast { get; } + + //public IReadOnlyCollection FilteredErrors { get; private set; } = []; public NewGameVerifyPipeline( VerificationTarget verificationTarget, @@ -52,114 +65,119 @@ public NewGameVerifyPipeline( _gameEngineService = serviceProvider.GetRequiredService(); _logger = serviceProvider.GetService()?.CreateLogger(GetType()); - _verifyRunner = pipelineSettings.ParallelVerifiers switch - { - < 0 or > 64 => throw new ArgumentException("_pipelineSettings has invalid parallel worker number.", - nameof(pipelineSettings)), - 1 => new SequentialStepRunner(serviceProvider), - _ => new ParallelStepRunner(pipelineSettings.ParallelVerifiers, serviceProvider) - }; - - FailFast = pipelineSettings.FailFast; - } - - protected override async Task PrepareCoreAsync() - { - _verifiers.Clear(); - - - IStarWarsGameEngine gameEngine; - - try - { - gameEngine = await _gameEngineService.InitializeAsync( - _verificationTarget.Engine, - _verificationTarget.Location, - _engineErrorReporter, - _engineInitializationReporter, - false, - CancellationToken.None).ConfigureAwait(false); - } - catch (Exception e) - { - _logger?.LogError(e, "Creating game engine failed: {Message}", e.Message); - throw; - } - - - AddStep(new GameEngineErrorCollector(_engineErrorReporter, gameEngine, _pipelineSettings.GameVerifySettings, ServiceProvider)); - - foreach (var gameVerificationStep in CreateVerificationSteps(gameEngine)) - AddStep(gameVerificationStep); - - return true; - } - - protected override async Task RunCoreAsync(CancellationToken token) - { - var aggregatedVerifyProgressReporter = new AggregatedVerifyProgressReporter(_progressReporter, _verificationSteps); - - try - { - Logger?.LogInformation("Running game verifiers..."); - _progressReporter.Report(0.0, $"Verifing {_verificationTarget.Name}...", VerifyProgress.ProgressType, default); - _verifyRunner.Error += OnError; - await _verifyRunner.RunAsync(token); - } - finally - { - aggregatedVerifyProgressReporter.Dispose(); - _verifyRunner.Error -= OnError; - Logger?.LogDebug("Game verifiers finished."); - } - - token.ThrowIfCancellationRequested(); - - var failedSteps = _verifyRunner.ExecutedSteps.Where(p => - p.Error != null && !p.Error.IsExceptionType()).ToList(); + //_verifyRunner = pipelineSettings.ParallelVerifiers switch + //{ + // < 0 or > 64 => throw new ArgumentException("_pipelineSettings has invalid parallel worker number.", + // nameof(pipelineSettings)), + // 1 => new SequentialStepRunner(serviceProvider), + // _ => new AsyncStepRunner(pipelineSettings.ParallelVerifiers, serviceProvider) + //}; - if (failedSteps.Count != 0) - throw new StepFailureException(failedSteps); - - FilteredErrors = GetReportableErrors(_verifiers.SelectMany(s => s.VerifyErrors)).ToList(); - - - _progressReporter.Report(1.0, $"Finished Verifing {_verificationTarget.Name}", VerifyProgress.ProgressType, default); + //FailFast = pipelineSettings.FailFast; } - 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; - } - base.OnError(sender, e); - } - - private void AddStep(GameVerifier verifier) - { - var verificationStep = new GameVerifierPipelineStep(verifier, ServiceProvider); - _verifyRunner.AddStep(verificationStep); - _verificationSteps.Add(verificationStep); - _verifiers.Add(verifier); - } - - private IEnumerable GetReportableErrors(IEnumerable errors) - { - Logger?.LogDebug("Applying baseline and suppressions."); - // NB: We don't filter for severity here, as the individual reporters handle that. - // This allows better control over what gets reported. - return errors.ApplyBaseline(_reportSettings.Baseline) - .ApplySuppressions(_reportSettings.Suppressions); - } - - private IEnumerable CreateVerificationSteps(IStarWarsGameEngine engine) - { - return _pipelineSettings.VerifiersProvider - .GetVerifiers(engine, _pipelineSettings.GameVerifySettings, ServiceProvider); - } + //protected override async Task PrepareCoreAsync() + //{ + // _verifiers.Clear(); + + + // IStarWarsGameEngine gameEngine; + + // try + // { + // gameEngine = await _gameEngineService.InitializeAsync( + // _verificationTarget.Engine, + // _verificationTarget.Location, + // _engineErrorReporter, + // _engineInitializationReporter, + // false, + // CancellationToken.None).ConfigureAwait(false); + // } + // catch (Exception e) + // { + // _logger?.LogError(e, "Creating game engine failed: {Message}", e.Message); + // throw; + // } + + + // AddStep(new GameEngineErrorCollector(_engineErrorReporter, gameEngine, _pipelineSettings.GameVerifySettings, ServiceProvider)); + + // foreach (var gameVerificationStep in CreateVerificationSteps(gameEngine)) + // AddStep(gameVerificationStep); + + // return true; + //} + + //protected override Task> CreateRunnerSteps(CancellationToken token) + //{ + // throw new NotImplementedException(); + //} + + //protected override async Task RunCoreAsync(CancellationToken token) + //{ + // var aggregatedVerifyProgressReporter = new AggregatedVerifyProgressReporter(_progressReporter, _verificationSteps); + + // try + // { + // Logger?.LogInformation("Running game verifiers..."); + // _progressReporter.Report(0.0, $"Verifing {_verificationTarget.Name}...", VerifyProgress.ProgressType, default); + // _verifyRunner.Error += OnError; + // await _verifyRunner.RunAsync(token); + // } + // finally + // { + // aggregatedVerifyProgressReporter.Dispose(); + // _verifyRunner.Error -= OnError; + // Logger?.LogDebug("Game verifiers finished."); + // } + + // token.ThrowIfCancellationRequested(); + + // var failedSteps = _verifyRunner.ExecutedSteps.Where(p => + // p.Error != null && !p.Error.IsExceptionType()).ToList(); + + // if (failedSteps.Count != 0) + // throw new StepFailureException(failedSteps); + + // FilteredErrors = GetReportableErrors(_verifiers.SelectMany(s => s.VerifyErrors)).ToList(); + + + // _progressReporter.Report(1.0, $"Finished Verifing {_verificationTarget.Name}", VerifyProgress.ProgressType, default); + //} + + //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; + // } + // base.OnError(sender, e); + //} + + //private void AddStep(GameVerifier verifier) + //{ + // var verificationStep = new GameVerifierPipelineStep(verifier, ServiceProvider); + // _verifyRunner.AddStep(verificationStep); + // _verificationSteps.Add(verificationStep); + // _verifiers.Add(verifier); + //} + + //private IEnumerable GetReportableErrors(IEnumerable errors) + //{ + // Logger?.LogDebug("Applying baseline and suppressions."); + // // NB: We don't filter for severity here, as the individual reporters handle that. + // // This allows better control over what gets reported. + // return errors.ApplyBaseline(_reportSettings.Baseline) + // .ApplySuppressions(_reportSettings.Suppressions); + //} + + //private IEnumerable CreateVerificationSteps(IStarWarsGameEngine engine) + //{ + // return _pipelineSettings.VerifiersProvider + // .GetVerifiers(engine, _pipelineSettings.GameVerifySettings, ServiceProvider); + //} } diff --git a/src/ModVerify/Verifiers/DuplicateNameFinder.cs b/src/ModVerify/Verifiers/DuplicateNameFinder.cs index 23ab1b8..0b3b585 100644 --- a/src/ModVerify/Verifiers/DuplicateNameFinder.cs +++ b/src/ModVerify/Verifiers/DuplicateNameFinder.cs @@ -40,10 +40,10 @@ private void CheckForDuplicateCrcEntries( string sourceName, TSource source, Func> crcSelector, - Func> entrySelector, + Func> entrySelector, Func entryToStringSelector, - Func, IEnumerable> contextSelector, - Func, string, string> errorMessageCreator) + Func, IEnumerable> contextSelector, + Func, string, string> errorMessageCreator) { foreach (var crc32 in crcSelector(source)) { @@ -87,13 +87,13 @@ private void CheckXmlObjectsForDuplicates(string databaseName, IGameManager entries, string fileName) + private static string CreateDuplicateMtdErrorMessage(ImmutableFrugalList entries, string fileName) { var firstEntry = entries.First(); return $"MTD File '{fileName}' has duplicate definitions for CRC ({firstEntry}): {string.Join(",", entries.Select(x => x.FileName))}"; } - private static string CreateDuplicateXmlErrorMessage(ReadOnlyFrugalList entries, string databaseName) where T : NamedXmlObject + private static string CreateDuplicateXmlErrorMessage(ImmutableFrugalList entries, string databaseName) where T : NamedXmlObject { var firstEntry = entries.First(); var message = $"{databaseName} '{firstEntry.Name}' ({firstEntry.Crc32}) has duplicate definitions: "; From 68453c2823481abf78d724ccdc00ce7caa08a3d2 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 16 Jan 2026 17:28:03 +0100 Subject: [PATCH 13/22] make everything compile at least --- modules/ModdingToolBase | 2 +- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 39 ++++++++++++------- src/ModVerify/Pipeline/GameVerifyPipeline.cs | 2 +- test/ModVerify.CliApp.Test/CommonTestBase.cs | 3 +- .../ModVerify.CliApp.Test.csproj | 10 ++--- 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index a6a05f7..9155aeb 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit a6a05f7c3ecc91d796afff91b9686bcded0f8030 +Subproject commit 9155aebd41f0dc24ed2e900139a9e6b75d631228 diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 9874e8c..2301a28 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -21,6 +21,10 @@ en + + true + + @@ -30,18 +34,18 @@ - - + + - + - - - - - + + + + + @@ -51,10 +55,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - all @@ -64,9 +64,18 @@ - - true - + + + + compile + runtime; build; native; contentfiles; analyzers; buildtransitive + + + compile + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/src/ModVerify/Pipeline/GameVerifyPipeline.cs b/src/ModVerify/Pipeline/GameVerifyPipeline.cs index a450813..a2ee0b5 100644 --- a/src/ModVerify/Pipeline/GameVerifyPipeline.cs +++ b/src/ModVerify/Pipeline/GameVerifyPipeline.cs @@ -47,7 +47,7 @@ protected override AsyncStepRunner CreateRunner() //protected override bool FailFast { get; } - //public IReadOnlyCollection FilteredErrors { get; private set; } = []; + public IReadOnlyCollection FilteredErrors { get; private set; } = []; public NewGameVerifyPipeline( VerificationTarget verificationTarget, diff --git a/test/ModVerify.CliApp.Test/CommonTestBase.cs b/test/ModVerify.CliApp.Test/CommonTestBase.cs index a93b970..8a93507 100644 --- a/test/ModVerify.CliApp.Test/CommonTestBase.cs +++ b/test/ModVerify.CliApp.Test/CommonTestBase.cs @@ -1,5 +1,6 @@ using AET.SteamAbstraction; using AnakinRaW.CommonUtilities.Hashing; +using AnakinRaW.CommonUtilities.Testing; using Microsoft.Extensions.DependencyInjection; using PG.Commons; using PG.StarWarsGame.Infrastructure; @@ -7,7 +8,7 @@ namespace ModVerify.CliApp.Test; -public abstract class CommonTestBase : AET.Testing.TestBaseWithFileSystem +public abstract class CommonTestBase : TestBaseWithFileSystem { protected override void SetupServices(IServiceCollection serviceCollection) { diff --git a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj index 2a54398..09e62a4 100644 --- a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj +++ b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj @@ -13,13 +13,13 @@ - - - + + + - + - + all From 371b5eb8e6e40424f72b376cb0cd016809502607 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 16 Jan 2026 17:28:11 +0100 Subject: [PATCH 14/22] to weekly deps check --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b43ddf6..8c2061c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,7 +8,7 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" + interval: "weekly" groups: actions-deps: patterns: @@ -19,7 +19,7 @@ updates: - package-ecosystem: "nuget" directory: "/" schedule: - interval: "daily" + interval: "weekly" target-branch: "develop" open-pull-requests-limit: 1 groups: From 5ae5aedbc9685a448dcc527aff6b23cc2de79673 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 16 Jan 2026 17:28:29 +0100 Subject: [PATCH 15/22] update license year --- Directory.Build.props | 4 ++-- LICENSE | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index d475e7f..5110cdb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,7 +12,7 @@ ModVerify Alamo Engine Tools and Contributors - Copyright © 2025 Alamo Engine Tools and contributors. All rights reserved. + Copyright © 2026 Alamo Engine Tools and contributors. All rights reserved. https://github.com/AlamoEngine-Tools/ModVerify $(RepoRootPath)LICENSE MIT @@ -33,7 +33,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/LICENSE b/LICENSE index 7159126..23fe869 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Alamo Engine Tools +Copyright (c) 2026 Alamo Engine Tools Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 30641ed85df1d10c5676d8e4f7db01076c1b089c Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 16 Jan 2026 17:49:42 +0100 Subject: [PATCH 16/22] rename class --- src/ModVerify.CliApp/ModVerifyApplication.cs | 2 +- src/ModVerify/Pipeline/GameVerifyPipeline.cs | 294 +++++-------------- 2 files changed, 75 insertions(+), 221 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index fb3ebab..4d45979 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -129,7 +129,7 @@ private async Task> Verify( { var progressReporter = new VerifyConsoleProgressReporter(verificationTarget.Name); - using var verifyPipeline = new NewGameVerifyPipeline( + using var verifyPipeline = new GameVerifyPipeline( verificationTarget, settings.VerifyPipelineSettings, reportSettings, diff --git a/src/ModVerify/Pipeline/GameVerifyPipeline.cs b/src/ModVerify/Pipeline/GameVerifyPipeline.cs index a2ee0b5..1260681 100644 --- a/src/ModVerify/Pipeline/GameVerifyPipeline.cs +++ b/src/ModVerify/Pipeline/GameVerifyPipeline.cs @@ -9,19 +9,20 @@ using PG.StarWarsGame.Engine; using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using AET.ModVerify.Utilities; using Microsoft.Extensions.DependencyInjection; namespace AET.ModVerify.Pipeline; -public sealed class NewGameVerifyPipeline : StepRunnerPipelineBase +public sealed class GameVerifyPipeline : StepRunnerPipelineBase { private readonly List _verifiers = new(); private readonly List _verificationSteps = new(); private readonly ConcurrentGameEngineErrorReporter _engineErrorReporter = new(); - private readonly AsyncStepRunner _verifyRunner; private readonly VerificationTarget _verificationTarget; private readonly VerifyPipelineSettings _pipelineSettings; private readonly GlobalVerifyReportSettings _reportSettings; @@ -30,26 +31,9 @@ public sealed class NewGameVerifyPipeline : StepRunnerPipelineBase FilteredErrors { get; private set; } = []; - public NewGameVerifyPipeline( + public GameVerifyPipeline( VerificationTarget verificationTarget, VerifyPipelineSettings pipelineSettings, GlobalVerifyReportSettings reportSettings, @@ -65,53 +49,48 @@ public NewGameVerifyPipeline( _gameEngineService = serviceProvider.GetRequiredService(); _logger = serviceProvider.GetService()?.CreateLogger(GetType()); - //_verifyRunner = pipelineSettings.ParallelVerifiers switch - //{ - // < 0 or > 64 => throw new ArgumentException("_pipelineSettings has invalid parallel worker number.", - // nameof(pipelineSettings)), - // 1 => new SequentialStepRunner(serviceProvider), - // _ => new AsyncStepRunner(pipelineSettings.ParallelVerifiers, serviceProvider) - //}; - - //FailFast = pipelineSettings.FailFast; + FailFast = pipelineSettings.FailFast; } - //protected override async Task PrepareCoreAsync() - //{ - // _verifiers.Clear(); - - - // IStarWarsGameEngine gameEngine; - - // try - // { - // gameEngine = await _gameEngineService.InitializeAsync( - // _verificationTarget.Engine, - // _verificationTarget.Location, - // _engineErrorReporter, - // _engineInitializationReporter, - // false, - // CancellationToken.None).ConfigureAwait(false); - // } - // catch (Exception e) - // { - // _logger?.LogError(e, "Creating game engine failed: {Message}", e.Message); - // throw; - // } - - - // AddStep(new GameEngineErrorCollector(_engineErrorReporter, gameEngine, _pipelineSettings.GameVerifySettings, ServiceProvider)); - - // foreach (var gameVerificationStep in CreateVerificationSteps(gameEngine)) - // AddStep(gameVerificationStep); - - // return true; - //} + protected override AsyncStepRunner CreateRunner() + { + var requestedRunnerCount = _pipelineSettings.ParallelVerifiers; + return requestedRunnerCount switch + { + < 0 or > 64 => throw new InvalidOperationException( + $"Invalid parallel worker count ({requestedRunnerCount}) specified in verifier settings."), + 1 => new SequentialStepRunner(ServiceProvider), + _ => new AsyncStepRunner(requestedRunnerCount, ServiceProvider) + }; + } - //protected override Task> CreateRunnerSteps(CancellationToken token) - //{ - // throw new NotImplementedException(); - //} + protected override async Task PrepareCoreAsync(CancellationToken token) + { + _verifiers.Clear(); + + IStarWarsGameEngine gameEngine; + + try + { + gameEngine = await _gameEngineService.InitializeAsync( + _verificationTarget.Engine, + _verificationTarget.Location, + _engineErrorReporter, + _engineInitializationReporter, + false, + CancellationToken.None).ConfigureAwait(false); + } + catch (Exception e) + { + _logger?.LogError(e, "Creating game engine failed: {Message}", e.Message); + throw; + } + + AddStep(new GameEngineErrorCollector(_engineErrorReporter, gameEngine, _pipelineSettings.GameVerifySettings, ServiceProvider)); + + foreach (var gameVerificationStep in CreateVerificationSteps(gameEngine)) + AddStep(gameVerificationStep); + } //protected override async Task RunCoreAsync(CancellationToken token) //{ @@ -145,162 +124,37 @@ public NewGameVerifyPipeline( // _progressReporter.Report(1.0, $"Finished Verifing {_verificationTarget.Name}", VerifyProgress.ProgressType, default); //} - //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; - // } - // base.OnError(sender, e); - //} - - //private void AddStep(GameVerifier verifier) - //{ - // var verificationStep = new GameVerifierPipelineStep(verifier, ServiceProvider); - // _verifyRunner.AddStep(verificationStep); - // _verificationSteps.Add(verificationStep); - // _verifiers.Add(verifier); - //} - - //private IEnumerable GetReportableErrors(IEnumerable errors) - //{ - // Logger?.LogDebug("Applying baseline and suppressions."); - // // NB: We don't filter for severity here, as the individual reporters handle that. - // // This allows better control over what gets reported. - // return errors.ApplyBaseline(_reportSettings.Baseline) - // .ApplySuppressions(_reportSettings.Suppressions); - //} - - //private IEnumerable CreateVerificationSteps(IStarWarsGameEngine engine) - //{ - // return _pipelineSettings.VerifiersProvider - // .GetVerifiers(engine, _pipelineSettings.GameVerifySettings, ServiceProvider); - //} -} - - - - - - - - - -//public sealed class GameVerifyPipeline : AnakinRaW.CommonUtilities.SimplePipeline.Pipeline -//{ -// private readonly List _verifiers = new(); -// private readonly List _verificationSteps = new(); -// private readonly StepRunnerBase _verifyRunner; - -// private readonly IStarWarsGameEngine _gameEngine; -// private readonly IGameEngineErrorCollection _engineErrors; - -// private readonly VerifyPipelineSettings _pipelineSettings; -// private readonly GlobalVerifyReportSettings _reportSettings; - -// private readonly IVerifyProgressReporter _progressReporter; - -// protected override bool FailFast { get; } - -// public IReadOnlyCollection FilteredErrors { get; private set; } = []; - -// public GameVerifyPipeline( -// IStarWarsGameEngine gameEngine, -// IGameEngineErrorCollection engineErrors, -// VerifyPipelineSettings pipelineSettings, -// GlobalVerifyReportSettings reportSettings, -// IVerifyProgressReporter progressReporter, -// IServiceProvider serviceProvider) : base(serviceProvider) -// { -// _gameEngine = gameEngine ?? throw new ArgumentNullException(nameof(gameEngine)); -// _engineErrors = engineErrors ?? throw new ArgumentNullException(nameof(gameEngine)); -// _pipelineSettings = pipelineSettings ?? throw new ArgumentNullException(nameof(pipelineSettings)); -// _reportSettings = reportSettings ?? throw new ArgumentNullException(nameof(reportSettings)); -// _progressReporter = progressReporter ?? throw new ArgumentNullException(nameof(progressReporter)); - -// if (pipelineSettings.ParallelVerifiers is < 0 or > 64) -// throw new ArgumentException("_pipelineSettings has invalid parallel worker number.", nameof(pipelineSettings)); - -// if (pipelineSettings.ParallelVerifiers == 1) -// _verifyRunner = new SequentialStepRunner(serviceProvider); -// else -// _verifyRunner = new ParallelStepRunner(pipelineSettings.ParallelVerifiers, serviceProvider); - -// FailFast = pipelineSettings.FailFast; -// } - -// protected override Task PrepareCoreAsync() -// { -// _verifiers.Clear(); - -// AddStep(new GameEngineErrorCollector(_engineErrors, _gameEngine, _pipelineSettings.GameVerifySettings, ServiceProvider)); - -// foreach (var gameVerificationStep in CreateVerificationSteps(_gameEngine)) -// AddStep(gameVerificationStep); - -// return Task.FromResult(true); -// } - -// protected override async Task RunCoreAsync(CancellationToken token) -// { -// var aggregatedVerifyProgressReporter = new AggregatedVerifyProgressReporter(_progressReporter, _verificationSteps); - -// try -// { -// Logger?.LogInformation("Running game verifiers..."); -// _verifyRunner.Error += OnError; -// await _verifyRunner.RunAsync(token); -// } -// finally -// { -// aggregatedVerifyProgressReporter.Dispose(); -// _verifyRunner.Error -= OnError; -// Logger?.LogDebug("Game verifiers finished."); -// } - -// token.ThrowIfCancellationRequested(); - -// var failedSteps = _verifyRunner.ExecutedSteps.Where(p => -// p.Error != null && !p.Error.IsExceptionType()).ToList(); - -// if (failedSteps.Count != 0) -// throw new StepFailureException(failedSteps); - -// FilteredErrors = GetReportableErrors(_verifiers.SelectMany(s => s.VerifyErrors)).ToList(); -// } - -// 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; -// } -// base.OnError(sender, e); -// } + protected override void OnRunnerExecutionError(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; + } + base.OnRunnerExecutionError(sender, e); + } -// private IEnumerable CreateVerificationSteps(IStarWarsGameEngine database) -// { -// return _pipelineSettings.VerifiersProvider.GetVerifiers(database, _pipelineSettings.GameVerifySettings, ServiceProvider); -// } + private void AddStep(GameVerifier verifier) + { + var verificationStep = new GameVerifierPipelineStep(verifier, ServiceProvider); + StepRunner.AddStep(verificationStep); + _verificationSteps.Add(verificationStep); + _verifiers.Add(verifier); + } -// private void AddStep(GameVerifier verifier) -// { -// var verificationStep = new GameVerifierPipelineStep(verifier, ServiceProvider); -// _verifyRunner.AddStep(verificationStep); -// _verificationSteps.Add(verificationStep); -// _verifiers.Add(verifier); -// } + private IEnumerable GetReportableErrors(IEnumerable errors) + { + Logger?.LogDebug("Applying baseline and suppressions."); + // NB: We don't filter for severity here, as the individual reporters handle that. + // This allows better control over what gets reported. + return errors.ApplyBaseline(_reportSettings.Baseline) + .ApplySuppressions(_reportSettings.Suppressions); + } -// private IEnumerable GetReportableErrors(IEnumerable errors) -// { -// Logger?.LogDebug("Applying baseline and suppressions."); -// // NB: We don't filter for severity here, as the individual reporters handle that. -// // This allows better control over what gets reported. -// return errors.ApplyBaseline(_reportSettings.Baseline) -// .ApplySuppressions(_reportSettings.Suppressions); -// } -//} \ No newline at end of file + private IEnumerable CreateVerificationSteps(IStarWarsGameEngine engine) + { + return _pipelineSettings.VerifiersProvider + .GetVerifiers(engine, _pipelineSettings.GameVerifySettings, ServiceProvider); + } +} \ No newline at end of file From 47ae7024014d6f72e14faf77044f160372e06f9a Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 16 Jan 2026 19:29:25 +0100 Subject: [PATCH 17/22] make compile and run again --- modules/ModdingToolBase | 2 +- src/ModVerify/ModVerify.csproj | 4 +- src/ModVerify/Pipeline/GameVerifyPipeline.cs | 51 ++++++++----------- .../CommandBar/CommandBarGameManager.cs | 4 +- .../PG.StarWarsGame.Engine.csproj | 2 +- .../ModVerify.CliApp.Test.csproj | 2 +- 6 files changed, 27 insertions(+), 38 deletions(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 9155aeb..9ab5f4b 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 9155aebd41f0dc24ed2e900139a9e6b75d631228 +Subproject commit 9ab5f4b0be7541123dfe91c67aabd12711a25cfa diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index 905b020..634dbf7 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -33,8 +33,8 @@ - - + + diff --git a/src/ModVerify/Pipeline/GameVerifyPipeline.cs b/src/ModVerify/Pipeline/GameVerifyPipeline.cs index 1260681..bc748e3 100644 --- a/src/ModVerify/Pipeline/GameVerifyPipeline.cs +++ b/src/ModVerify/Pipeline/GameVerifyPipeline.cs @@ -30,6 +30,7 @@ public sealed class GameVerifyPipeline : StepRunnerPipelineBase private readonly IGameEngineInitializationReporter? _engineInitializationReporter; private readonly IPetroglyphStarWarsGameEngineService _gameEngineService; private readonly ILogger? _logger; + private AggregatedVerifyProgressReporter? _aggregatedVerifyProgressReporter; public IReadOnlyCollection FilteredErrors { get; private set; } = []; @@ -92,37 +93,19 @@ protected override async Task PrepareCoreAsync(CancellationToken token) AddStep(gameVerificationStep); } - //protected override async Task RunCoreAsync(CancellationToken token) - //{ - // var aggregatedVerifyProgressReporter = new AggregatedVerifyProgressReporter(_progressReporter, _verificationSteps); - - // try - // { - // Logger?.LogInformation("Running game verifiers..."); - // _progressReporter.Report(0.0, $"Verifing {_verificationTarget.Name}...", VerifyProgress.ProgressType, default); - // _verifyRunner.Error += OnError; - // await _verifyRunner.RunAsync(token); - // } - // finally - // { - // aggregatedVerifyProgressReporter.Dispose(); - // _verifyRunner.Error -= OnError; - // Logger?.LogDebug("Game verifiers finished."); - // } - - // token.ThrowIfCancellationRequested(); - - // var failedSteps = _verifyRunner.ExecutedSteps.Where(p => - // p.Error != null && !p.Error.IsExceptionType()).ToList(); - - // if (failedSteps.Count != 0) - // throw new StepFailureException(failedSteps); - - // FilteredErrors = GetReportableErrors(_verifiers.SelectMany(s => s.VerifyErrors)).ToList(); - - - // _progressReporter.Report(1.0, $"Finished Verifing {_verificationTarget.Name}", VerifyProgress.ProgressType, default); - //} + protected override void OnExecuteStarted() + { + Logger?.LogInformation("Running game verifiers..."); + _aggregatedVerifyProgressReporter = new AggregatedVerifyProgressReporter(_progressReporter, _verificationSteps); + _progressReporter.Report(0.0, $"Verifying {_verificationTarget.Name}...", VerifyProgress.ProgressType, default); + } + + protected override void OnExecuteCompleted() + { + Logger?.LogInformation("Game verifiers finished."); + FilteredErrors = GetReportableErrors(_verifiers.SelectMany(s => s.VerifyErrors)).ToList(); + _progressReporter.Report(1.0, $"Finished Verifying {_verificationTarget.Name}", VerifyProgress.ProgressType, default); + } protected override void OnRunnerExecutionError(object sender, StepRunnerErrorEventArgs e) { @@ -157,4 +140,10 @@ private IEnumerable CreateVerificationSteps(IStarWarsGameEngine en return _pipelineSettings.VerifiersProvider .GetVerifiers(engine, _pipelineSettings.GameVerifySettings, ServiceProvider); } + + protected override void DisposeResources() + { + base.DisposeResources(); + _aggregatedVerifyProgressReporter?.Dispose(); + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index a007472..0b8687e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -213,8 +213,8 @@ private void SetDefaultFont() if (_defaultFont is null) { // TODO: From GameConstants - string fontName = PGConstants.DefaultUnicodeFontName; - int size = 11; + var fontName = PGConstants.DefaultUnicodeFontName; + var size = 11; var font = fontManager.CreateFont(fontName, size, true, false, false, 1.0f); if (font is null) ErrorReporter.Assert(EngineAssert.FromNullOrEmpty([ToString()], $"Unable to create Default from name {fontName}")); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index 19b4c1a..43301f5 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -25,7 +25,7 @@ - + diff --git a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj index 09e62a4..112201d 100644 --- a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj +++ b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj @@ -15,7 +15,7 @@ - + From b4ffd064425a5bca1387f0193ce5d22325900b4c Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 16 Jan 2026 21:20:07 +0100 Subject: [PATCH 18/22] local deploy script --- deploy-local.ps1 | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 deploy-local.ps1 diff --git a/deploy-local.ps1 b/deploy-local.ps1 new file mode 100644 index 0000000..740c579 --- /dev/null +++ b/deploy-local.ps1 @@ -0,0 +1,75 @@ +# Local deployment script for ModVerify to test the update feature. +# This script builds the application, creates an update manifest, and "deploys" it to a local directory. + +$ErrorActionPreference = "Stop" + +$root = $PSScriptRoot +if ([string]::IsNullOrEmpty($root)) { $root = Get-Location } + +$deployRoot = Join-Path $root ".local_deploy" +$stagingDir = Join-Path $deployRoot "staging" +$serverDir = Join-Path $deployRoot "server" +$installDir = Join-Path $deployRoot "install" + +$toolProj = Join-Path $root "src\ModVerify.CliApp\ModVerify.CliApp.csproj" +$creatorProj = Join-Path $root "modules\ModdingToolBase\src\AnakinApps\ApplicationManifestCreator\ApplicationManifestCreator.csproj" +$uploaderProj = Join-Path $root "modules\ModdingToolBase\src\AnakinApps\FtpUploader\FtpUploader.csproj" + +$toolExe = "ModVerify.exe" +$updaterExe = "AnakinRaW.ExternalUpdater.exe" +$manifestCreatorDll = "AnakinRaW.ApplicationManifestCreator.dll" +$uploaderDll = "AnakinRaW.FtpUploader.dll" + +# 1. Clean and Create directories +if (Test-Path $deployRoot) { Remove-Item -Recurse -Force $deployRoot } +New-Item -ItemType Directory -Path $stagingDir | Out-Null +New-Item -ItemType Directory -Path $serverDir | Out-Null +New-Item -ItemType Directory -Path $installDir | Out-Null + +Write-Host "--- Building ModVerify (net481) ---" -ForegroundColor Cyan +dotnet build $toolProj --configuration Release -f net481 --output "$deployRoot\bin\tool" /p:DebugType=None /p:DebugSymbols=false + +Write-Host "--- Building Manifest Creator ---" -ForegroundColor Cyan +dotnet build $creatorProj --configuration Release --output "$deployRoot\bin\creator" + +Write-Host "--- Building Local Uploader ---" -ForegroundColor Cyan +dotnet build $uploaderProj --configuration Release --output "$deployRoot\bin\uploader" + +# 2. Prepare staging +Write-Host "--- Preparing Staging ---" -ForegroundColor Cyan +Copy-Item "$deployRoot\bin\tool\$toolExe" $stagingDir +Copy-Item "$deployRoot\bin\tool\$updaterExe" $stagingDir + +# 3. Create Manifest +# Origin must be an absolute URI for the manifest creator. +# Using 127.0.0.1 and file:// is tricky with Flurl/DownloadManager sometimes. +# We'll use the local path and ensure it's formatted correctly. +$serverPath = (Resolve-Path $serverDir).Path +$serverUri = "file:///$($serverPath.Replace('\', '/'))" +# If we have 3 slashes, Flurl/DownloadManager might still fail on Windows if it expects a certain format. +# However, the ManifestCreator just needs a valid URI for the 'Origin' field in the manifest. +Write-Host "--- Creating Manifest (Origin: $serverUri) ---" -ForegroundColor Cyan +dotnet "$deployRoot\bin\creator\$manifestCreatorDll" ` + -a "$stagingDir\$toolExe" ` + --appDataFiles "$stagingDir\$updaterExe" ` + --origin "$serverUri" ` + -o "$stagingDir" ` + -b "beta" + +# 4. "Deploy" to server using the local uploader +Write-Host "--- Deploying to Local Server ---" -ForegroundColor Cyan +dotnet "$deployRoot\bin\uploader\$uploaderDll" local --base "$serverDir" --source "$stagingDir" + +# 5. Setup a "test" installation +Write-Host "--- Setting up Test Installation ---" -ForegroundColor Cyan +Copy-Item "$deployRoot\bin\tool\*" $installDir -Recurse + +Write-Host "`nLocal deployment complete!" -ForegroundColor Green +Write-Host "Server directory: $serverDir" +Write-Host "Install directory: $installDir" +Write-Host "`nTo test the update:" +Write-Host "1. (Optional) Modify the version in version.json and run this script again to 'push' a new version to the local server." +Write-Host "2. Run ModVerify from the install directory with the following command:" +Write-Host " cd '$installDir'" +Write-Host " .\ModVerify.exe updateApplication --updateManifestUrl '$serverUri'" +Write-Host "`n Note: You can also specify a different branch using --updateBranch if needed." From c36a40636b707e597238650450712ed11097d9c2 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 16 Jan 2026 21:20:18 +0100 Subject: [PATCH 19/22] update gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 8a30d25..7d20657 100644 --- a/.gitignore +++ b/.gitignore @@ -396,3 +396,6 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml +.idea + +.local_deploy \ No newline at end of file From 9721fa04eeee561a3489b8b9618dbef03a7954bd Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 16 Jan 2026 21:20:32 +0100 Subject: [PATCH 20/22] polishing --- .../ModVerifyAppEnvironment.cs | 11 ++++-- .../Settings/SettingsBuilder.cs | 1 - .../Pipeline/GameVerifierPipelineStep.cs | 35 +++++++++---------- src/ModVerify/Pipeline/GameVerifyPipeline.cs | 4 +-- .../Reporting/Json/JsonVerificationTarget.cs | 2 +- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs b/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs index 86bfc40..90a41db 100644 --- a/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs +++ b/src/ModVerify.CliApp/ModVerifyAppEnvironment.cs @@ -1,4 +1,5 @@ -using System.IO.Abstractions; +using System.IO; +using System.IO.Abstractions; using System.Reflection; using AnakinRaW.ApplicationBase.Environment; #if !NET @@ -26,11 +27,17 @@ internal sealed class ModVerifyAppEnvironment(Assembly assembly, IFileSystem fil public override ICollection UpdateMirrors { get; } = new List { #if DEBUG - new("C:\\Test\\ModVerify"), + new(CreateDebugPath()), #endif new($"https://republicatwar.com/downloads/{ModVerifyConstants.ModVerifyToolPath}") }; + private static string CreateDebugPath() + { + var dir = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "../../../../..")); + return Path.Combine(dir, ".local_deploy/server"); + } + public override string UpdateRegistryPath => $@"SOFTWARE\{ModVerifyConstants.ModVerifyToolPath}\Update"; protected override UpdateConfiguration CreateUpdateConfiguration() diff --git a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs index 984099b..02b2a01 100644 --- a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; 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.Settings; diff --git a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs index f201d6b..e83cb25 100644 --- a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs +++ b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs @@ -23,27 +23,24 @@ public sealed class GameVerifierPipelineStep( public long Size => 1; - protected override async Task RunCoreAsync(CancellationToken token) + protected override Task RunCoreAsync(CancellationToken token) { - await Task.Run(() => + try { - try - { - Logger?.LogDebug("Running verifier '{Name}'...", GameVerifier.FriendlyName); - ReportProgress(new ProgressEventArgs(0.0, "Started")); - - GameVerifier.Progress += OnVerifyProgress; - GameVerifier.Verify(token); - - Logger?.LogDebug("Finished verifier '{Name}'", GameVerifier.FriendlyName); - ReportProgress(new ProgressEventArgs(1.0, "Finished")); - } - finally - { - GameVerifier.Progress += OnVerifyProgress; - } - }, CancellationToken.None).ConfigureAwait(false); - + Logger?.LogDebug("Running verifier '{Name}'...", GameVerifier.FriendlyName); + ReportProgress(new ProgressEventArgs(0.0, "Started")); + + GameVerifier.Progress += OnVerifyProgress; + GameVerifier.Verify(token); + + Logger?.LogDebug("Finished verifier '{Name}'", GameVerifier.FriendlyName); + ReportProgress(new ProgressEventArgs(1.0, "Finished")); + return Task.CompletedTask; + } + finally + { + GameVerifier.Progress += OnVerifyProgress; + } } private void OnVerifyProgress(object _, ProgressEventArgs e) diff --git a/src/ModVerify/Pipeline/GameVerifyPipeline.cs b/src/ModVerify/Pipeline/GameVerifyPipeline.cs index bc748e3..8e521eb 100644 --- a/src/ModVerify/Pipeline/GameVerifyPipeline.cs +++ b/src/ModVerify/Pipeline/GameVerifyPipeline.cs @@ -19,8 +19,8 @@ namespace AET.ModVerify.Pipeline; public sealed class GameVerifyPipeline : StepRunnerPipelineBase { - private readonly List _verifiers = new(); - private readonly List _verificationSteps = new(); + private readonly List _verifiers = []; + private readonly List _verificationSteps = []; private readonly ConcurrentGameEngineErrorReporter _engineErrorReporter = new(); private readonly VerificationTarget _verificationTarget; diff --git a/src/ModVerify/Reporting/Json/JsonVerificationTarget.cs b/src/ModVerify/Reporting/Json/JsonVerificationTarget.cs index 4b74b6f..451f7f7 100644 --- a/src/ModVerify/Reporting/Json/JsonVerificationTarget.cs +++ b/src/ModVerify/Reporting/Json/JsonVerificationTarget.cs @@ -39,7 +39,7 @@ public JsonVerificationTarget(VerificationTarget target) public static VerificationTarget? ToTarget(JsonVerificationTarget? jsonTarget) { if (jsonTarget is null) - return null!; + return null; return new VerificationTarget { Engine = jsonTarget.Engine, From 95cfd743977629bd88e99bd3d52bc3fe7064067e Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 17 Jan 2026 10:49:24 +0100 Subject: [PATCH 21/22] update module --- modules/ModdingToolBase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 9ab5f4b..3552005 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 9ab5f4b0be7541123dfe91c67aabd12711a25cfa +Subproject commit 3552005cbaa2b7199698924d17786550e370f7ba From 45ca2e67646f302cb0fe5f4d7b5dc4797c4889ee Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 18 Jan 2026 10:35:52 +0100 Subject: [PATCH 22/22] various little changes --- src/ModVerify.CliApp/ModVerifyApplication.cs | 38 +++++++++---------- src/ModVerify.CliApp/Program.cs | 2 +- .../Properties/launchSettings.json | 2 +- .../Reporting/BaselineFactory.cs | 35 ++++++++++++++--- .../Reporting/BaselineSelector.cs | 10 ++--- .../Settings/CommandLine/VerifyVerbOption.cs | 2 +- .../Settings/ModVerifyAppSettings.cs | 26 +++++++++++++ .../Settings/SettingsBuilder.cs | 2 +- .../Reporting/Json/JsonBaselineParser.cs | 7 ++-- .../Reporting/VerificationBaseline.cs | 21 ++++++++++ .../Resources/Schemas/2.1/baseline.json | 3 +- .../EmbeddedBaselineTest.cs | 4 +- 12 files changed, 109 insertions(+), 43 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs index 4d45979..ee71dcf 100644 --- a/src/ModVerify.CliApp/ModVerifyApplication.cs +++ b/src/ModVerify.CliApp/ModVerifyApplication.cs @@ -10,7 +10,6 @@ using Serilog; using System; using System.Collections.Generic; -using System.IO; using System.IO.Abstractions; using System.Linq; using System.Threading.Tasks; @@ -26,21 +25,21 @@ internal sealed class ModVerifyApplication(ModVerifyAppSettings settings, IServi private readonly IFileSystem _fileSystem = services.GetRequiredService(); private readonly ModVerifyAppEnvironment _appEnvironment = services.GetRequiredService(); - public async Task Run() + public async Task RunAsync() { using (new UnhandledExceptionHandler(services)) using (new UnobservedTaskExceptionHandler(services)) - return await RunCore().ConfigureAwait(false); + return await RunCoreAsync().ConfigureAwait(false); } - private async Task RunCore() + private async Task RunCoreAsync() { _logger?.LogDebug("Raw command line: {CommandLine}", Environment.CommandLine); var interactive = settings.Interactive; try { - return await RunVerify().ConfigureAwait(false); + return await RunModVerifyAsync().ConfigureAwait(false); } catch (Exception e) { @@ -66,7 +65,7 @@ private async Task RunCore() } - private async Task RunVerify() + private async Task RunModVerifyAsync() { VerificationTarget verificationTarget; try @@ -102,28 +101,34 @@ private async Task RunVerify() _logger?.LogDebug("Verification taget: {Target}", verificationTarget); _logger?.LogTrace("Verify settings: {Settings}", settings); - var allErrors = await Verify(verificationTarget, reportSettings) + var allErrors = await VerifyTargetAsync(verificationTarget, reportSettings) .ConfigureAwait(false); + // TODO: Refactor method to represent "verify" and "baseline" mode of the app. + // Also display to user more prominently which mode is active. + try { await ReportErrors(allErrors).ConfigureAwait(false); } catch (GameVerificationException e) { + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, + "The verification of {Target} completed with findings of the specified failure severity {Severity}", + verificationTarget.Name, settings.AppThrowsOnMinimumSeverity); return e.HResult; } if (!settings.CreateNewBaseline) return 0; - await WriteBaseline(verificationTarget, reportSettings, allErrors, settings.NewBaselinePath).ConfigureAwait(false); + await WriteBaselineAsync(verificationTarget, reportSettings, allErrors, settings.NewBaselinePath).ConfigureAwait(false); _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Baseline successfully created."); return 0; } - private async Task> Verify( + private async Task> VerifyTargetAsync( VerificationTarget verificationTarget, GlobalVerifyReportSettings reportSettings) { @@ -181,22 +186,15 @@ private async Task ReportErrors(IReadOnlyCollection errors) throw new GameVerificationException(errors); } - private async Task WriteBaseline( + private async Task WriteBaselineAsync( VerificationTarget target, GlobalVerifyReportSettings reportSettings, IEnumerable errors, string baselineFile) { - var baseline = new VerificationBaseline(reportSettings.MinimumReportSeverity, errors, target); - - var fullPath = _fileSystem.Path.GetFullPath(baselineFile); - _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Writing Baseline to '{FullPath}'", fullPath); - -#if NET - await -#endif - using var fs = _fileSystem.FileStream.New(fullPath, FileMode.Create, FileAccess.Write, FileShare.None); - await baseline.ToJsonAsync(fs); + var baselineFactory = services.GetRequiredService(); + var baseline = baselineFactory.CreateBaseline(target, reportSettings, errors); + await baselineFactory.WriteBaselineAsync(baseline, baselineFile); } private GlobalVerifyReportSettings CreateGlobalReportSettings(VerificationTarget verificationTarget) diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index cd4747b..2d293b9 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -173,7 +173,7 @@ protected override async Task RunAppAsync(string[] args, IServiceProvider a return e.HResult; } - return await new ModVerifyApplication(modVerifySettings, appServiceProvider).Run().ConfigureAwait(false); + return await new ModVerifyApplication(modVerifySettings, appServiceProvider).RunAsync().ConfigureAwait(false); } private void SetupVerifyReporting(IServiceCollection serviceCollection) diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index bafb11d..0dc1305 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -6,7 +6,7 @@ }, "Interactive Verify": { "commandName": "Project", - "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --offline" + "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --offline --failFast" }, "Interactive Baseline": { "commandName": "Project", diff --git a/src/ModVerify.CliApp/Reporting/BaselineFactory.cs b/src/ModVerify.CliApp/Reporting/BaselineFactory.cs index 9fcc911..1b49e88 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineFactory.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineFactory.cs @@ -1,10 +1,13 @@ -using System; +using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting.Settings; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Abstractions; -using AET.ModVerify.Reporting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; +using System.Threading.Tasks; namespace AET.ModVerify.App.Reporting; @@ -13,7 +16,7 @@ internal sealed class BaselineFactory(IServiceProvider serviceProvider) private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(BaselineFactory)); private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); - public bool TryCreateBaseline( + public bool TryFindBaselineInDirectory( string directory, out VerificationBaseline baseline, [NotNullWhen(true)] out string? path) @@ -58,11 +61,31 @@ public bool TryCreateBaseline( return false; } - public VerificationBaseline CreateBaseline(string filePath) + public VerificationBaseline ParseBaseline(string filePath) { return CreateBaselineFromFilePath(filePath); } + public VerificationBaseline CreateBaseline( + VerificationTarget target, + GlobalVerifyReportSettings reportSettings, + IEnumerable errors) + { + return new VerificationBaseline(reportSettings.MinimumReportSeverity, errors, target); + } + + public async Task WriteBaselineAsync(VerificationBaseline baseline, string filePath) + { + var fullPath = _fileSystem.Path.GetFullPath(filePath); + _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Writing Baseline to '{FullPath}'", fullPath); + +#if NET + await +#endif + using var fs = _fileSystem.FileStream.New(fullPath, FileMode.Create, FileAccess.Write, FileShare.None); + await baseline.ToJsonAsync(fs); + } + private VerificationBaseline CreateBaselineFromFilePath(string baselineFile) { using var fs = _fileSystem.FileStream.New(baselineFile, FileMode.Open, FileAccess.Read); diff --git a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs index 9fa637f..348c3a6 100644 --- a/src/ModVerify.CliApp/Reporting/BaselineSelector.cs +++ b/src/ModVerify.CliApp/Reporting/BaselineSelector.cs @@ -23,7 +23,7 @@ public VerificationBaseline SelectBaseline(VerificationTarget verificationTarget try { usedBaselinePath = baselinePath; - return _baselineFactory.CreateBaseline(baselinePath!); + return _baselineFactory.ParseBaseline(baselinePath!); } catch (InvalidBaselineException e) { @@ -65,10 +65,10 @@ private VerificationBaseline FindBaselineInteractive(VerificationTarget verifica _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Searching for local baseline files..."); - if (!_baselineFactory.TryCreateBaseline(verificationTarget.Location.TargetPath, out var baseline, + if (!_baselineFactory.TryFindBaselineInDirectory(verificationTarget.Location.TargetPath, out var baseline, out baselinePath)) { - if (!_baselineFactory.TryCreateBaseline("./", out baseline, out baselinePath)) + if (!_baselineFactory.TryFindBaselineInDirectory("./", 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) @@ -115,7 +115,7 @@ private VerificationBaseline TryGetDefaultBaseline(GameEngineType engineType, ou } } - internal VerificationBaseline LoadEmbeddedBaseline(GameEngineType engineType) + internal static VerificationBaseline LoadEmbeddedBaseline(GameEngineType engineType) { var baselineFileName = $"baseline-{engineType.ToString().ToLower()}.json"; var resourcePath = $"{typeof(BaselineResources).Namespace}.{baselineFileName}"; @@ -126,7 +126,7 @@ internal VerificationBaseline LoadEmbeddedBaseline(GameEngineType engineType) private VerificationBaseline FindBaselineNonInteractive(string targetPath, out string? usedPath) { - if (_baselineFactory.TryCreateBaseline(targetPath, out var baseline, out usedPath)) + if (_baselineFactory.TryFindBaselineInDirectory(targetPath, out var baseline, out usedPath)) { _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Automatically applying local baseline file '{Path}'.", usedPath); return baseline; diff --git a/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs b/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs index d833f12..9581a9c 100644 --- a/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs +++ b/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs @@ -20,7 +20,7 @@ internal sealed class VerifyVerbOption : BaseModVerifyOptions 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.")] + HelpText = "When set, the application returns with an error, if any finding has at least the specified severity value.")] public VerificationSeverity? MinimumFailureSeverity { get; set; } [Option("ignoreAsserts", Required = false, diff --git a/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs b/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs index e65464c..0419f33 100644 --- a/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs +++ b/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs @@ -20,4 +20,30 @@ internal sealed class ModVerifyAppSettings public bool CreateNewBaseline => !string.IsNullOrEmpty(NewBaselinePath); public string? NewBaselinePath { get; init; } +} + + +internal enum AppMode +{ + Verify, + Baseline +} + +internal abstract class ModVerifyAppSettingsBase +{ + public abstract AppMode Mode { get; } + public bool IsInteractive => VerificationTargetSettings.Interactive; + public required VerificationTargetSettings VerificationTargetSettings { get; init; } +} + +internal sealed class VerifyAppSettings : ModVerifyAppSettingsBase +{ + public override AppMode Mode => AppMode.Verify; + public VerificationSeverity? AppThrowsOnMinimumSeverity { get; init; } +} + +internal sealed class BaselineAppSettings : ModVerifyAppSettingsBase +{ + public override AppMode Mode => AppMode.Baseline; + public required string NewBaselinePath { 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 02b2a01..8a31301 100644 --- a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs @@ -55,7 +55,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 '{Info}'.", VerificationSeverity.Information); + "Verification is configured to fail fast but 'minFailSeverity' is not specified. Using severity '{Severity}'.", VerificationSeverity.Information); minFailSeverity = VerificationSeverity.Information; } diff --git a/src/ModVerify/Reporting/Json/JsonBaselineParser.cs b/src/ModVerify/Reporting/Json/JsonBaselineParser.cs index d3b4acd..669ef53 100644 --- a/src/ModVerify/Reporting/Json/JsonBaselineParser.cs +++ b/src/ModVerify/Reporting/Json/JsonBaselineParser.cs @@ -4,7 +4,7 @@ namespace AET.ModVerify.Reporting.Json; -public static class JsonBaselineParser +internal static class JsonBaselineParser { public static VerificationBaseline Parse(Stream dataStream) { @@ -13,7 +13,7 @@ public static VerificationBaseline Parse(Stream dataStream) try { var jsonNode = JsonDocument.Parse(dataStream); - var jsonBaseline = ParseCore(jsonNode); + var jsonBaseline = EvaluateAndDeserialize(jsonNode); if (jsonBaseline is null) throw new InvalidBaselineException($"Unable to parse input from stream to {nameof(VerificationBaseline)}. Unknown Error!"); @@ -26,11 +26,10 @@ public static VerificationBaseline Parse(Stream dataStream) } } - private static JsonVerificationBaseline? ParseCore(JsonDocument? json) + private static JsonVerificationBaseline? EvaluateAndDeserialize(JsonDocument? json) { if (json is null) return null; - JsonBaselineSchema.Evaluate(json.RootElement); return json.Deserialize(); } diff --git a/src/ModVerify/Reporting/VerificationBaseline.cs b/src/ModVerify/Reporting/VerificationBaseline.cs index 1cc8312..ca8700d 100644 --- a/src/ModVerify/Reporting/VerificationBaseline.cs +++ b/src/ModVerify/Reporting/VerificationBaseline.cs @@ -7,6 +7,7 @@ using System.Text.Json; using System.Threading.Tasks; using AET.ModVerify.Reporting.Json; +using PG.StarWarsGame.Engine; namespace AET.ModVerify.Reporting; @@ -88,4 +89,24 @@ public override string ToString() sb.Append(']'); return sb.ToString(); } +} + +public sealed class BaselineVerificationTarget +{ + public required GameEngineType Engine { get; init; } + public required string Name { get; init; } + public GameLocations? Location { get; init; } + public string? Version { get; init; } + public bool IsGame => Location.ModPaths.Count == 0; + + + + public override string ToString() + { + var sb = new StringBuilder($"[Name={Name};EngineType={Engine};"); + if (!string.IsNullOrEmpty(Version)) sb.Append($"Version={Version};"); + sb.Append($"Location={Location};"); + sb.Append(']'); + return sb.ToString(); + } } \ No newline at end of file diff --git a/src/ModVerify/Resources/Schemas/2.1/baseline.json b/src/ModVerify/Resources/Schemas/2.1/baseline.json index 56bfb86..ed5a014 100644 --- a/src/ModVerify/Resources/Schemas/2.1/baseline.json +++ b/src/ModVerify/Resources/Schemas/2.1/baseline.json @@ -48,8 +48,7 @@ }, "required": [ "name", - "engine", - "location" + "engine" ], "additionalProperties": false }, diff --git a/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs index 3272261..1747677 100644 --- a/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs +++ b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs @@ -16,6 +16,7 @@ namespace ModVerify.CliApp.Test; public class BaselineSelectorTest { private static readonly IFileSystem FileSystem = new RealFileSystem(); + private readonly IServiceProvider _serviceProvider; private static readonly ModVerifyAppSettings TestSettings = new() { ReportSettings = new(), @@ -27,7 +28,6 @@ public class BaselineSelectorTest } }; - private readonly IServiceProvider _serviceProvider; public BaselineSelectorTest() { @@ -43,6 +43,6 @@ public BaselineSelectorTest() 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); + BaselineSelector.LoadEmbeddedBaseline(engineType); } } \ No newline at end of file