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:
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
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
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."
diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase
index 479a088..18cfb7a 160000
--- a/modules/ModdingToolBase
+++ b/modules/ModdingToolBase
@@ -1 +1 @@
-Subproject commit 479a088a2b26dd4a3e2342b2e34f5359b0252e88
+Subproject commit 18cfb7a31a091ebc57708ca394b0d2e406343b31
diff --git a/src/ModVerify.CliApp/App/CreateBaselineAction.cs b/src/ModVerify.CliApp/App/CreateBaselineAction.cs
new file mode 100644
index 0000000..b0eb880
--- /dev/null
+++ b/src/ModVerify.CliApp/App/CreateBaselineAction.cs
@@ -0,0 +1,56 @@
+using AET.ModVerify.App.Reporting;
+using AET.ModVerify.App.Settings;
+using AET.ModVerify.App.Utilities;
+using AET.ModVerify.Reporting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO.Abstractions;
+using System.Threading.Tasks;
+
+namespace AET.ModVerify.App;
+
+internal sealed class CreateBaselineAction(AppBaselineSettings settings, IServiceProvider serviceProvider)
+ : ModVerifyApplicationAction(settings, serviceProvider)
+{
+ private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService();
+
+ protected override void PrintAction(VerificationTarget target)
+ {
+ Console.WriteLine();
+ Console.ForegroundColor = ConsoleColor.DarkGreen;
+ Console.WriteLine($"Creating baseline for {target.Name}...");
+ Console.WriteLine();
+ ModVerifyConsoleUtilities.WriteSelectedTarget(target);
+ Console.WriteLine();
+ }
+
+ protected override async Task ProcessVerifyFindings(
+ VerificationTarget verificationTarget,
+ IReadOnlyCollection allErrors)
+ {
+ var baselineFactory = ServiceProvider.GetRequiredService();
+ var baseline = baselineFactory.CreateBaseline(verificationTarget, Settings, allErrors);
+
+ var fullPath = _fileSystem.Path.GetFullPath(Settings.NewBaselinePath);
+ Logger?.LogInformation(ModVerifyConstants.ConsoleEventId,
+ "Writing Baseline to '{FullPath}' with {Number} findings", fullPath, allErrors.Count);
+
+ await baselineFactory.WriteBaselineAsync(baseline, Settings.NewBaselinePath);
+
+ Logger?.LogDebug("Baseline successfully created.");
+
+ Console.WriteLine();
+ Console.ForegroundColor = ConsoleColor.DarkGreen;
+ Console.WriteLine($"Baseline for {verificationTarget.Name} created.");
+ Console.ResetColor();
+
+ return ModVerifyConstants.Success;
+ }
+
+ protected override VerificationBaseline GetBaseline(VerificationTarget verificationTarget)
+ {
+ return VerificationBaseline.Empty;
+ }
+}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/App/IModVerifyAppAction.cs b/src/ModVerify.CliApp/App/IModVerifyAppAction.cs
new file mode 100644
index 0000000..1a75b84
--- /dev/null
+++ b/src/ModVerify.CliApp/App/IModVerifyAppAction.cs
@@ -0,0 +1,8 @@
+using System.Threading.Tasks;
+
+namespace AET.ModVerify.App;
+
+internal interface IModVerifyAppAction
+{
+ Task ExecuteAsync();
+}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/App/ModVerifyApplication.cs b/src/ModVerify.CliApp/App/ModVerifyApplication.cs
new file mode 100644
index 0000000..eae7851
--- /dev/null
+++ b/src/ModVerify.CliApp/App/ModVerifyApplication.cs
@@ -0,0 +1,68 @@
+using AET.ModVerify.App.Settings;
+using AnakinRaW.ApplicationBase;
+using AnakinRaW.ApplicationBase.Utilities;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Serilog;
+using System;
+using System.Threading.Tasks;
+using ILogger = Microsoft.Extensions.Logging.ILogger;
+
+namespace AET.ModVerify.App;
+
+internal sealed class ModVerifyApplication(AppSettingsBase settings, IServiceProvider services)
+{
+ private readonly ILogger? _logger = services.GetService()?.CreateLogger(typeof(ModVerifyApplication));
+
+ public async Task RunAsync()
+ {
+ using (new UnhandledExceptionHandler(services))
+ using (new UnobservedTaskExceptionHandler(services))
+ return await RunCoreAsync().ConfigureAwait(false);
+ }
+
+ private async Task RunCoreAsync()
+ {
+ _logger?.LogDebug("Raw command line: {CommandLine}", Environment.CommandLine);
+
+ try
+ {
+ var action = CreateAppAction();
+ return await action.ExecuteAsync().ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ _logger?.LogCritical(e, e.Message);
+ ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, e);
+ return e.HResult;
+ }
+ finally
+ {
+#if NET
+ await Log.CloseAndFlushAsync();
+#else
+ Log.CloseAndFlush();
+#endif
+ if (settings is AppVerifySettings { IsInteractive: true })
+ {
+ Console.WriteLine();
+ ConsoleUtilities.WriteHorizontalLine('-');
+ Console.WriteLine("Press any key to exit");
+ Console.ReadLine();
+ }
+ }
+ }
+
+ private IModVerifyAppAction CreateAppAction()
+ {
+ switch (settings)
+ {
+ case AppVerifySettings verifySettings:
+ return new VerifyAction(verifySettings, services);
+ case AppBaselineSettings baselineSettings:
+ return new CreateBaselineAction(baselineSettings, services);
+ default:
+ throw new InvalidOperationException("Unknown settings");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/App/ModVerifyApplicationAction.cs b/src/ModVerify.CliApp/App/ModVerifyApplicationAction.cs
new file mode 100644
index 0000000..e10bfde
--- /dev/null
+++ b/src/ModVerify.CliApp/App/ModVerifyApplicationAction.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.IO.Abstractions;
+using System.Threading.Tasks;
+using AET.ModVerify.App.GameFinder;
+using AET.ModVerify.App.Reporting;
+using AET.ModVerify.App.Settings;
+using AET.ModVerify.App.TargetSelectors;
+using AET.ModVerify.Pipeline;
+using AET.ModVerify.Reporting;
+using AnakinRaW.ApplicationBase;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace AET.ModVerify.App;
+
+internal abstract class ModVerifyApplicationAction : IModVerifyAppAction where T : AppSettingsBase
+{
+ private readonly ModVerifyAppEnvironment _appEnvironment;
+ private readonly IFileSystem _fileSystem;
+
+ protected T Settings { get; }
+
+ protected IServiceProvider ServiceProvider { get; }
+
+ protected ILogger? Logger { get; }
+
+ protected ModVerifyApplicationAction(T settings, IServiceProvider serviceProvider)
+ {
+ Settings = settings ?? throw new ArgumentNullException(nameof(settings));
+ ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ Logger = serviceProvider.GetService()?.CreateLogger(GetType());
+ _appEnvironment = ServiceProvider.GetRequiredService();
+ _fileSystem = ServiceProvider.GetRequiredService();
+ }
+
+ protected virtual void PrintAction(VerificationTarget target)
+ {
+ }
+
+ public async Task ExecuteAsync()
+ {
+ VerificationTarget verificationTarget;
+ try
+ {
+ var targetSettings = Settings.VerificationTargetSettings;
+ verificationTarget = new VerificationTargetSelectorFactory(ServiceProvider)
+ .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,
+ "Unable to find an installation of Empire at War or Forces of Corruption.");
+ Logger?.LogError(ex, "Game not found: {Message}", ex.Message);
+ return ex.HResult;
+ }
+
+ PrintAction(verificationTarget);
+
+ var allErrors = await VerifyTargetAsync(verificationTarget)
+ .ConfigureAwait(false);
+
+ return await ProcessVerifyFindings(verificationTarget, allErrors);
+ }
+
+ protected abstract Task ProcessVerifyFindings(
+ VerificationTarget verificationTarget,
+ IReadOnlyCollection allErrors);
+
+ protected abstract VerificationBaseline GetBaseline(VerificationTarget verificationTarget);
+
+ private async Task> VerifyTargetAsync(VerificationTarget verificationTarget)
+ {
+ var progressReporter = new VerifyConsoleProgressReporter(verificationTarget.Name, Settings.ReportSettings);
+
+ var baseline = GetBaseline(verificationTarget);
+ var suppressions = GetSuppressions();
+
+ using var verifyPipeline = new GameVerifyPipeline(
+ verificationTarget,
+ Settings.VerifyPipelineSettings,
+ progressReporter,
+ new EngineInitializeProgressReporter(verificationTarget.Engine),
+ baseline,
+ suppressions,
+ ServiceProvider);
+
+ try
+ {
+ Logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Verifying '{Target}'...", verificationTarget.Name);
+ await verifyPipeline.RunAsync().ConfigureAwait(false);
+ progressReporter.Report(string.Empty, 1.0);
+ }
+ catch (OperationCanceledException)
+ {
+ Logger?.LogWarning(ModVerifyConstants.ConsoleEventId, "Verification stopped due to enabled failFast setting.");
+ }
+ catch (Exception e)
+ {
+ progressReporter.ReportError("Verification failed!", e.Message);
+ Logger?.LogError(e, "Verification failed: {Message}", e.Message);
+ throw;
+ }
+ finally
+ {
+ progressReporter.Dispose();
+ }
+
+ Logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Finished verification");
+ return verifyPipeline.FilteredErrors;
+ }
+
+ private SuppressionList GetSuppressions()
+ {
+ var suppressionsFile = Settings.ReportSettings.SuppressionsPath;
+ SuppressionList suppressions;
+ if (string.IsNullOrEmpty(suppressionsFile))
+ suppressions = SuppressionList.Empty;
+ else
+ {
+ using var fileStream = _fileSystem.File.OpenRead(suppressionsFile);
+ suppressions = SuppressionList.FromJson(fileStream);
+ if (suppressions.Count > 0)
+ Logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Using suppressions from '{Suppressions}'", suppressionsFile);
+ }
+ return suppressions;
+ }
+}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/App/VerifyAction.cs b/src/ModVerify.CliApp/App/VerifyAction.cs
new file mode 100644
index 0000000..0cfea0e
--- /dev/null
+++ b/src/ModVerify.CliApp/App/VerifyAction.cs
@@ -0,0 +1,60 @@
+using AET.ModVerify.App.Reporting;
+using AET.ModVerify.App.Settings;
+using AET.ModVerify.Reporting;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using AET.ModVerify.App.Utilities;
+
+namespace AET.ModVerify.App;
+
+internal sealed class VerifyAction(AppVerifySettings settings, IServiceProvider services)
+ : ModVerifyApplicationAction(settings, services)
+{
+ protected override void PrintAction(VerificationTarget target)
+ {
+ Console.WriteLine();
+ Console.ForegroundColor = ConsoleColor.DarkGreen;
+ Console.WriteLine($"Verifying {target.Name} for issues...");
+ Console.WriteLine();
+ ModVerifyConsoleUtilities.WriteSelectedTarget(target);
+ Console.WriteLine();
+ }
+
+ protected override async Task ProcessVerifyFindings(
+ VerificationTarget verificationTarget,
+ IReadOnlyCollection allErrors)
+ {
+ Logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Reporting Errors...");
+ var reportBroker = new VerificationReportBroker(ServiceProvider);
+ await reportBroker.ReportAsync(allErrors);
+
+ if (Settings.AppFailsOnMinimumSeverity.HasValue &&
+ allErrors.Any(x => x.Severity >= Settings.AppFailsOnMinimumSeverity))
+ {
+ Logger?.LogInformation(ModVerifyConstants.ConsoleEventId,
+ "The verification of {Target} completed with findings of the specified failure severity {Severity}",
+ verificationTarget.Name, Settings.AppFailsOnMinimumSeverity);
+
+ return ModVerifyConstants.CompletedWithFindings;
+ }
+
+ return ModVerifyConstants.Success;
+ }
+
+ protected override VerificationBaseline GetBaseline(VerificationTarget verificationTarget)
+ {
+ var baselineSelector = new BaselineSelector(Settings, ServiceProvider);
+ var baseline = baselineSelector.SelectBaseline(verificationTarget, out var baselinePath);
+ if (!baseline.IsEmpty)
+ {
+ Console.WriteLine();
+ ModVerifyConsoleUtilities.WriteBaselineInfo(baseline, baselinePath);
+ Logger?.LogDebug("Using baseline {Baseline} from location '{Path}'", baseline.ToString(), baselinePath);
+ Console.WriteLine();
+ }
+ return baseline;
+ }
+}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/AppArgumentException.cs b/src/ModVerify.CliApp/AppArgumentException.cs
new file mode 100644
index 0000000..8ead9ed
--- /dev/null
+++ b/src/ModVerify.CliApp/AppArgumentException.cs
@@ -0,0 +1,5 @@
+using System;
+
+namespace AET.ModVerify.App;
+
+internal class AppArgumentException(string message) : ArgumentException(message);
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs
index a88ebd9..0ac8346 100644
--- a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs
+++ b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs
@@ -1,11 +1,17 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO.Abstractions;
+using System.Runtime.InteropServices;
+using AET.ModVerify.App.Utilities;
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.Games.Registry;
using PG.StarWarsGame.Infrastructure.Mods;
using PG.StarWarsGame.Infrastructure.Services;
using PG.StarWarsGame.Infrastructure.Services.Detection;
@@ -29,7 +35,7 @@ public GameFinderService(IServiceProvider serviceProvider)
_logger = _serviceProvider.GetService()?.CreateLogger(GetType());
}
- public GameFinderResult FindGames()
+ public GameFinderResult FindGames(GameFinderSettings settings)
{
var detectors = new List
{
@@ -37,10 +43,47 @@ public GameFinderResult FindGames()
new SteamPetroglyphStarWarsGameDetector(_serviceProvider),
};
- return FindGames(detectors);
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ var registryFactory = _serviceProvider.GetRequiredService();
+ detectors.Add(new RegistryGameDetector(
+ registryFactory.CreateRegistry(GameType.Eaw),
+ registryFactory.CreateRegistry(GameType.Foc),
+ false, _serviceProvider));
+ }
+
+ 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 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:
// 1. path points to the actual game directory
@@ -49,87 +92,136 @@ public GameFinderResult FindGamesFromPathOrGlobal(string path)
// 4. path points to a "detached mod" at a completely different location
var givenDirectory = _fileSystem.DirectoryInfo.New(path);
var possibleGameDir = givenDirectory.Parent?.Parent;
+
+
+ // We need to check the local paths first, before falling back to global detectors,
+ // to ensure that we always find the correct game installation,
+ // especially if the user did not request a specific engine.
- var detectors = new List
+ var localDetectors = new List
{
- // Case 1
- new DirectoryGameDetector(givenDirectory, _serviceProvider)
+ new DirectoryGameDetector(givenDirectory, _serviceProvider),
};
+ if (possibleGameDir is not null)
+ localDetectors.Add(new DirectoryGameDetector(possibleGameDir, _serviceProvider));
- // Case 2
- if (possibleGameDir is not null)
- detectors.Add(new DirectoryGameDetector(possibleGameDir, _serviceProvider));
+ // Case 1 & 2
+ if (TryFindGames(localDetectors, settings, out var finderResult))
+ return finderResult;
+
+
+ // There is the rare scenario where the user specified a specific engine,
+ // but path points to a game with the opposite engine.
+ // This does not make sense and the global detectors can not handle this.
+ // Thus, we need to check against this.
+ if (settings.Engine is not null)
+ {
+ if (TryFindGame(path, new GameFinderSettings
+ {
+ Engine = settings.Engine.Value.Opposite(),
+ InitMods = false,
+ SearchFallbackGame = false
+ }, out _))
+ {
+ var e = new GameNotFoundException(
+ $"The specified game engine '{settings.Engine.Value}' does not match engine of the specified path '{path}'.");
+ _logger?.LogTrace(e, e.Message);
+ throw e;
+ }
+ }
// Cases 3 & 4
- detectors.Add(new SteamPetroglyphStarWarsGameDetector(_serviceProvider));
- return FindGames(detectors);
+ return FindGames(CreateGlobalDetectors(), settings);
}
- private bool TryDetectGame(GameType gameType, IList detectors, out GameDetectionResult result)
+ private bool TryFindGames(IList detectors, GameFinderSettings settings,
+ [NotNullWhen(true)] out GameFinderResult? finderResult)
{
- var gd = new CompositeGameDetector(detectors, _serviceProvider);
-
try
{
- result = gd.Detect(gameType);
- if (result.GameLocation is null)
- return false;
+ finderResult = FindGames(detectors, settings);
return true;
}
- catch (Exception e)
+ catch (GameNotFoundException)
{
- result = GameDetectionResult.NotInstalled(gameType);
- _logger?.LogTrace("Unable to find game installation: {Message}", e.Message);
+ finderResult = null;
return false;
}
}
- 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))
+ 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)
- throw new GameNotFoundException("Unable to find game installation: Wrong install path?");
+ Debug.Assert(detectionResult.GameLocation is not null);
- _logger?.LogInformation(ModVerifyConstants.ConsoleEventId,
- "Found game installation: {ResultGameIdentity} at {GameLocationFullName}", result.GameIdentity, result.GameLocation.FullName);
+ _logger?.LogDebug("Found game installation: {ResultGameIdentity} at {GameLocationFullName}",
+ detectionResult.GameIdentity, detectionResult.GameLocation!.FullName);
- var game = _gameFactory.CreateGame(result, CultureInfo.InvariantCulture);
-
- SetupMods(game);
+ var game = _gameFactory.CreateGame(detectionResult, CultureInfo.InvariantCulture);
+ 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 (SearchForFallbackGame(settings, detectionResult))
{
- 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)
+ if (!TryDetectGame(GameType.Eaw, CreateGlobalDetectors(), out var fallbackResult))
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?.LogDebug("Found fallback game installation: {FallbackResultGameIdentity} at {GameLocationFullName}",
+ fallbackResult.GameIdentity, fallbackResult.GameLocation!.FullName);
fallbackGame = _gameFactory.CreateGame(fallbackResult, CultureInfo.InvariantCulture);
- 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, true);
+ result = gd.Detect(gameType);
+ return result.GameLocation is not null;
+ }
+
private void SetupMods(IGame game)
{
var modFinder = _serviceProvider.GetRequiredService();
@@ -152,4 +244,21 @@ private void SetupMods(IGame game)
mod.ResolveDependencies();
}
}
+
+ private IList CreateGlobalDetectors()
+ {
+ var detectors = new List
+ {
+ new SteamPetroglyphStarWarsGameDetector(_serviceProvider),
+ };
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ var registryFactory = _serviceProvider.GetRequiredService();
+ detectors.Add(new RegistryGameDetector(
+ registryFactory.CreateRegistry(GameType.Eaw),
+ registryFactory.CreateRegistry(GameType.Foc),
+ false, _serviceProvider));
+ }
+ return detectors;
+ }
}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/GameFinder/GameFinderSettings.cs b/src/ModVerify.CliApp/GameFinder/GameFinderSettings.cs
new file mode 100644
index 0000000..fc57b24
--- /dev/null
+++ b/src/ModVerify.CliApp/GameFinder/GameFinderSettings.cs
@@ -0,0 +1,14 @@
+using PG.StarWarsGame.Engine;
+
+namespace AET.ModVerify.App.GameFinder;
+
+internal sealed 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;
+}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs
deleted file mode 100644
index 717db7b..0000000
--- a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using System;
-using System.Globalization;
-using System.IO.Abstractions;
-using System.Linq;
-using AET.ModVerify.App.GameFinder;
-using AET.ModVerify.App.Settings;
-using AET.ModVerify.App.Utilities;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using PG.StarWarsGame.Engine;
-using PG.StarWarsGame.Infrastructure;
-using PG.StarWarsGame.Infrastructure.Games;
-using PG.StarWarsGame.Infrastructure.Mods;
-using PG.StarWarsGame.Infrastructure.Services;
-using PG.StarWarsGame.Infrastructure.Services.Detection;
-
-namespace AET.ModVerify.App.ModSelectors;
-
-internal class AutomaticModSelector(IServiceProvider serviceProvider) : ModSelectorBase(serviceProvider)
-{
- private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService();
-
- public override GameLocations? Select(
- GameInstallationsSettings settings,
- out IPhysicalPlayableObject? targetObject,
- out GameEngineType? actualEngineType)
- {
- var pathToVerify = settings.AutoPath;
- if (pathToVerify is null)
- throw new InvalidOperationException("path to verify cannot be null.");
-
- actualEngineType = settings.EngineType;
-
- GameFinderResult finderResult;
- try
- {
- finderResult = GameFinderService.FindGamesFromPathOrGlobal(pathToVerify);
- }
- 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;
- }
-
-
- var modOrGame = GetAttachedModOrGame(finderResult, actualEngineType, pathToVerify);
-
- if (modOrGame is not 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.");
-
- targetObject = modOrGame;
- return GetLocations(targetObject, finderResult, settings.AdditionalFallbackPaths);
- }
-
- if (!settings.EngineType.HasValue)
- throw new ArgumentException("Unable to determine game type. Use --type argument to set the game type.");
-
- Logger?.LogDebug("The requested mod at '{PathToVerify}' is detached from its games.", pathToVerify);
-
- // The path is a detached mod, that exists on a different location than the game.
- var result = GetDetachedModLocations(pathToVerify, finderResult, settings, out var mod);
- targetObject = mod;
- return result;
- }
-
- private IPhysicalPlayableObject? GetAttachedModOrGame(GameFinderResult finderResult, GameEngineType? requestedEngineType, string searchPath)
- {
- var fullSearchPath = _fileSystem.Path.GetFullPath(searchPath);
-
- if (finderResult.Game.Directory.FullName.Equals(fullSearchPath, 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.");
- return finderResult.Game;
- }
-
- if (finderResult.FallbackGame is not null &&
- finderResult.FallbackGame.Directory.FullName.Equals(fullSearchPath, StringComparison.OrdinalIgnoreCase))
- {
- 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.");
- return finderResult.FallbackGame;
- }
-
- return GetMatchingModFromGame(finderResult.Game, requestedEngineType, fullSearchPath) ??
- GetMatchingModFromGame(finderResult.FallbackGame, requestedEngineType, fullSearchPath);
- }
-
- private GameLocations GetDetachedModLocations(string modPath, GameFinderResult gameResult, GameInstallationsSettings settings, out IPhysicalMod mod)
- {
- IGame game = null!;
-
- if (gameResult.Game.Type.ToEngineType() == settings.EngineType)
- game = gameResult.Game;
- if (gameResult.FallbackGame is not null && gameResult.FallbackGame.Type.ToEngineType() == settings.EngineType)
- game = gameResult.FallbackGame;
-
- if (game is null)
- throw new GameNotFoundException($"Unable to find game of type '{settings.EngineType}'");
-
- 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}'.");
-
- var modFactory = ServiceProvider.GetRequiredService();
- mod = modFactory.CreatePhysicalMod(game, modRef, CultureInfo.InvariantCulture);
-
- game.AddMod(mod);
-
- mod.ResolveDependencies();
-
- return GetLocations(mod, gameResult, settings.AdditionalFallbackPaths);
- }
-
- private static IPhysicalMod? GetMatchingModFromGame(IGame? game, GameEngineType? requestedEngineType, string modPath)
- {
- if (game is null)
- 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 game type '{requestedEngineType}' does not match the actual type of the mod '{modPath}' to verify.");
- return physicalMod;
- }
- }
- }
-
- return null;
- }
-}
\ No newline at end of file
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/ManualModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs
deleted file mode 100644
index 34cf39d..0000000
--- a/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-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,
- settings.GamePath!,
- GetFallbackPaths(settings.FallbackGamePath, settings.AdditionalFallbackPaths));
- }
-}
\ 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 8dd1d90..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, playableObject.Game.Directory.FullName, fallbacks);
- }
-
- 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 221bb9e..0000000
--- a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs
+++ /dev/null
@@ -1,40 +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 VerifyInstallationData 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 VerifyInstallationData
- {
- EngineType = engineType.Value,
- GameLocations = gameLocations,
- Name = GetNameFromGameLocations(targetObject, gameLocations, engineType.Value)
- };
- }
-
- private static string GetNameFromGameLocations(IPlayableObject? targetObject, GameLocations gameLocations, GameEngineType engineType)
- {
- if (targetObject is not null)
- return targetObject.Name;
-
- var mod = gameLocations.ModPaths.FirstOrDefault();
- return mod ?? gameLocations.GamePath;
- }
-}
\ No newline at end of file
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/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj
index 0073b2b..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.CliApp/ModVerify.CliApp.csproj.DotSettings b/src/ModVerify.CliApp/ModVerify.CliApp.csproj.DotSettings
new file mode 100644
index 0000000..0bcf4ed
--- /dev/null
+++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
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/ModVerifyApplication.cs b/src/ModVerify.CliApp/ModVerifyApplication.cs
deleted file mode 100644
index 7ea461c..0000000
--- a/src/ModVerify.CliApp/ModVerifyApplication.cs
+++ /dev/null
@@ -1,251 +0,0 @@
-using AET.ModVerify.App.ModSelectors;
-using AET.ModVerify.App.Reporting;
-using AET.ModVerify.App.Settings;
-using AET.ModVerify.Pipeline;
-using AET.ModVerify.Reporting;
-using AET.ModVerify.Reporting.Settings;
-using AnakinRaW.ApplicationBase;
-using AnakinRaW.ApplicationBase.Utilities;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using PG.StarWarsGame.Engine;
-using Serilog;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.IO.Abstractions;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using AET.ModVerify.App.GameFinder;
-using ILogger = Microsoft.Extensions.Logging.ILogger;
-
-namespace AET.ModVerify.App;
-
-internal sealed class ModVerifyApplication(ModVerifyAppSettings settings, IServiceProvider services)
-{
- private readonly ILogger? _logger = services.GetService()?.CreateLogger(typeof(ModVerifyApplication));
- private readonly IFileSystem _fileSystem = services.GetRequiredService();
- private readonly ModVerifyAppEnvironment _appEnvironment = services.GetRequiredService();
-
- public async Task Run()
- {
- using (new UnhandledExceptionHandler(services))
- using (new UnobservedTaskExceptionHandler(services))
- return await RunCore().ConfigureAwait(false);
- }
-
- private async Task RunCore()
- {
- _logger?.LogDebug("Raw command line: {CommandLine}", Environment.CommandLine);
-
- var interactive = settings.Interactive;
- try
- {
- return await RunVerify().ConfigureAwait(false);
- }
- catch (Exception e)
- {
- _logger?.LogCritical(e, e.Message);
- ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, e);
- return e.HResult;
- }
- finally
- {
-#if NET
- await Log.CloseAndFlushAsync();
-#else
- Log.CloseAndFlush();
-#endif
- if (interactive)
- {
- Console.WriteLine();
- ConsoleUtilities.WriteHorizontalLine('-');
- Console.WriteLine("Press any key to exit");
- Console.ReadLine();
- }
- }
- }
-
-
- private async Task RunVerify()
- {
- VerifyInstallationData installData;
- try
- {
- installData = new SettingsBasedModSelector(services)
- .CreateInstallationDataFromSettings(settings.GameInstallationsSettings);
- }
- catch (GameNotFoundException ex)
- {
- ConsoleUtilities.WriteApplicationFatalError(_appEnvironment.ApplicationName,
- "Unable to find an installation of Empire at War or Forces of Corruption.");
- _logger?.LogError(ex, "Game not found: {Message}", ex.Message);
- return ex.HResult;
- }
-
- var reportSettings = CreateGlobalReportSettings(installData);
-
- _logger?.LogDebug("Verify install data: {InstallData}", installData);
- _logger?.LogTrace("Verify settings: {Settings}", settings);
-
- var allErrors = await Verify(installData, reportSettings)
- .ConfigureAwait(false);
-
- try
- {
- await ReportErrors(allErrors).ConfigureAwait(false);
- }
- catch (GameVerificationException e)
- {
- return e.HResult;
- }
-
- if (!settings.CreateNewBaseline)
- return 0;
-
- await WriteBaseline(reportSettings, allErrors, settings.NewBaselinePath).ConfigureAwait(false);
- _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Baseline successfully created.");
-
- return 0;
- }
-
- 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 progressReporter = new VerifyConsoleProgressReporter(installData.Name);
-
- using var verifyPipeline = new GameVerifyPipeline(
- gameEngine,
- engineErrorReporter,
- settings.VerifyPipelineSettings,
- reportSettings,
- progressReporter,
- services);
-
- try
- {
- try
- {
- _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Verifying '{Target}'...", installData.Name);
- await verifyPipeline.RunAsync().ConfigureAwait(false);
- progressReporter.Report(string.Empty, 1.0);
- }
- catch
- {
- progressReporter.ReportError("Verification failed", null);
- throw;
- }
- finally
- {
- progressReporter.Dispose();
- }
- }
- catch (OperationCanceledException)
- {
- _logger?.LogWarning(ModVerifyConstants.ConsoleEventId, "Verification stopped due to enabled failFast setting.");
- }
- catch (Exception e)
- {
- _logger?.LogError(e, "Verification failed: {Message}", e.Message);
- throw;
- }
-
- _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Finished verification");
- return verifyPipeline.FilteredErrors;
- }
-
- private async Task ReportErrors(IReadOnlyCollection errors)
- {
- _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Reporting Errors...");
-
- var reportBroker = new VerificationReportBroker(services);
-
- await reportBroker.ReportAsync(errors);
-
- if (errors.Any(x => x.Severity >= settings.AppThrowsOnMinimumSeverity))
- throw new GameVerificationException(errors);
- }
-
- private async Task WriteBaseline(
- GlobalVerifyReportSettings reportSettings,
- IEnumerable errors,
- string baselineFile)
- {
- var baseline = new VerificationBaseline(reportSettings.MinimumReportSeverity, errors);
-
- 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);
- }
-
- private GlobalVerifyReportSettings CreateGlobalReportSettings(VerifyInstallationData installData)
- {
- var baselineSelector = new BaselineSelector(settings, services);
- var baseline = baselineSelector.SelectBaseline(installData, out var baselinePath);
-
- if (baseline.Count > 0)
- _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Using baseline '{Baseline}'", baselinePath);
-
- var suppressionsFile = settings.ReportSettings.SuppressionsPath;
- SuppressionList suppressions;
-
- if (string.IsNullOrEmpty(suppressionsFile))
- suppressions = SuppressionList.Empty;
- else
- {
- using var fs = _fileSystem.File.OpenRead(suppressionsFile);
- suppressions = SuppressionList.FromJson(fs);
-
- if (suppressions.Count > 0)
- _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Using suppressions from '{Suppressions}'", suppressionsFile);
- }
-
-
- return new GlobalVerifyReportSettings
- {
- Baseline = baseline,
- Suppressions = suppressions,
- MinimumReportSeverity = settings.ReportSettings.MinimumReportSeverity,
- };
- }
-}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/ModVerifyConstants.cs b/src/ModVerify.CliApp/ModVerifyConstants.cs
index 6b60f06..041a4a5 100644
--- a/src/ModVerify.CliApp/ModVerifyConstants.cs
+++ b/src/ModVerify.CliApp/ModVerifyConstants.cs
@@ -9,5 +9,9 @@ internal static class ModVerifyConstants
public const string ModVerifyToolPath = "ModVerify";
public const int ConsoleEventIdValue = 1138;
+ public const int Success = 0;
+ public const int CompletedWithFindings = 1;
+ public const int ErrorBadArguments = 0xA0;
+
public static readonly EventId ConsoleEventId = new(ConsoleEventIdValue, "LogToConsole");
}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs
index cd4747b..2d23b3b 100644
--- a/src/ModVerify.CliApp/Program.cs
+++ b/src/ModVerify.CliApp/Program.cs
@@ -40,6 +40,7 @@
using System.IO.Abstractions;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
+using AET.ModVerify.App.Reporting;
using Testably.Abstractions;
using ILogger = Serilog.ILogger;
@@ -61,21 +62,22 @@ internal class Program : SelfUpdateableAppLifecycle
private static readonly string ModVerifyRootNameSpace = typeof(Program).Namespace!;
private static readonly CompiledExpression PrintToConsoleExpression = SerilogExpression.Compile($"EventId.Id = {ModVerifyConstants.ConsoleEventIdValue}");
- private static ModVerifyOptionsContainer _optionsContainer = null!;
-
- protected override async Task InitializeAppAsync(IReadOnlyList args)
+ private AppSettingsBase? _modVerifyAppSettings;
+ private ApplicationUpdateOptions _updateOptions = new();
+ private bool _offlineMode;
+ private bool _verboseMode;
+ private bool _isLaunchedWithoutArguments;
+
+ protected override async Task InitializeAppAsync(IReadOnlyList args, IServiceProvider bootstrapServices)
{
+ await base.InitializeAppAsync(args, bootstrapServices);
+
ModVerifyConsoleUtilities.WriteHeader(ApplicationEnvironment.AssemblyInfo.InformationalVersion);
- await base.InitializeAppAsync(args);
-
+ ModVerifyOptionsContainer parsedOptions;
try
{
- var settings = new ModVerifyOptionsParser(ApplicationEnvironment, BootstrapLoggerFactory).Parse(args);
- if (!settings.HasOptions)
- return 0xA0;
- _optionsContainer = settings;
- return 0;
+ parsedOptions = new ModVerifyOptionsParser(ApplicationEnvironment, BootstrapLoggerFactory).Parse(args);
}
catch (Exception e)
{
@@ -83,6 +85,41 @@ protected override async Task InitializeAppAsync(IReadOnlyList args
ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, e);
return e.HResult;
}
+
+ if (!parsedOptions.HasOptions)
+ return ModVerifyConstants.ErrorBadArguments;
+
+ if (parsedOptions.UpdateOptions is not null)
+ _updateOptions = parsedOptions.UpdateOptions;
+
+ if (parsedOptions.ModVerifyOptions?.Verbose is true || parsedOptions.UpdateOptions?.Verbose is true)
+ _verboseMode = true;
+
+ if (parsedOptions.ModVerifyOptions is null)
+ return ModVerifyConstants.Success;
+
+ _offlineMode = parsedOptions.ModVerifyOptions.OfflineMode;
+ _isLaunchedWithoutArguments = parsedOptions.ModVerifyOptions.LaunchedWithoutArguments();
+
+ try
+ {
+ _modVerifyAppSettings = new SettingsBuilder(bootstrapServices)
+ .BuildSettings(parsedOptions.ModVerifyOptions);
+ }
+ catch (AppArgumentException e)
+ {
+ Logger?.LogCritical(e, "Invalid arguments specified by the user: {Message}", e.Message);
+ ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, e.Message);
+ return e.HResult;
+ }
+ catch (Exception e)
+ {
+ Logger?.LogCritical(e, "Failed to create settings form commandline arguments: {Message}", e.Message);
+ ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, e);
+ return e.HResult;
+ }
+
+ return ModVerifyConstants.Success;
}
protected override void CreateAppServices(IServiceCollection services, IReadOnlyList args)
@@ -106,8 +143,8 @@ protected override void CreateAppServices(IServiceCollection services, IReadOnly
sp => new CosturaApplicationProductService(ApplicationEnvironment, sp),
sp => new JsonManifestLoader(sp));
}
-
- if (_optionsContainer.ModVerifyOptions is null)
+
+ if (_modVerifyAppSettings is null)
return;
SteamAbstractionLayer.InitializeServices(services);
@@ -122,10 +159,11 @@ protected override void CreateAppServices(IServiceCollection services, IReadOnly
PetroglyphEngineServiceContribution.ContributeServices(services);
services.RegisterVerifierCache();
-
+ services.AddSingleton(sp => new BaselineFactory(sp));
+
SetupVerifyReporting(services);
- if (_optionsContainer.ModVerifyOptions.OfflineMode)
+ if (_offlineMode)
{
services.AddSingleton(sp => new OfflineModNameResolver(sp));
services.AddSingleton(sp => new OfflineModGameTypeResolver(sp));
@@ -157,59 +195,39 @@ protected override IRegistry CreateRegistry()
protected override async Task RunAppAsync(string[] args, IServiceProvider appServiceProvider)
{
var result = await HandleUpdate(appServiceProvider);
- if (result != 0 || _optionsContainer.ModVerifyOptions is null)
+ if (result != 0 || _modVerifyAppSettings is null)
return result;
-
- ModVerifyAppSettings modVerifySettings;
-
- try
- {
- modVerifySettings = new SettingsBuilder(appServiceProvider).BuildSettings(_optionsContainer.ModVerifyOptions);
- }
- catch (Exception e)
- {
- Logger?.LogCritical(e, "Failed to create settings form commandline arguments: {EMessage}", e.Message);
- ConsoleUtilities.WriteApplicationFatalError(ModVerifyConstants.AppNameString, e);
- return e.HResult;
- }
-
- return await new ModVerifyApplication(modVerifySettings, appServiceProvider).Run().ConfigureAwait(false);
+ return await new ModVerifyApplication(_modVerifyAppSettings, appServiceProvider).RunAsync().ConfigureAwait(false);
}
private void SetupVerifyReporting(IServiceCollection serviceCollection)
{
- var options = _optionsContainer.ModVerifyOptions;
- Debug.Assert(options is not null);
-
+ Debug.Assert(_modVerifyAppSettings is not null);
- var verifyVerb = options as VerifyVerbOption;
+ var verifySettings = _modVerifyAppSettings as AppVerifySettings;
- // Console should be in minimal summary mode if we are not in verify mode.
- var printOnlySummary = verifyVerb is null;
-
- serviceCollection.RegisterConsoleReporter(new VerifyReportSettings
+ // Console should be in minimal summary mode if we are in a different mode than verify.
+ serviceCollection.RegisterConsoleReporter(new ReporterSettings
{
- MinimumReportSeverity = VerificationSeverity.Error
- }, printOnlySummary);
+ MinimumReportSeverity = verifySettings?.VerifyPipelineSettings.FailFastSettings.IsFailFast is true
+ ? VerificationSeverity.Information
+ : VerificationSeverity.Error
+ }, summaryOnly: verifySettings is null);
- if (verifyVerb == null)
+ if (verifySettings == null)
return;
- var outputDirectory = Environment.CurrentDirectory;
-
- if (!string.IsNullOrEmpty(verifyVerb.OutputDirectory))
- outputDirectory = FileSystem.Path.GetFullPath(FileSystem.Path.Combine(Environment.CurrentDirectory, verifyVerb.OutputDirectory!));
-
+ var outputDirectory = verifySettings.ReportDirectory;
serviceCollection.RegisterJsonReporter(new JsonReporterSettings
{
OutputDirectory = outputDirectory!,
- MinimumReportSeverity = options.MinimumSeverity
+ MinimumReportSeverity = _modVerifyAppSettings.ReportSettings.MinimumReportSeverity
});
serviceCollection.RegisterTextFileReporter(new TextFileReporterSettings
{
OutputDirectory = outputDirectory!,
- MinimumReportSeverity = options.MinimumSeverity
+ MinimumReportSeverity = _modVerifyAppSettings.ReportSettings.MinimumReportSeverity
});
}
@@ -224,7 +242,7 @@ private void ConfigureLogging(ILoggingBuilder loggingBuilder)
loggingBuilder.AddDebug();
#endif
- if (_optionsContainer.ModVerifyOptions?.Verbose == true || _optionsContainer.UpdateOptions?.Verbose == true)
+ if (_verboseMode)
{
logLevel = LogEventLevel.Verbose;
loggingBuilder.AddDebug();
@@ -297,31 +315,30 @@ static bool IsXmlParserLogging(LogEvent logEvent)
private async Task HandleUpdate(IServiceProvider serviceProvider)
{
- var updateOptions = _optionsContainer.UpdateOptions ?? new ApplicationUpdateOptions();
+ if (_offlineMode)
+ {
+ Logger?.LogTrace("Running in offline mode. There will be nothing to update.");
+ return ModVerifyConstants.Success;
+ }
+
ModVerifyUpdateMode updateMode;
- if (_optionsContainer.ModVerifyOptions is not null)
+ if (_isLaunchedWithoutArguments)
+ updateMode = ModVerifyUpdateMode.InteractiveUpdate;
+ else
{
- if (_optionsContainer.ModVerifyOptions.OfflineMode)
- {
- Logger?.LogTrace("Running in offline mode. There will be nothing to update.");
- return 0;
- }
-
- updateMode = _optionsContainer.ModVerifyOptions.LaunchedWithoutArguments()
- ? ModVerifyUpdateMode.InteractiveUpdate
- : ModVerifyUpdateMode.CheckOnly;
+ updateMode = _modVerifyAppSettings is not null
+ ? ModVerifyUpdateMode.CheckOnly
+ : ModVerifyUpdateMode.AutoUpdate;
}
- else
- updateMode = ModVerifyUpdateMode.AutoUpdate;
-
+
try
{
Logger?.LogDebug("Running update with mode '{ModVerifyUpdateMode}'", updateMode);
var modVerifyUpdater = new ModVerifyUpdater(serviceProvider);
- await modVerifyUpdater.RunUpdateProcedure(updateOptions, updateMode).ConfigureAwait(false);
+ await modVerifyUpdater.RunUpdateProcedure(_updateOptions, updateMode).ConfigureAwait(false);
Logger?.LogDebug("Update procedure completed successfully.");
- return 0;
+ return ModVerifyConstants.Success;
}
catch (Exception e)
{
diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json
index e47583a..299ce46 100644
--- a/src/ModVerify.CliApp/Properties/launchSettings.json
+++ b/src/ModVerify.CliApp/Properties/launchSettings.json
@@ -1,21 +1,20 @@
{
"profiles": {
- "Run": {
+ "Verify": {
"commandName": "Project",
"commandLineArgs": ""
},
- "Interactive Verify": {
+ "Verify (Interactive)": {
"commandName": "Project",
- "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --offline"
+ "commandLineArgs": "verify -o verifyResults --offline --minFailSeverity Information"
},
- "Interactive Baseline": {
+ "Verify (Automatic Target Selection)": {
"commandName": "Project",
- "commandLineArgs": "createBaseline -o focBaseline.json --offline"
+ "commandLineArgs": "verify -o verifyResults --path \"C:\\Program Files (x86)\\Steam\\steamapps\\common\\Star Wars Empire at War\\corruption\""
},
-
- "FromModPath": {
+ "Create Baseline Interactive": {
"commandName": "Project",
- "commandLineArgs": "-o verifyResults --baseline focBaseline.json --path C:/test --type Foc"
+ "commandLineArgs": "createBaseline -o baseline.json --offline --skipLocation"
}
}
}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/Reporting/BaselineFactory.cs b/src/ModVerify.CliApp/Reporting/BaselineFactory.cs
index 9fcc911..a83cea9 100644
--- a/src/ModVerify.CliApp/Reporting/BaselineFactory.cs
+++ b/src/ModVerify.CliApp/Reporting/BaselineFactory.cs
@@ -1,24 +1,31 @@
-using System;
-using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.IO.Abstractions;
+using AET.ModVerify.App.Settings;
using AET.ModVerify.Reporting;
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 System.Linq;
+using System.Threading.Tasks;
+using PG.StarWarsGame.Engine;
+using AET.ModVerify.App.Utilities;
namespace AET.ModVerify.App.Reporting;
-internal sealed class BaselineFactory(IServiceProvider serviceProvider)
+internal sealed class BaselineFactory(IServiceProvider serviceProvider) : IBaselineFactory
{
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,
+ Predicate baselineSelector,
+ [NotNullWhen(true)] out VerificationBaseline? baseline,
[NotNullWhen(true)] out string? path)
{
- baseline = VerificationBaseline.Empty;
+ baseline = null;
path = null;
if (!_fileSystem.Directory.Exists(directory))
@@ -42,9 +49,17 @@ public bool TryCreateBaseline(
{
try
{
- baseline = CreateBaselineFromFilePath(jsonFile);
- path = jsonFile;
- _logger?.LogDebug("Create baseline from file: {JsonFile}", jsonFile);
+ var parsedBaseline = CreateBaselineFromFilePath(jsonFile);
+ if (!baselineSelector(parsedBaseline))
+ {
+ _logger?.LogDebug("Baseline '{JsonFile}' was denied by selector.", jsonFile);
+ continue;
+ }
+
+ baseline = parsedBaseline;
+ path = _fileSystem.Path.GetFullPath(jsonFile);
+
+ _logger?.LogDebug("Create baseline from file '{JsonFile}'", jsonFile);
return true;
}
catch (InvalidBaselineException e)
@@ -54,15 +69,50 @@ public bool TryCreateBaseline(
}
}
+ baseline = null;
path = null;
return false;
}
- public VerificationBaseline CreateBaseline(string filePath)
+ public VerificationBaseline ParseBaseline(string filePath)
{
return CreateBaselineFromFilePath(filePath);
}
+ public VerificationBaseline CreateBaseline(
+ VerificationTarget target,
+ AppBaselineSettings settings,
+ IEnumerable errors)
+ {
+ var baselineTarget = new BaselineVerificationTarget
+ {
+ Engine = target.Engine,
+ Name = target.Name,
+ Version = target.Version,
+ Location = settings.WriteLocations ? MaskUsername(target.Location) : null,
+ IsGame = target.IsGame,
+ };
+
+ return new VerificationBaseline(settings.ReportSettings.MinimumReportSeverity, errors, baselineTarget);
+ }
+
+ private static GameLocations MaskUsername(GameLocations targetLocation)
+ {
+ return new GameLocations(
+ targetLocation.ModPaths.Select(PathUtilities.MaskUsername).ToList(),
+ PathUtilities.MaskUsername(targetLocation.GamePath),
+ targetLocation.FallbackPaths.Select(PathUtilities.MaskUsername).ToList());
+ }
+
+ public async Task WriteBaselineAsync(VerificationBaseline baseline, string filePath)
+ {
+#if NET
+ await
+#endif
+ using var fs = _fileSystem.FileStream.New(filePath, 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 95953f1..791eaae 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;
@@ -8,15 +7,17 @@
using PG.StarWarsGame.Engine;
using System;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Text;
namespace AET.ModVerify.App.Reporting;
-internal sealed class BaselineSelector(ModVerifyAppSettings settings, IServiceProvider services)
+internal sealed class BaselineSelector(AppVerifySettings settings, IServiceProvider services)
{
private readonly ILogger? _logger = services.GetService()?.CreateLogger(typeof(ModVerifyApplication));
- private readonly BaselineFactory _baselineFactory = new(services);
+ private readonly IBaselineFactory _baselineFactory = services.GetRequiredService();
- 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))
@@ -24,7 +25,7 @@ public VerificationBaseline SelectBaseline(VerifyInstallationData installationDa
try
{
usedBaselinePath = baselinePath;
- return _baselineFactory.CreateBaseline(baselinePath!);
+ return _baselineFactory.ParseBaseline(baselinePath);
}
catch (InvalidBaselineException e)
{
@@ -43,20 +44,21 @@ public VerificationBaseline SelectBaseline(VerifyInstallationData installationDa
if (!settings.ReportSettings.SearchBaselineLocally)
{
- _logger?.LogDebug(ModVerifyConstants.ConsoleEventId, "No baseline path specified and local search is not enabled. Using empty baseline.");
+ _logger?.LogDebug(ModVerifyConstants.ConsoleEventId,
+ "No baseline path specified and local search is not enabled. Using empty baseline.");
usedBaselinePath = null;
return VerificationBaseline.Empty;
}
- if (settings.Interactive)
- return FindBaselineInteractive(installationData, out usedBaselinePath);
+ if (settings.IsInteractive)
+ 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, 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,48 +68,52 @@ 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.TryFindBaselineInDirectory(
+ verificationTarget.Location.TargetPath,
+ b => IsBaselineCompatible(b, verificationTarget),
+ out var baseline,
out baselinePath))
{
- if (!_baselineFactory.TryCreateBaseline("./", out baseline, out baselinePath))
+ if (!_baselineFactory.TryFindBaselineInDirectory(
+ Environment.CurrentDirectory,
+ b => IsBaselineCompatible(b, verificationTarget),
+ 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)
- {
- _logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "No local baseline file found.");
- return VerificationBaseline.Empty;
- }
-
+ Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("No baseline found locally.");
- return TryGetDefaultBaseline(installationData.EngineType, out baselinePath);
+ Console.ResetColor();
+ baselinePath = null;
+ TryGetDefaultBaseline(verificationTarget.Engine, out baseline);
+ return baseline ?? VerificationBaseline.Empty;
}
}
- Debug.Assert(baselinePath is not null);
+ Debug.Assert(baselinePath is not null && baseline is not null);
- return ConsoleUtilities.UserYesNoQuestion($"ModVerify found the baseline file '{baselinePath}'. Do you want to use it?")
- ? baseline
+ return ShouldUseBaseline(baseline, baselinePath)
+ ? baseline
: VerificationBaseline.Empty;
}
- private VerificationBaseline TryGetDefaultBaseline(GameEngineType engineType, out string? baselinePath)
+ private static bool TryGetDefaultBaseline(
+ GameEngineType engineType,
+ [NotNullWhen(true)] out VerificationBaseline? baseline)
{
- baselinePath = null;
+ baseline = null;
if (engineType == GameEngineType.Eaw)
{
// TODO: EAW currently not implemented
- return VerificationBaseline.Empty;
+ return false;
}
if (!ConsoleUtilities.UserYesNoQuestion($"Do you want to load the default baseline for game engine '{engineType}'?"))
- return VerificationBaseline.Empty;
-
- baselinePath = $"{engineType} (Default)";
+ return false;
try
{
- return LoadEmbeddedBaseline(engineType);
+ baseline = LoadEmbeddedBaseline(engineType);
+ return true;
}
catch (InvalidBaselineException)
{
@@ -116,7 +122,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}";
@@ -125,15 +131,39 @@ internal VerificationBaseline LoadEmbeddedBaseline(GameEngineType engineType)
return VerificationBaseline.FromJson(baselineStream);
}
- private VerificationBaseline FindBaselineNonInteractive(string targetPath, out string? usedPath)
+ private VerificationBaseline FindBaselineNonInteractive(VerificationTarget target, out string? usedPath)
{
- if (_baselineFactory.TryCreateBaseline(targetPath, out var baseline, out usedPath))
+ if (_baselineFactory.TryFindBaselineInDirectory(
+ target.Location.TargetPath,
+ b => IsBaselineCompatible(b, target),
+ out var baseline,
+ out usedPath))
{
_logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Automatically applying local baseline file '{Path}'.", usedPath);
return baseline;
}
- _logger?.LogTrace("No baseline file found in taget path '{TargetPath}'.", targetPath);
+ _logger?.LogTrace("No baseline file found in taget path '{TargetPath}'.", target.Location.TargetPath);
usedPath = null;
return VerificationBaseline.Empty;
}
+
+
+ private static bool IsBaselineCompatible(VerificationBaseline baseline, VerificationTarget target)
+ {
+ return baseline.Target?.Engine == target.Engine;
+ }
+
+ private static bool ShouldUseBaseline(VerificationBaseline baseline, string baselinePath)
+ {
+ var sb = new StringBuilder("Found baseline ");
+ if (baseline.Target is not null)
+ sb.Append($"for '{baseline.Target.Name}' ");
+
+ sb.Append($"at '{baselinePath}'.");
+
+ Console.ForegroundColor = ConsoleColor.Cyan;
+ Console.WriteLine(sb.ToString());
+
+ return ConsoleUtilities.UserYesNoQuestion("Do you want to use it?");
+ }
}
\ No newline at end of file
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
diff --git a/src/ModVerify.CliApp/Reporting/IBaselineFactory.cs b/src/ModVerify.CliApp/Reporting/IBaselineFactory.cs
new file mode 100644
index 0000000..721a7ce
--- /dev/null
+++ b/src/ModVerify.CliApp/Reporting/IBaselineFactory.cs
@@ -0,0 +1,26 @@
+using AET.ModVerify.App.Settings;
+using AET.ModVerify.Reporting;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading.Tasks;
+
+namespace AET.ModVerify.App.Reporting;
+
+internal interface IBaselineFactory
+{
+ bool TryFindBaselineInDirectory(
+ string directory,
+ Predicate baselineSelector,
+ [NotNullWhen(true)] out VerificationBaseline? baseline,
+ [NotNullWhen(true)] out string? path);
+
+ VerificationBaseline ParseBaseline(string filePath);
+
+ Task WriteBaselineAsync(VerificationBaseline baseline, string filePath);
+
+ VerificationBaseline CreateBaseline(
+ VerificationTarget target,
+ AppBaselineSettings settings,
+ IEnumerable errors);
+}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs b/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs
index 4700457..b2ce170 100644
--- a/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs
+++ b/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs
@@ -1,5 +1,6 @@
using System;
using System.Threading;
+using AET.ModVerify.App.Settings;
using AET.ModVerify.Pipeline.Progress;
using AnakinRaW.CommonUtilities;
using AnakinRaW.CommonUtilities.SimplePipeline.Progress;
@@ -7,7 +8,8 @@
namespace AET.ModVerify.App.Reporting;
-public sealed class VerifyConsoleProgressReporter(string toVerifyName) : DisposableObject, IVerifyProgressReporter
+public sealed class VerifyConsoleProgressReporter(string toVerifyName, AppReportSettings reportSettings)
+ : DisposableObject, IVerifyProgressReporter
{
private static readonly ProgressBarOptions ProgressBarOptions = new()
{
@@ -17,6 +19,7 @@ public sealed class VerifyConsoleProgressReporter(string toVerifyName) : Disposa
WriteQueuedMessage = WriteQueuedMessage,
};
+ private readonly bool _verbose = reportSettings.Verbose;
private ProgressBar? _progressBar;
public void ReportError(string message, string? errorLine)
@@ -38,8 +41,8 @@ public void Report(double progress, string? progressText, ProgressType type, Ver
var progressBar = EnsureProgressBar();
- // TODO: Only recognize detailed mode
- progressBar.Message = progressText;
+ if (detailedProgress.IsDetailed)
+ progressBar.Message = progressText;
if (progress >= 1.0)
progressBar.Message = $"Verified '{toVerifyName}'";
@@ -47,8 +50,8 @@ public void Report(double progress, string? progressText, ProgressType type, Ver
var cpb = progressBar.AsProgress();
cpb.Report(progress);
- // TODO: Only in verbose mode
- //progressBar.WriteLine(progressText);
+ if (_verbose)
+ progressBar.WriteLine(progressText);
}
protected override void DisposeResources()
diff --git a/src/ModVerify.CliApp/Resources/Baselines/baseline-foc.json b/src/ModVerify.CliApp/Resources/Baselines/baseline-foc.json
index c94d121..ce70f8a 100644
--- a/src/ModVerify.CliApp/Resources/Baselines/baseline-foc.json
+++ b/src/ModVerify.CliApp/Resources/Baselines/baseline-foc.json
@@ -1,5 +1,11 @@
{
- "version": "2.0",
+ "version": "2.1",
+ "target": {
+ "name": "Forces of Corruption (SteamGold)",
+ "engine": "Foc",
+ "isGame": true,
+ "version": "1.121.13.7360"
+ },
"minSeverity": "Information",
"errors": [
{
@@ -36,12 +42,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 +53,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 +80,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 +91,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 +102,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 +114,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 +127,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 +140,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 +162,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 +173,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 +186,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 +199,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 +223,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 +234,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 +245,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 +258,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 +271,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 +284,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 +297,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 +332,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 +356,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 +367,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 +380,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 +393,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 +404,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 +415,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 +426,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 +437,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 +448,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 +461,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 +474,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 +485,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 +509,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 +561,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 +574,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 +585,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 +598,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 +611,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 +624,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 +635,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 +648,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 +707,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 +718,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 +729,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 +753,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 +764,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 +786,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 +799,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 +812,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 +823,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 +862,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 +873,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 +886,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 +899,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 +910,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 +923,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 +934,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 +956,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 +972,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 +983,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 +994,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 +1005,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 +1016,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 +1027,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 +1052,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 +1065,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 +1076,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 +1089,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 +1100,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 +1111,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 +1124,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 +1137,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 +1150,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 +1411,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 +1543,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.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs b/src/ModVerify.CliApp/Settings/CommandLine/BaseModVerifyOptions.cs
index 0e5e203..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. " +
@@ -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/CreateBaselineVerbOption.cs b/src/ModVerify.CliApp/Settings/CommandLine/CreateBaselineVerbOption.cs
index dc60a73..6593245 100644
--- a/src/ModVerify.CliApp/Settings/CommandLine/CreateBaselineVerbOption.cs
+++ b/src/ModVerify.CliApp/Settings/CommandLine/CreateBaselineVerbOption.cs
@@ -7,4 +7,7 @@ internal sealed class CreateBaselineVerbOption : BaseModVerifyOptions
{
[Option('o', "outFile", Required = true, HelpText = "The file path of the new baseline file.")]
public required string OutputFile { get; init; }
+
+ [Option("skipLocation", Required = false, HelpText = "Skips writing the target location to the baseline.")]
+ public bool SkipLocation { get; init; }
}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs b/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs
index 97f1536..e3be836 100644
--- a/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs
+++ b/src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs
@@ -16,17 +16,19 @@ internal sealed class VerifyVerbOption : BaseModVerifyOptions
public string? OutputDirectory { get; init; }
[Option("failFast", Required = false, Default = false,
- HelpText = "When set, the application will abort on the first failure. The option also recognized the 'MinimumFailureSeverity' setting.")]
+ HelpText = "When set, the application will abort on the first failure. " +
+ "The option requires 'minFailSeverity' to be set.")]
public bool FailFast { get; init; }
[Option("minFailSeverity", Required = false, Default = null,
- HelpText = "When set, the application return with an error, if any finding has at least the specified severity value.")]
+ 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,
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; }
diff --git a/src/ModVerify.CliApp/Settings/CommandLineHelper.cs b/src/ModVerify.CliApp/Settings/CommandLineHelper.cs
new file mode 100644
index 0000000..f799591
--- /dev/null
+++ b/src/ModVerify.CliApp/Settings/CommandLineHelper.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using CommandLine;
+
+namespace AET.ModVerify.App.Settings;
+
+internal static class CommandLineHelper
+{
+ public static string GetOptionName(this Type type, string optionPropertyName)
+ {
+ var property = type.GetProperties().FirstOrDefault(p => p.Name.Equals(optionPropertyName));
+ var optionAttribute = property?.GetCustomAttribute();
+ return optionAttribute is null
+ ? throw new InvalidOperationException($"Unable to get option data for {type}:{optionAttribute}")
+ : $"--{optionAttribute.LongName}";
+ }
+}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs b/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs
deleted file mode 100644
index 00bc4f0..0000000
--- a/src/ModVerify.CliApp/Settings/GameInstallationsSettings.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using PG.StarWarsGame.Engine;
-
-namespace AET.ModVerify.App.Settings;
-
-internal sealed record GameInstallationsSettings
-{
- public bool Interactive => string.IsNullOrEmpty(AutoPath) && ModPaths.Count == 0 && string.IsNullOrEmpty(GamePath);
-
- [MemberNotNullWhen(true, nameof(AutoPath))]
- public bool UseAutoDetection => !string.IsNullOrEmpty(AutoPath);
-
- [MemberNotNullWhen(true, nameof(GamePath))]
- public bool ManualSetup => !string.IsNullOrEmpty(GamePath);
-
- public string? AutoPath { get; init; }
-
- public IList ModPaths { get; init; } = [];
-
- public string? GamePath { get; init; }
-
- public string? FallbackGamePath { get; init; }
-
- public IList AdditionalFallbackPaths { get; init; } = [];
-
- public GameEngineType? EngineType { get; init; }
-}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs b/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs
index 3376354..e4488a0 100644
--- a/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs
+++ b/src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs
@@ -1,23 +1,77 @@
-using System.Diagnostics.CodeAnalysis;
+using System;
using AET.ModVerify.Reporting;
using AET.ModVerify.Settings;
namespace AET.ModVerify.App.Settings;
-internal sealed class ModVerifyAppSettings
+public class AppReportSettings
{
- public bool Interactive => GameInstallationsSettings.Interactive;
+ public VerificationSeverity MinimumReportSeverity { get; init; }
+
+ public string? SuppressionsPath { get; init; }
- public required VerifyPipelineSettings VerifyPipelineSettings { get; init; }
+ public bool Verbose { get; init; }
+}
- public required ModVerifyReportSettings ReportSettings { get; init; }
+public sealed class VerifyReportSettings : AppReportSettings
+{
+ public string? BaselinePath { get; init; }
+ public bool SearchBaselineLocally { get; init; }
+}
+
+internal abstract class AppSettingsBase(AppReportSettings reportSettings)
+{
+ public bool IsInteractive => VerificationTargetSettings.Interactive;
+
+ public required VerificationTargetSettings VerificationTargetSettings
+ {
+ get;
+ init => field = value ?? throw new ArgumentNullException(nameof(value));
+ }
+
+ public required VerifyPipelineSettings VerifyPipelineSettings
+ {
+ get;
+ init => field = value ?? throw new ArgumentNullException(nameof(value));
+ }
- public required GameInstallationsSettings GameInstallationsSettings { get; init; }
+ public AppReportSettings ReportSettings { get; } = reportSettings ?? throw new ArgumentNullException(nameof(reportSettings));
+}
- public VerificationSeverity? AppThrowsOnMinimumSeverity { get; init; }
+internal abstract class AppSettingsBase(T reportSettings) : AppSettingsBase(reportSettings)
+ where T : AppReportSettings
+{
+ public new T ReportSettings { get; } = reportSettings ?? throw new ArgumentNullException(nameof(reportSettings));
+}
+
+internal sealed class AppVerifySettings(VerifyReportSettings reportSettings) : AppSettingsBase(reportSettings)
+{
+ public VerificationSeverity? AppFailsOnMinimumSeverity { get; init; }
- [MemberNotNullWhen(true, nameof(NewBaselinePath))]
- public bool CreateNewBaseline => !string.IsNullOrEmpty(NewBaselinePath);
+ public required string ReportDirectory
+ {
+ get;
+ init
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ throw new ArgumentNullException(nameof(value));
+ field = value;
+ }
+ }
+}
+
+internal sealed class AppBaselineSettings(AppReportSettings reportSettings) : AppSettingsBase(reportSettings)
+{
+ public required string NewBaselinePath
+ {
+ get;
+ init
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ throw new ArgumentNullException(nameof(value));
+ field = value;
+ }
+ }
- public string? NewBaselinePath { get; init; }
+ public bool WriteLocations { get; init; } = true;
}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/Settings/ModVerifyReportSettings.cs b/src/ModVerify.CliApp/Settings/ModVerifyReportSettings.cs
deleted file mode 100644
index 482b844..0000000
--- a/src/ModVerify.CliApp/Settings/ModVerifyReportSettings.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using AET.ModVerify.Reporting;
-
-namespace AET.ModVerify.App.Settings;
-
-internal sealed class ModVerifyReportSettings
-{
- public VerificationSeverity MinimumReportSeverity { get; init; }
-
- public string? SuppressionsPath { get; init; }
-
- public string? BaselinePath { get; init; }
-
- public bool SearchBaselineLocally { get; init; }
-}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs
index 3c5535f..23a89ff 100644
--- a/src/ModVerify.CliApp/Settings/SettingsBuilder.cs
+++ b/src/ModVerify.CliApp/Settings/SettingsBuilder.cs
@@ -1,22 +1,18 @@
-using System;
-using System.Collections.Generic;
-using System.IO.Abstractions;
-using AET.ModVerify.App.Settings.CommandLine;
-using AET.ModVerify.App.Utilities;
+using AET.ModVerify.App.Settings.CommandLine;
using AET.ModVerify.Pipeline;
-using AET.ModVerify.Reporting;
using AET.ModVerify.Settings;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO.Abstractions;
namespace AET.ModVerify.App.Settings;
internal sealed class SettingsBuilder(IServiceProvider serviceProvider)
{
- private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(SettingsBuilder));
private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService();
- public ModVerifyAppSettings BuildSettings(BaseModVerifyOptions options)
+ public AppSettingsBase BuildSettings(BaseModVerifyOptions options)
{
switch (options)
{
@@ -28,90 +24,103 @@ public ModVerifyAppSettings BuildSettings(BaseModVerifyOptions options)
throw new NotSupportedException($"The option '{options.GetType().Name}' is not supported!");
}
- private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions)
+ private AppVerifySettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions)
{
- return new ModVerifyAppSettings
+ ValidateVerb();
+ var failFastSetting = GetFailFastSetting();
+ return new AppVerifySettings(BuildReportSettings())
{
+ ReportDirectory = GetReportDirectory(),
VerifyPipelineSettings = new VerifyPipelineSettings
{
ParallelVerifiers = verifyOptions.Parallel ? 4 : 1,
VerifiersProvider = new DefaultGameVerifiersProvider(),
- FailFast = verifyOptions.FailFast,
+ FailFastSettings = failFastSetting,
GameVerifySettings = new GameVerifySettings
{
IgnoreAsserts = verifyOptions.IgnoreAsserts,
- ThrowsOnMinimumSeverity = GetVerifierMinimumThrowSeverity()
+ ThrowsOnMinimumSeverity = failFastSetting.IsFailFast
+ ? failFastSetting.MinumumSeverity
+ // The app shall not make a specific verifier throw, but it should always run to completion.
+ : null
}
},
- AppThrowsOnMinimumSeverity = verifyOptions.MinimumFailureSeverity,
- GameInstallationsSettings = BuildInstallationSettings(verifyOptions),
- ReportSettings = BuildReportSettings(verifyOptions),
+ AppFailsOnMinimumSeverity = verifyOptions.MinimumFailureSeverity,
+ VerificationTargetSettings = BuildTargetSettings(verifyOptions),
};
- VerificationSeverity? GetVerifierMinimumThrowSeverity()
+ void ValidateVerb()
{
- var minFailSeverity = verifyOptions.MinimumFailureSeverity;
- if (verifyOptions.FailFast)
+ if (verifyOptions.SearchBaselineLocally && !string.IsNullOrEmpty(verifyOptions.Baseline))
{
- if (minFailSeverity == null)
- {
- _logger?.LogWarning(ModVerifyConstants.ConsoleEventId,
- "Verification is configured to fail fast but 'minFailSeverity' is not specified. Using severity '{Info}'.", VerificationSeverity.Information);
- minFailSeverity = VerificationSeverity.Information;
- }
+ var searchOption = typeof(VerifyVerbOption).GetOptionName(nameof(VerifyVerbOption.SearchBaselineLocally));
+ var baselineOption = typeof(VerifyVerbOption).GetOptionName(nameof(VerifyVerbOption.Baseline));
+ throw new AppArgumentException($"Options {searchOption} and {baselineOption} cannot be used together.");
+ }
- return minFailSeverity;
+ if (verifyOptions is { FailFast: true, MinimumFailureSeverity: null })
+ {
+ var failFast = typeof(VerifyVerbOption).GetOptionName(nameof(VerifyVerbOption.FailFast));
+ var minThrowSeverity = typeof(VerifyVerbOption).GetOptionName(nameof(VerifyVerbOption.MinimumFailureSeverity));
+ throw new AppArgumentException($"Option {failFast} requires to set {minThrowSeverity}.");
}
+ }
+
+ FailFastSetting GetFailFastSetting()
+ {
+ return !verifyOptions.FailFast
+ ? FailFastSetting.NoFailFast
+ : new FailFastSetting(verifyOptions.MinimumFailureSeverity!.Value);
+ }
+
+ string GetReportDirectory()
+ {
+ return _fileSystem.Path.GetFullPath(_fileSystem.Path.Combine(
+ Environment.CurrentDirectory,
+ verifyOptions.OutputDirectory ?? "ModVerifyResults"));
+ }
- // Only in a failFast scenario we want the verifier to throw.
- // In a normal run, the verifier should simply store the error.
- return null;
+ VerifyReportSettings BuildReportSettings()
+ {
+ return new VerifyReportSettings
+ {
+ BaselinePath = verifyOptions.Baseline,
+ MinimumReportSeverity = verifyOptions.MinimumSeverity,
+ SearchBaselineLocally = verifyOptions.SearchBaselineLocally,
+ SuppressionsPath = verifyOptions.Suppressions,
+ Verbose = verifyOptions.Verbose
+ };
}
}
- private ModVerifyAppSettings BuildFromCreateBaselineVerb(CreateBaselineVerbOption baselineVerb)
+ private AppBaselineSettings BuildFromCreateBaselineVerb(CreateBaselineVerbOption baselineVerb)
{
- return new ModVerifyAppSettings
+ return new AppBaselineSettings(BuildReportSettings())
{
VerifyPipelineSettings = new VerifyPipelineSettings
{
ParallelVerifiers = baselineVerb.Parallel ? 4 : 1,
- GameVerifySettings = new GameVerifySettings
- {
- IgnoreAsserts = false,
- ThrowsOnMinimumSeverity = null,
- },
VerifiersProvider = new DefaultGameVerifiersProvider(),
- FailFast = false,
+ GameVerifySettings = GameVerifySettings.Default,
+ FailFastSettings = FailFastSetting.NoFailFast,
},
- AppThrowsOnMinimumSeverity = null,
- GameInstallationsSettings = BuildInstallationSettings(baselineVerb),
- ReportSettings = BuildReportSettings(baselineVerb),
+ VerificationTargetSettings = BuildTargetSettings(baselineVerb),
NewBaselinePath = baselineVerb.OutputFile,
+ WriteLocations = !baselineVerb.SkipLocation
};
- }
- private static ModVerifyReportSettings BuildReportSettings(BaseModVerifyOptions options)
- {
- var baselinePath = (options as VerifyVerbOption)?.Baseline;
-
- return new ModVerifyReportSettings
- {
- BaselinePath = baselinePath,
- MinimumReportSeverity = options.MinimumSeverity,
- SearchBaselineLocally = SearchLocally(options),
- SuppressionsPath = options.Suppressions
- };
-
- static bool SearchLocally(BaseModVerifyOptions o)
+ AppReportSettings BuildReportSettings()
{
- if (o is not VerifyVerbOption v)
- return false;
- return v.SearchBaselineLocally || v.LaunchedWithoutArguments();
+ return new AppReportSettings
+ {
+ MinimumReportSeverity = baselineVerb.MinimumSeverity,
+ SuppressionsPath = baselineVerb.Suppressions,
+ Verbose = baselineVerb.Verbose
+ };
}
}
- private GameInstallationsSettings BuildInstallationSettings(BaseModVerifyOptions options)
+ private VerificationTargetSettings BuildTargetSettings(BaseModVerifyOptions options)
{
var modPaths = new List();
if (options.ModPaths is not null)
@@ -142,18 +151,18 @@ 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,
AdditionalFallbackPaths = fallbackPaths,
- EngineType = options.GameType
+ Engine = options.Engine
};
}
}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/Settings/VerificationTargetSettings.cs b/src/ModVerify.CliApp/Settings/VerificationTargetSettings.cs
new file mode 100644
index 0000000..b5e1a40
--- /dev/null
+++ b/src/ModVerify.CliApp/Settings/VerificationTargetSettings.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using PG.StarWarsGame.Engine;
+
+namespace AET.ModVerify.App.Settings;
+
+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) && ModPaths.Count == 0 && string.IsNullOrEmpty(GamePath) && string.IsNullOrEmpty(FallbackGamePath);
+
+ [MemberNotNullWhen(true, nameof(GamePath))]
+ public bool ManualSetup => !string.IsNullOrEmpty(GamePath);
+
+ public string? TargetPath { get; init; }
+
+ public IReadOnlyList ModPaths { get; init; } = [];
+
+ public string? GamePath { get; init; }
+
+ public string? FallbackGamePath { get; init; }
+
+ public IReadOnlyList AdditionalFallbackPaths { get; init; } = [];
+
+ public GameEngineType? Engine { get; init; }
+}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs b/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs
new file mode 100644
index 0000000..5250ec4
--- /dev/null
+++ b/src/ModVerify.CliApp/TargetSelectors/AutomaticSelector.cs
@@ -0,0 +1,163 @@
+using AET.ModVerify.App.GameFinder;
+using AET.ModVerify.App.Settings;
+using AET.ModVerify.App.Utilities;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using PG.StarWarsGame.Engine;
+using PG.StarWarsGame.Infrastructure;
+using PG.StarWarsGame.Infrastructure.Games;
+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;
+using AnakinRaW.CommonUtilities.FileSystem;
+
+namespace AET.ModVerify.App.TargetSelectors;
+
+internal class AutomaticSelector(IServiceProvider serviceProvider) : VerificationTargetSelectorBase(serviceProvider)
+{
+ private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService();
+
+ internal override SelectionResult SelectTarget(VerificationTargetSettings settings)
+ {
+ if (!settings.UseAutoDetection)
+ throw new ArgumentException("wrong settings format provided.", nameof(settings));
+
+ var targetPath = settings.TargetPath;
+ 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
+ {
+ 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.", targetPath);
+ throw;
+ }
+
+ GameLocations locations;
+
+ var targetObject = GetAttachedModOrGame(finderResult, targetPath, engine);
+
+ if (targetObject is not null)
+ {
+ 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, engine.Value, settings.AdditionalFallbackPaths, out var mod);
+ targetObject = mod;
+ }
+
+ return new(locations, engine.Value, targetObject);
+ }
+
+ 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))
+ target = finderResult.Game;
+
+ target ??= GetMatchingModFromGame(finderResult.Game, targetFullPath, requestedEngineType) ??
+ GetMatchingModFromGame(finderResult.FallbackGame, targetFullPath, requestedEngineType);
+
+ return target;
+ }
+
+ private GameLocations GetDetachedModLocations(
+ string modPath,
+ GameFinderResult gameResult,
+ GameEngineType requestedGameEngine,
+ IReadOnlyList additionalFallbackPaths,
+ out IPhysicalMod mod)
+ {
+ // Because requestedGameEngine must be set, GameFinderService already ensures
+ // gameResult.Game is the correct type.
+ var game = gameResult.Game;
+ var modFinder = ServiceProvider.GetRequiredService();
+ var modRef = modFinder.FindMods(game, _fileSystem.DirectoryInfo.New(modPath)).FirstOrDefault();
+
+ if (modRef is null)
+ ThrowEngineNotSupported(requestedGameEngine, modPath);
+
+ var modFactory = ServiceProvider.GetRequiredService();
+ mod = modFactory.CreatePhysicalMod(game, modRef, CultureInfo.InvariantCulture);
+
+ game.AddMod(mod);
+
+ mod.ResolveDependencies();
+
+ return GetLocations(mod, gameResult.FallbackGame, additionalFallbackPaths);
+ }
+
+ private IPhysicalMod? GetMatchingModFromGame(IGame? game, string modPath, GameEngineType? requestedEngineType)
+ {
+ if (game is null || !IsEngineTypeSupported(requestedEngineType, game.Type))
+ return null;
+
+ foreach (var mod in game.Game.Mods)
+ {
+ if (mod is not IPhysicalMod physicalMod)
+ continue;
+
+ if (_fileSystem.Path.AreEqual(modPath, physicalMod.Directory.FullName))
+ return physicalMod;
+ }
+
+ return null;
+ }
+
+ private static IGame? GetTargetGame(GameFinderResult finderResult, GameEngineType? requestedEngine)
+ {
+ 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)
+ {
+ 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/ModSelectors/ConsoleModSelector.cs b/src/ModVerify.CliApp/TargetSelectors/ConsoleSelector.cs
similarity index 79%
rename from src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs
rename to src/ModVerify.CliApp/TargetSelectors/ConsoleSelector.cs
index c776d6d..17b9791 100644
--- a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs
+++ b/src/ModVerify.CliApp/TargetSelectors/ConsoleSelector.cs
@@ -5,22 +5,21 @@
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;
+namespace AET.ModVerify.App.TargetSelectors;
-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)
+ internal override SelectionResult SelectTarget(VerificationTargetSettings 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 locations = GetLocations(targetObject, gameResult.FallbackGame, settings.AdditionalFallbackPaths);
+ return new SelectionResult(locations, engine, targetObject);
}
private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult finderResult)
@@ -31,6 +30,9 @@ private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult fin
list.Add(finderResult.Game);
Console.WriteLine();
+ Console.ForegroundColor = ConsoleColor.Cyan;
+ Console.WriteLine("Listing Games and Mods:");
+ Console.ResetColor();
ConsoleUtilities.WriteHorizontalLine();
Console.WriteLine($"0: {game.Name}");
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/TargetSelectors/ManualSelector.cs b/src/ModVerify.CliApp/TargetSelectors/ManualSelector.cs
new file mode 100644
index 0000000..f963b4f
--- /dev/null
+++ b/src/ModVerify.CliApp/TargetSelectors/ManualSelector.cs
@@ -0,0 +1,73 @@
+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.TargetSelectors;
+
+internal class ManualSelector(IServiceProvider serviceProvider) : VerificationTargetSelectorBase(serviceProvider)
+{
+ internal override SelectionResult SelectTarget(VerificationTargetSettings 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());
+
+
+ // 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.
+ 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))
+ {
+
+ 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)
+ {
+ if (game is null)
+ return null;
+ 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/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/src/ModVerify.CliApp/TargetSelectors/VerificationTargetSelectorBase.cs b/src/ModVerify.CliApp/TargetSelectors/VerificationTargetSelectorBase.cs
new file mode 100644
index 0000000..68f0f39
--- /dev/null
+++ b/src/ModVerify.CliApp/TargetSelectors/VerificationTargetSelectorBase.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Collections.Generic;
+using System.IO.Abstractions;
+using System.Linq;
+using System.Runtime.InteropServices;
+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.Clients;
+using PG.StarWarsGame.Infrastructure.Clients.Utilities;
+using PG.StarWarsGame.Infrastructure.Games;
+using PG.StarWarsGame.Infrastructure.Mods;
+using PG.StarWarsGame.Infrastructure.Services.Dependencies;
+
+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;
+ 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 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,
+ 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(IPhysicalPlayableObject 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(IPhysicalPlayableObject? 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 string? GetTargetVersion(IPhysicalPlayableObject? targetObject)
+ {
+ return targetObject switch
+ {
+ IMod mod => mod.Version?.ToString(),
+ IGame game => GetVersionFromGame(game),
+ _ => null
+ };
+ }
+
+ private string? GetVersionFromGame(IGame game)
+ {
+ var exeFile = GameExecutableFileUtilities.GetExecutableForGame(game, GameBuildType.Release);
+ if (exeFile is null)
+ {
+ Logger?.LogWarning(ModVerifyConstants.ConsoleEventId,
+ "Unable to get game version of target path '{Path}'. Is this a game directory?",
+ game.Directory.FullName);
+ return null;
+ }
+
+ var versionInfo = FileSystem.FileVersionInfo.GetVersionInfo(exeFile.FullName);
+ var version =
+ $"{versionInfo.FileMajorPart}.{versionInfo.FileMinorPart}.{versionInfo.FileBuildPart}.{versionInfo.FilePrivatePart}";
+ return version;
+ }
+}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/TargetSelectors/VerificationTargetSelectorFactory.cs b/src/ModVerify.CliApp/TargetSelectors/VerificationTargetSelectorFactory.cs
new file mode 100644
index 0000000..5e106eb
--- /dev/null
+++ b/src/ModVerify.CliApp/TargetSelectors/VerificationTargetSelectorFactory.cs
@@ -0,0 +1,18 @@
+using System;
+using AET.ModVerify.App.Settings;
+
+namespace AET.ModVerify.App.TargetSelectors;
+
+internal sealed class VerificationTargetSelectorFactory(IServiceProvider serviceProvider)
+{
+ public IVerificationTargetSelector CreateSelector(VerificationTargetSettings 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/Utilities/ExtensionMethods.cs b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs
index b2e7ab0..fc07469 100644
--- a/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs
+++ b/src/ModVerify.CliApp/Utilities/ExtensionMethods.cs
@@ -8,16 +8,23 @@ namespace AET.ModVerify.App.Utilities;
internal static class ExtensionMethods
{
- public static GameEngineType ToEngineType(this GameType type)
+ extension(GameEngineType type)
{
- return type == GameType.Foc ? GameEngineType.Foc : GameEngineType.Eaw;
+ public GameType FromEngineType()
+ {
+ return (GameType)(int)type;
+ }
+
+ public GameEngineType Opposite()
+ {
+ return (GameEngineType)((int)type ^ 1);
+ }
}
- public static GameType FromEngineType(this GameEngineType type)
+ public static GameEngineType ToEngineType(this GameType type)
{
- return type == GameEngineType.Foc ? GameType.Foc : GameType.Eaw;
+ return (GameEngineType)(int)type;
}
-
extension(ApplicationEnvironment modVerifyEnvironment)
{
public bool IsUpdatable()
diff --git a/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs b/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs
index 6e6664a..b9ebff0 100644
--- a/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs
+++ b/src/ModVerify.CliApp/Utilities/ModVerifyConsoleUtilities.cs
@@ -1,6 +1,8 @@
using AnakinRaW.ApplicationBase;
using Figgle;
using System;
+using System.Collections.Generic;
+using AET.ModVerify.Reporting;
namespace AET.ModVerify.App.Utilities;
@@ -21,10 +23,68 @@ public static void WriteHeader(string? version = null)
Console.ResetColor();
Console.WriteLine();
}
+
ConsoleUtilities.WriteHorizontalLine('*', lineLength);
ConsoleUtilities.WriteLineRight(author, lineLength);
Console.WriteLine();
Console.WriteLine();
}
+
+ public static void WriteSelectedTarget(VerificationTarget target)
+ {
+ Console.ForegroundColor = ConsoleColor.Cyan;
+ Console.WriteLine("Selected Target:");
+ Console.ForegroundColor = ConsoleColor.DarkGray;
+ ConsoleUtilities.PrintAsTable([
+ ("Name", target.Name),
+ ("Type", target.IsGame ? "Game" : "Mod"),
+ ("Engine", target.Engine),
+ ("Version", target.Version ?? "n/a"),
+ ("Location", target.Location.TargetPath),
+ ], 120);
+ Console.ResetColor();
+ }
+
+ public static void WriteBaselineInfo(VerificationBaseline baseline, string? filePath)
+ {
+ if (baseline.IsEmpty)
+ return;
+
+ Console.ForegroundColor = ConsoleColor.Cyan;
+ Console.WriteLine("Using Baseline:");
+ Console.ForegroundColor = ConsoleColor.DarkGray;
+
+ IList<(string, object)> baselineData =
+ [
+ ("Version", baseline.Version?.ToString(2) ?? "n/a"),
+ ("Is Default", filePath is null),
+ ("Minimum Severity", baseline.MinimumSeverity.ToString()),
+ ("Entries", baseline.Count.ToString())
+ ];
+ if (!string.IsNullOrEmpty(filePath))
+ baselineData.Add(("File Path", filePath));
+
+ ConsoleUtilities.PrintAsTable(baselineData, 120);
+
+ if (baseline.Target is not null)
+ {
+ Console.ForegroundColor = ConsoleColor.DarkMagenta;
+ Console.WriteLine("Baseline Target:");
+ Console.ForegroundColor = ConsoleColor.DarkGray;
+
+ IList<(string, object)> targetData = [
+ ("Name", baseline.Target.Name),
+ ("Type", baseline.Target.IsGame ? "Game" : "Mod"),
+ ("Engine", baseline.Target.Engine),
+ ("Version", baseline.Target.Version ?? "n/a"),
+ ];
+
+ if (baseline.Target.Location is not null)
+ targetData.Add(("Location", baseline.Target.Location.TargetPath));
+
+ ConsoleUtilities.PrintAsTable(targetData, 120);
+ }
+ Console.ResetColor();
+ }
}
\ No newline at end of file
diff --git a/src/ModVerify.CliApp/Utilities/PathUtilities.cs b/src/ModVerify.CliApp/Utilities/PathUtilities.cs
new file mode 100644
index 0000000..561630f
--- /dev/null
+++ b/src/ModVerify.CliApp/Utilities/PathUtilities.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Runtime.InteropServices;
+using AnakinRaW.CommonUtilities.FileSystem.Normalization;
+
+namespace AET.ModVerify.App.Utilities;
+
+internal static class PathUtilities
+{
+ private static readonly string HomeVariable;
+ private static readonly string HomePath;
+ private static readonly StringComparison StringComparer;
+
+ static PathUtilities()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ HomeVariable = "%USERPROFILE%";
+ StringComparer = StringComparison.OrdinalIgnoreCase;
+ }
+ else
+ {
+ HomeVariable = "$HOME";
+ StringComparer = StringComparison.Ordinal;
+ }
+
+ HomePath = PathNormalizer.Normalize(
+ Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ PathNormalizeOptions.EnsureTrailingSeparator);
+ }
+
+ internal static string MaskUsername(string path)
+ {
+ if (string.IsNullOrWhiteSpace(path))
+ return path;
+
+ var index = path.IndexOf(HomePath, StringComparer);
+ return index >= 0 ? path.Remove(index, HomePath.Length).Insert(index, HomeVariable) : path;
+ }
+}
\ No newline at end of file
diff --git a/src/ModVerify/GameVerificationException.cs b/src/ModVerify/GameVerificationException.cs
index 1096179..dba312b 100644
--- a/src/ModVerify/GameVerificationException.cs
+++ b/src/ModVerify/GameVerificationException.cs
@@ -7,23 +7,21 @@ namespace AET.ModVerify;
public sealed class GameVerificationException : Exception
{
- private readonly string? _errorMessage = null;
-
public IReadOnlyCollection Errors { get; }
private string ErrorMessage
{
get
{
- if (_errorMessage != null)
- return _errorMessage;
+ if (field != null)
+ return field;
var stringBuilder = new StringBuilder();
foreach (var error in Errors)
stringBuilder.AppendLine($"Verification error: {error.Id}: {error.Message};");
return stringBuilder.ToString().TrimEnd(';');
}
- }
+ } = null;
///
public override string Message => ErrorMessage;
diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj
index 427458d..73d45e0 100644
--- a/src/ModVerify/ModVerify.csproj
+++ b/src/ModVerify/ModVerify.csproj
@@ -25,18 +25,18 @@
-
+
-
+
-
-
-
-
+
+
+
+
@@ -51,8 +51,4 @@
-
-
-
-
diff --git a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs
index 6405dbd..e83cb25 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,18 +23,19 @@ public sealed class GameVerifierPipelineStep(
public long Size => 1;
- protected override void RunCore(CancellationToken token)
+ protected override Task RunCoreAsync(CancellationToken token)
{
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"));
+ return Task.CompletedTask;
}
finally
{
diff --git a/src/ModVerify/Pipeline/GameVerifyPipeline.cs b/src/ModVerify/Pipeline/GameVerifyPipeline.cs
index 810651a..493aa8c 100644
--- a/src/ModVerify/Pipeline/GameVerifyPipeline.cs
+++ b/src/ModVerify/Pipeline/GameVerifyPipeline.cs
@@ -1,7 +1,6 @@
-using AET.ModVerify.Reporting;
-using AET.ModVerify.Reporting.Settings;
+using AET.ModVerify.Pipeline.Progress;
+using AET.ModVerify.Reporting;
using AET.ModVerify.Settings;
-using AET.ModVerify.Utilities;
using AET.ModVerify.Verifiers;
using AnakinRaW.CommonUtilities.SimplePipeline;
using AnakinRaW.CommonUtilities.SimplePipeline.Runners;
@@ -12,113 +11,127 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using AET.ModVerify.Pipeline.Progress;
+using AET.ModVerify.Utilities;
+using Microsoft.Extensions.DependencyInjection;
namespace AET.ModVerify.Pipeline;
-public sealed class GameVerifyPipeline : AnakinRaW.CommonUtilities.SimplePipeline.Pipeline
+public sealed class GameVerifyPipeline : StepRunnerPipelineBase
{
- private readonly List _verifiers = new();
- private readonly List _verificationSteps = new();
- private readonly StepRunnerBase _verifyRunner;
-
- private readonly IStarWarsGameEngine _gameEngine;
- private readonly IGameEngineErrorCollection _engineErrors;
+ private readonly List _verifiers = [];
+ private readonly List _verificationSteps = [];
+ private readonly ConcurrentGameEngineErrorReporter _engineErrorReporter = new();
+ private readonly VerificationTarget _verificationTarget;
private readonly VerifyPipelineSettings _pipelineSettings;
- private readonly GlobalVerifyReportSettings _reportSettings;
-
private readonly IVerifyProgressReporter _progressReporter;
-
- protected override bool FailFast { get; }
+ private readonly IGameEngineInitializationReporter? _engineInitializationReporter;
+ private readonly IPetroglyphStarWarsGameEngineService _gameEngineService;
+ private readonly ILogger? _logger;
+ private AggregatedVerifyProgressReporter? _aggregatedVerifyProgressReporter;
public IReadOnlyCollection FilteredErrors { get; private set; } = [];
-
+ public VerificationBaseline Baseline { get; }
+ public SuppressionList Suppressions { get; }
+
public GameVerifyPipeline(
- IStarWarsGameEngine gameEngine,
- IGameEngineErrorCollection engineErrors,
- VerifyPipelineSettings pipelineSettings,
- GlobalVerifyReportSettings reportSettings,
+ VerificationTarget verificationTarget,
+ VerifyPipelineSettings pipelineSettings,
IVerifyProgressReporter progressReporter,
+ IGameEngineInitializationReporter? engineInitializationReporter,
+ VerificationBaseline baseline,
+ SuppressionList suppressions,
IServiceProvider serviceProvider) : base(serviceProvider)
{
- _gameEngine = gameEngine ?? throw new ArgumentNullException(nameof(gameEngine));
- _engineErrors = engineErrors ?? throw new ArgumentNullException(nameof(gameEngine));
+ Baseline = baseline ?? throw new ArgumentNullException(nameof(baseline));
+ Suppressions = suppressions ?? throw new ArgumentNullException(nameof(suppressions));
+ _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);
+ FailFast = pipelineSettings.FailFastSettings.IsFailFast;
+ }
- FailFast = pipelineSettings.FailFast;
+ 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 PrepareCoreAsync()
+ protected override async Task PrepareCoreAsync(CancellationToken token)
{
_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);
+ IStarWarsGameEngine gameEngine;
try
{
- Logger?.LogInformation("Running game verifiers...");
- _verifyRunner.Error += OnError;
- await _verifyRunner.RunAsync(token);
+ gameEngine = await _gameEngineService.InitializeAsync(
+ _verificationTarget.Engine,
+ _verificationTarget.Location,
+ _engineErrorReporter,
+ _engineInitializationReporter,
+ false,
+ CancellationToken.None).ConfigureAwait(false);
}
- finally
+ catch (Exception e)
{
- aggregatedVerifyProgressReporter.Dispose();
- _verifyRunner.Error -= OnError;
- Logger?.LogDebug("Game verifiers finished.");
+ _logger?.LogError(e, "Creating game engine failed: {Message}", e.Message);
+ throw;
}
- token.ThrowIfCancellationRequested();
-
- var failedSteps = _verifyRunner.ExecutedSteps.Where(p =>
- p.Error != null && !p.Error.IsExceptionType()).ToList();
+ AddStep(new GameEngineErrorCollector(_engineErrorReporter, gameEngine, _pipelineSettings.GameVerifySettings, ServiceProvider));
- if (failedSteps.Count != 0)
- throw new StepFailureException(failedSteps);
+ foreach (var gameVerificationStep in CreateVerificationSteps(gameEngine))
+ AddStep(gameVerificationStep);
+ }
+ 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 OnError(object sender, StepRunnerErrorEventArgs e)
+ protected override void OnRunnerExecutionError(object sender, StepRunnerErrorEventArgs e)
{
- if (FailFast && e.Exception is GameVerificationException v)
+ if (FailFast && e.Exception is GameVerificationException verificationException)
{
- // TODO: Apply globalMinSeverity
- if (v.Errors.All(error => _reportSettings.Baseline.Contains(error) || _reportSettings.Suppressions.Suppresses(error)))
+ var minSeverity = _pipelineSettings.FailFastSettings.MinumumSeverity;
+ var ignoreError = verificationException.Errors
+ .Where(error => error.Severity >= minSeverity)
+ .All(error => Baseline.Contains(error) || Suppressions.Suppresses(error));
+ if (ignoreError)
return;
}
- base.OnError(sender, e);
+ base.OnRunnerExecutionError(sender, e);
}
- private IEnumerable CreateVerificationSteps(IStarWarsGameEngine database)
+ protected override IEnumerable GetFailedSteps(IEnumerable steps)
{
- return _pipelineSettings.VerifiersProvider.GetVerifiers(database, _pipelineSettings.GameVerifySettings, ServiceProvider);
+ return base.GetFailedSteps(steps).Where(s => s.Error is not GameVerificationException);
}
private void AddStep(GameVerifier verifier)
{
var verificationStep = new GameVerifierPipelineStep(verifier, ServiceProvider);
- _verifyRunner.AddStep(verificationStep);
+ StepRunner.AddStep(verificationStep);
_verificationSteps.Add(verificationStep);
_verifiers.Add(verifier);
}
@@ -128,7 +141,18 @@ private IEnumerable GetReportableErrors(IEnumerable CreateVerificationSteps(IStarWarsGameEngine engine)
+ {
+ 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/ModVerify/Pipeline/Progress/AggregatedVerifyProgressReporter.cs b/src/ModVerify/Pipeline/Progress/AggregatedVerifyProgressReporter.cs
index 361febe..cfdae25 100644
--- a/src/ModVerify/Pipeline/Progress/AggregatedVerifyProgressReporter.cs
+++ b/src/ModVerify/Pipeline/Progress/AggregatedVerifyProgressReporter.cs
@@ -59,6 +59,7 @@ protected override ProgressEventArgs CalculateAggregatedProg
var progressInfo = new VerifyProgressInfo
{
TotalVerifiers = TotalStepCount,
+ IsDetailed = true
};
return new ProgressEventArgs(totalProgress, progress.ProgressText, progressInfo);
}
diff --git a/src/ModVerify/Pipeline/Progress/VerifyProgressInfo.cs b/src/ModVerify/Pipeline/Progress/VerifyProgressInfo.cs
index 1409239..cadeb0d 100644
--- a/src/ModVerify/Pipeline/Progress/VerifyProgressInfo.cs
+++ b/src/ModVerify/Pipeline/Progress/VerifyProgressInfo.cs
@@ -4,5 +4,5 @@ public struct VerifyProgressInfo
{
public bool IsDetailed { get; init; }
- public int TotalVerifiers { get; internal set; }
+ public int TotalVerifiers { get; internal init; }
}
\ No newline at end of file
diff --git a/src/ModVerify/Reporting/BaselineVerificationTarget.cs b/src/ModVerify/Reporting/BaselineVerificationTarget.cs
new file mode 100644
index 0000000..6e21ddd
--- /dev/null
+++ b/src/ModVerify/Reporting/BaselineVerificationTarget.cs
@@ -0,0 +1,23 @@
+using System.Text;
+using PG.StarWarsGame.Engine;
+
+namespace AET.ModVerify.Reporting;
+
+public sealed class BaselineVerificationTarget
+{
+ public required GameEngineType Engine { get; init; }
+ public required string Name { get; init; }
+ public GameLocations? Location { get; init; } // Optional compared to Verification Target
+ public string? Version { get; init; }
+ public bool IsGame { get; init; }
+
+ public override string ToString()
+ {
+ var sb = new StringBuilder($"[Name={Name};EngineType={Engine};IsGame={IsGame};");
+ if (!string.IsNullOrEmpty(Version)) sb.Append($"Version={Version};");
+ if (Location is not null)
+ sb.Append($"Location={Location};");
+ sb.Append(']');
+ return sb.ToString();
+ }
+}
\ No newline at end of file
diff --git a/src/ModVerify/Reporting/Json/JsonBaselineParser.cs b/src/ModVerify/Reporting/Json/JsonBaselineParser.cs
index ef2f5d1..669ef53 100644
--- a/src/ModVerify/Reporting/Json/JsonBaselineParser.cs
+++ b/src/ModVerify/Reporting/Json/JsonBaselineParser.cs
@@ -1,11 +1,10 @@
using System;
using System.IO;
using System.Text.Json;
-using System.Text.Json.Nodes;
namespace AET.ModVerify.Reporting.Json;
-public static class JsonBaselineParser
+internal static class JsonBaselineParser
{
public static VerificationBaseline Parse(Stream dataStream)
{
@@ -13,8 +12,8 @@ public static VerificationBaseline Parse(Stream dataStream)
throw new ArgumentNullException(nameof(dataStream));
try
{
- var jsonNode = JsonNode.Parse(dataStream);
- var jsonBaseline = ParseCore(jsonNode);
+ var jsonNode = JsonDocument.Parse(dataStream);
+ var jsonBaseline = EvaluateAndDeserialize(jsonNode);
if (jsonBaseline is null)
throw new InvalidBaselineException($"Unable to parse input from stream to {nameof(VerificationBaseline)}. Unknown Error!");
@@ -27,12 +26,11 @@ public static VerificationBaseline Parse(Stream dataStream)
}
}
- private static JsonVerificationBaseline? ParseCore(JsonNode? jsonData)
+ private static JsonVerificationBaseline? EvaluateAndDeserialize(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;
diff --git a/src/ModVerify/Reporting/Json/JsonGameLocation.cs b/src/ModVerify/Reporting/Json/JsonGameLocation.cs
new file mode 100644
index 0000000..bd51993
--- /dev/null
+++ b/src/ModVerify/Reporting/Json/JsonGameLocation.cs
@@ -0,0 +1,40 @@
+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 jsonLocation is null
+ ? null
+ : 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..9495456
--- /dev/null
+++ b/src/ModVerify/Reporting/Json/JsonVerificationTarget.cs
@@ -0,0 +1,63 @@
+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("isGame")]
+ public bool IsGame { get; }
+
+ [JsonPropertyName("version")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Version{ get; }
+
+ [JsonPropertyName("location")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public JsonGameLocation? Location { get; }
+
+ [JsonConstructor]
+ private JsonVerificationTarget(
+ string name,
+ string? version,
+ JsonGameLocation? location,
+ GameEngineType engine,
+ bool isGame)
+ {
+ Name = name;
+ Version = version;
+ Engine = engine;
+ Location = location;
+ IsGame = isGame;
+ }
+
+ public JsonVerificationTarget(BaselineVerificationTarget target)
+ {
+ Name = target.Name;
+ Version = target.Version;
+ Engine = target.Engine;
+ Location = target.Location is null ? null : new JsonGameLocation(target.Location);
+ IsGame = target.IsGame;
+ }
+
+ public static BaselineVerificationTarget? ToTarget(JsonVerificationTarget? jsonTarget)
+ {
+ if (jsonTarget is null)
+ return null;
+ return new BaselineVerificationTarget
+ {
+ Engine = jsonTarget.Engine,
+ Name = jsonTarget.Name,
+ Location = JsonGameLocation.ToLocation(jsonTarget.Location),
+ Version = jsonTarget.Version,
+ IsGame = jsonTarget.IsGame
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs b/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs
index 7ee435d..dbee50d 100644
--- a/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs
+++ b/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs
@@ -7,10 +7,10 @@
namespace AET.ModVerify.Reporting.Reporters;
internal class ConsoleReporter(
- VerifyReportSettings settings,
+ ReporterSettings settings,
bool summaryOnly,
IServiceProvider serviceProvider) :
- ReporterBase(settings, serviceProvider)
+ ReporterBase(settings, serviceProvider)
{
public override Task ReportAsync(IReadOnlyCollection errors)
{
diff --git a/src/ModVerify/Reporting/Reporters/Engine/EngineErrorReporterBase.cs b/src/ModVerify/Reporting/Reporters/Engine/EngineErrorReporterBase.cs
index 5ecbb6f..455212d 100644
--- a/src/ModVerify/Reporting/Reporters/Engine/EngineErrorReporterBase.cs
+++ b/src/ModVerify/Reporting/Reporters/Engine/EngineErrorReporterBase.cs
@@ -38,12 +38,13 @@ public ErrorData(string identifier, string message, IEnumerable context,
ThrowHelper.ThrowIfNullOrEmpty(message);
Identifier = identifier;
Message = message;
- Context = context;
+ Context = context ?? throw new ArgumentNullException(nameof(context));
Asset = asset;
Severity = severity;
}
- public ErrorData(string identifier, string message, string asset, VerificationSeverity severity) : this(identifier, message, [], asset, severity)
+ public ErrorData(string identifier, string message, string asset, VerificationSeverity severity)
+ : this(identifier, message, [], asset, severity)
{
}
}
diff --git a/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs b/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs
index cb6b258..6c5fb17 100644
--- a/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs
+++ b/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs
@@ -14,18 +14,10 @@ internal sealed class GameAssertErrorReporter(IGameRepository gameRepository, IS
protected override ErrorData CreateError(EngineAssert assert)
{
- // TODO: Why is context not used atm?
var context = new List();
-
- if (assert.Value is not null)
- context.Add($"value='{assert.Value}'");
- if (assert.Context is not null)
- context.Add($"context='{assert.Context}'");
-
- // The location is the only identifiable thing of an assert. 'Value' might be null, thus we cannot use it.
- var asset = GetLocation(assert);
-
- return new ErrorData(GetIdFromError(assert.Kind), assert.Message, asset, VerificationSeverity.Warning);
+ context.AddRange(assert.Context);
+ context.Add($"location='{GetLocation(assert)}'");
+ return new ErrorData(GetIdFromError(assert.Kind), assert.Message, context, assert.Value, VerificationSeverity.Warning);
}
private static string GetLocation(EngineAssert assert)
diff --git a/src/ModVerify/Reporting/Reporters/JSON/JsonReporter.cs b/src/ModVerify/Reporting/Reporters/JSON/JsonReporter.cs
index 941a9d5..348fbb1 100644
--- a/src/ModVerify/Reporting/Reporters/JSON/JsonReporter.cs
+++ b/src/ModVerify/Reporting/Reporters/JSON/JsonReporter.cs
@@ -12,7 +12,6 @@ internal class JsonReporter(JsonReporterSettings settings, IServiceProvider serv
{
public const string FileName = "VerificationResult.json";
-
public override async Task ReportAsync(IReadOnlyCollection errors)
{
var report = new JsonVerificationReport(errors.Select(x => new JsonVerificationError(x)));
diff --git a/src/ModVerify/Reporting/Reporters/ReporterBase.cs b/src/ModVerify/Reporting/Reporters/ReporterBase.cs
index fd86119..df444e3 100644
--- a/src/ModVerify/Reporting/Reporters/ReporterBase.cs
+++ b/src/ModVerify/Reporting/Reporters/ReporterBase.cs
@@ -6,7 +6,7 @@
namespace AET.ModVerify.Reporting.Reporters;
-public abstract class ReporterBase(T settings, IServiceProvider serviceProvider) : IVerificationReporter where T : VerifyReportSettings
+public abstract class ReporterBase(T settings, IServiceProvider serviceProvider) : IVerificationReporter where T : ReporterSettings
{
protected IServiceProvider ServiceProvider { get; } = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
diff --git a/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs b/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs
index 7de81eb..b8906ac 100644
--- a/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs
+++ b/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs
@@ -11,7 +11,7 @@ public static class VerificationReportersExtensions
{
public IServiceCollection RegisterJsonReporter()
{
- return RegisterJsonReporter(serviceCollection, new JsonReporterSettings
+ return serviceCollection.RegisterJsonReporter(new JsonReporterSettings
{
OutputDirectory = "."
});
@@ -19,7 +19,7 @@ public IServiceCollection RegisterJsonReporter()
public IServiceCollection RegisterTextFileReporter()
{
- return RegisterTextFileReporter(serviceCollection, new TextFileReporterSettings
+ return serviceCollection.RegisterTextFileReporter(new TextFileReporterSettings
{
OutputDirectory = "."
});
@@ -27,7 +27,7 @@ public IServiceCollection RegisterTextFileReporter()
public IServiceCollection RegisterConsoleReporter(bool summaryOnly = false)
{
- return RegisterConsoleReporter(serviceCollection, new VerifyReportSettings
+ return serviceCollection.RegisterConsoleReporter(new ReporterSettings
{
MinimumReportSeverity = VerificationSeverity.Error
}, summaryOnly);
@@ -43,7 +43,7 @@ public IServiceCollection RegisterTextFileReporter(TextFileReporterSettings sett
return serviceCollection.AddSingleton(sp => new TextFileReporter(settings, sp));
}
- public IServiceCollection RegisterConsoleReporter(VerifyReportSettings settings,
+ public IServiceCollection RegisterConsoleReporter(ReporterSettings settings,
bool summaryOnly = false)
{
return serviceCollection.AddSingleton(sp => new ConsoleReporter(settings, summaryOnly, sp));
diff --git a/src/ModVerify/Reporting/Settings/FileBasedReporterSettings.cs b/src/ModVerify/Reporting/Settings/FileBasedReporterSettings.cs
index c6233a1..759a6ab 100644
--- a/src/ModVerify/Reporting/Settings/FileBasedReporterSettings.cs
+++ b/src/ModVerify/Reporting/Settings/FileBasedReporterSettings.cs
@@ -2,13 +2,11 @@
namespace AET.ModVerify.Reporting.Settings;
-public record FileBasedReporterSettings : VerifyReportSettings
+public record FileBasedReporterSettings : ReporterSettings
{
- private readonly string _outputDirectory = Environment.CurrentDirectory;
-
public string OutputDirectory
{
- get => _outputDirectory;
- init => _outputDirectory = string.IsNullOrEmpty(value) ? Environment.CurrentDirectory : value;
- }
+ get;
+ init => field = string.IsNullOrEmpty(value) ? Environment.CurrentDirectory : value;
+ } = Environment.CurrentDirectory;
}
\ No newline at end of file
diff --git a/src/ModVerify/Reporting/Settings/GlobalVerifyReportSettings.cs b/src/ModVerify/Reporting/Settings/GlobalVerifyReportSettings.cs
index b376fe3..5a51436 100644
--- a/src/ModVerify/Reporting/Settings/GlobalVerifyReportSettings.cs
+++ b/src/ModVerify/Reporting/Settings/GlobalVerifyReportSettings.cs
@@ -1,8 +1,10 @@
namespace AET.ModVerify.Reporting.Settings;
-public record GlobalVerifyReportSettings : VerifyReportSettings
-{
- public VerificationBaseline Baseline { get; init; } = VerificationBaseline.Empty;
+//public record GlobalVerifyReportSettings
+//{
+// //public VerificationSeverity MinimumReportSeverity { get; init; } = VerificationSeverity.Information;
- public SuppressionList Suppressions { get; init; } = SuppressionList.Empty;
-}
\ No newline at end of file
+// public VerificationBaseline Baseline { get; init; } = VerificationBaseline.Empty;
+
+// public SuppressionList Suppressions { get; init; } = SuppressionList.Empty;
+//}
\ No newline at end of file
diff --git a/src/ModVerify/Reporting/Settings/VerifyReportSettings.cs b/src/ModVerify/Reporting/Settings/ReporterSettings.cs
similarity index 81%
rename from src/ModVerify/Reporting/Settings/VerifyReportSettings.cs
rename to src/ModVerify/Reporting/Settings/ReporterSettings.cs
index 1289822..ef33857 100644
--- a/src/ModVerify/Reporting/Settings/VerifyReportSettings.cs
+++ b/src/ModVerify/Reporting/Settings/ReporterSettings.cs
@@ -1,6 +1,6 @@
namespace AET.ModVerify.Reporting.Settings;
-public record VerifyReportSettings
+public record ReporterSettings
{
public VerificationSeverity MinimumReportSeverity { get; init; } = VerificationSeverity.Information;
}
\ No newline at end of file
diff --git a/src/ModVerify/Reporting/VerificationBaseline.cs b/src/ModVerify/Reporting/VerificationBaseline.cs
index c37539b..3f9c274 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 BaselineVerificationTarget? Target { get; }
+
public Version? Version { get; }
public VerificationSeverity MinimumSeverity { get; }
@@ -25,18 +28,22 @@ public sealed class VerificationBaseline : IReadOnlyCollection
public int Count => _errors.Count;
+ public bool IsEmpty => Count == 0;
+
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, BaselineVerificationTarget? target)
{
_errors = [..errors];
Version = LatestVersion;
MinimumSeverity = minimumSeverity;
+ Target = target;
}
public bool Contains(VerificationError error)
@@ -77,6 +84,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/Reporting/VerificationError.cs b/src/ModVerify/Reporting/VerificationError.cs
index 4174f32..538a8fd 100644
--- a/src/ModVerify/Reporting/VerificationError.cs
+++ b/src/ModVerify/Reporting/VerificationError.cs
@@ -117,6 +117,7 @@ public override int GetHashCode()
public override string ToString()
{
- return $"[{Severity}] [{string.Join(" --> ", VerifierChain)}] {Id}: Message={Message}; Asset='{Asset}'; Context=[{string.Join(",", ContextEntries)}];";
+ return $"[{Severity}] [{string.Join(" --> ", VerifierChain)}] " +
+ $"{Id}: Message={Message}; Asset='{Asset}'; Context=[{string.Join(",", ContextEntries)}];";
}
}
\ 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 55%
rename from src/ModVerify/Resources/Schemas/2.0/baseline.json
rename to src/ModVerify/Resources/Schemas/2.1/baseline.json
index 2520a58..da37c4d 100644
--- a/src/ModVerify/Resources/Schemas/2.0/baseline.json
+++ b/src/ModVerify/Resources/Schemas/2.1/baseline.json
@@ -1,9 +1,60 @@
{
- "$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"
+ },
+ "isGame": {
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "name",
+ "engine"
+ ],
+ "additionalProperties": false
+ },
"severity": {
"enum": [ "Information", "Warning", "Error", "Critical" ]
},
@@ -48,7 +99,7 @@
},
"properties": {
"version": {
- "const": "2.0"
+ "const": "2.1"
},
"minSeverity": {
"$ref": "#/$defs/severity"
@@ -59,6 +110,9 @@
"$ref": "#/$defs/error"
},
"additionalItems": false
+ },
+ "target": {
+ "$ref": "#/$defs/target"
}
},
"required": [
diff --git a/src/ModVerify/Settings/VerifyPipelineSettings.cs b/src/ModVerify/Settings/VerifyPipelineSettings.cs
index 2e11c74..db65969 100644
--- a/src/ModVerify/Settings/VerifyPipelineSettings.cs
+++ b/src/ModVerify/Settings/VerifyPipelineSettings.cs
@@ -1,4 +1,5 @@
using AET.ModVerify.Pipeline;
+using AET.ModVerify.Reporting;
namespace AET.ModVerify.Settings;
@@ -8,7 +9,22 @@ public sealed class VerifyPipelineSettings
public required IGameVerifiersProvider VerifiersProvider { get; init; }
- public bool FailFast { get; init; }
+ public FailFastSetting FailFastSettings { get; init; } = FailFastSetting.NoFailFast;
public int ParallelVerifiers { get; init; } = 4;
+}
+
+public readonly struct FailFastSetting
+{
+ public static readonly FailFastSetting NoFailFast = default;
+
+ public readonly bool IsFailFast;
+
+ public readonly VerificationSeverity MinumumSeverity;
+
+ public FailFastSetting(VerificationSeverity severity)
+ {
+ IsFailFast = true;
+ MinumumSeverity = severity;
+ }
}
\ No newline at end of file
diff --git a/src/ModVerify/VerificationTarget.cs b/src/ModVerify/VerificationTarget.cs
new file mode 100644
index 0000000..c0d5b97
--- /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/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: ";
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/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs
index 311c212..0b8687e 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
{
@@ -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/GameLocations.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs
index 3b77732..f57b605 100644
--- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs
+++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameLocations.cs
@@ -1,7 +1,8 @@
-using System;
+using AnakinRaW.CommonUtilities;
+using System;
using System.Collections.Generic;
using System.Linq;
-using AnakinRaW.CommonUtilities;
+using System.Text;
namespace PG.StarWarsGame.Engine;
@@ -27,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));
@@ -48,4 +49,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/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/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/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/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/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj
index e54e6f0..1376246 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/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.");
}
}
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/Rendering/Font/WindowsFontManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs
index f11374e..fe8f546 100644
--- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs
+++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs
@@ -28,8 +28,8 @@ public IEnumerable GetFontFamilies()
return fonts;
}
- static IEnumerable<(Gdi32.ENUMLOGFONTEXDV lpelfe, Gdi32.ENUMTEXTMETRIC lpntme, Gdi32.FontType FontType)> GetFonts(Gdi32.SafeHDC hdc)
+ static IEnumerable<(Gdi32.ENUMLOGFONTEXDV lpelfe, Gdi32.ENUMTEXTMETRIC _, Gdi32.FontType __)> GetFonts(Gdi32.SafeHDC hdc)
{
- return Gdi32.EnumFontFamiliesEx(hdc, CharacterSet.DEFAULT_CHARSET);
+ return Gdi32.EnumFontFamiliesEx(hdc, lfCharSet: CharacterSet.DEFAULT_CHARSET);
}
}
\ No newline at end of file
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
diff --git a/test/ModVerify.CliApp.Test/CommonTestBase.cs b/test/ModVerify.CliApp.Test/CommonTestBase.cs
index 7d6dc18..8a93507 100644
--- a/test/ModVerify.CliApp.Test/CommonTestBase.cs
+++ b/test/ModVerify.CliApp.Test/CommonTestBase.cs
@@ -1,29 +1,22 @@
-using System;
-using System.IO.Abstractions;
+using AET.SteamAbstraction;
using AnakinRaW.CommonUtilities.Hashing;
+using AnakinRaW.CommonUtilities.Testing;
using Microsoft.Extensions.DependencyInjection;
using PG.Commons;
-using Testably.Abstractions.Testing;
+using PG.StarWarsGame.Infrastructure;
+using PG.StarWarsGame.Infrastructure.Clients.Steam;
namespace ModVerify.CliApp.Test;
-public abstract class CommonTestBase
+public abstract class CommonTestBase : 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);
- // 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 cde1dad..ecf3253 100644
--- a/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs
+++ b/test/ModVerify.CliApp.Test/EmbeddedBaselineTest.cs
@@ -1,31 +1,18 @@
using AET.ModVerify.App;
using AET.ModVerify.App.Reporting;
-using AET.ModVerify.App.Settings;
-using AET.ModVerify.Settings;
using AnakinRaW.ApplicationBase.Environment;
using Microsoft.Extensions.DependencyInjection;
using PG.StarWarsGame.Engine;
using System;
using System.IO.Abstractions;
-using ModVerify.CliApp.Test.TestData;
using Testably.Abstractions;
+using Xunit;
namespace ModVerify.CliApp.Test;
public class BaselineSelectorTest
{
private static readonly IFileSystem FileSystem = new RealFileSystem();
- private static readonly ModVerifyAppSettings TestSettings = new()
- {
- ReportSettings = new(),
- GameInstallationsSettings = new (),
- VerifyPipelineSettings = new()
- {
- GameVerifySettings = new GameVerifySettings(),
- VerifiersProvider = new NoVerifierProvider()
- }
- };
-
private readonly IServiceProvider _serviceProvider;
public BaselineSelectorTest()
@@ -42,6 +29,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
diff --git a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj
index 84d1bba..826987d 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 294baf7..c5caf92 100644
--- a/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs
+++ b/test/ModVerify.CliApp.Test/ModVerifyOptionsParserTest.cs
@@ -4,7 +4,11 @@
using System.IO.Abstractions;
using ModVerify.CliApp.Test.TestData;
using Testably.Abstractions;
+using Xunit;
+#if NETFRAMEWORK
using ModVerify.CliApp.Test.Utilities;
+#endif
+
namespace ModVerify.CliApp.Test;
diff --git a/test/ModVerify.CliApp.Test/TargetSelectors/AutomaticSelectorTest.cs b/test/ModVerify.CliApp.Test/TargetSelectors/AutomaticSelectorTest.cs
new file mode 100644
index 0000000..f01d5cd
--- /dev/null
+++ b/test/ModVerify.CliApp.Test/TargetSelectors/AutomaticSelectorTest.cs
@@ -0,0 +1,406 @@
+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(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_ThrowsGameNotFoundException(IGameIdentity identity)
+ {
+ 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)
+ {
+ TestSelectTarget(
+ identity,
+ gameInstallation => gameInstallation.InstallMod("MyMod", false),
+ ti => new VerificationTargetSettings
+ {
+ TargetPath = ti.PlayableObject.Directory.FullName,
+ Engine = ti.GameInstallation.Game.Type.Opposite().ToEngineType()
+ }, typeof(ArgumentException));
+ }
+
+
+ [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)
+ {
+ 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: typeof(ArgumentException));
+ }
+
+ [Theory]
+ [MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))]
+ public void Test_SelectTarget_DetachedMod(IGameIdentity identity)
+ {
+ 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()
+ });
+ }
+
+ [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;
+ }
+
+ 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
diff --git a/test/ModVerify.CliApp.Test/TargetSelectors/GameFinderServiceTest.cs b/test/ModVerify.CliApp.Test/TargetSelectors/GameFinderServiceTest.cs
new file mode 100644
index 0000000..334d43f
--- /dev/null
+++ b/test/ModVerify.CliApp.Test/TargetSelectors/GameFinderServiceTest.cs
@@ -0,0 +1,72 @@
+using AET.ModVerify.App.GameFinder;
+using AnakinRaW.CommonUtilities.Registry;
+using AnakinRaW.CommonUtilities.Testing.Extensions;
+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 Xunit;
+
+namespace ModVerify.CliApp.Test.TargetSelectors;
+
+public class GameFinderServiceTest : CommonTestBase
+{
+ private readonly IRegistry _registry = new InMemoryRegistry(InMemoryRegistryCreationFlags.WindowsLike);
+ private readonly GameFinderService _finderService;
+ public GameFinderServiceTest()
+ {
+ _finderService = new GameFinderService(ServiceProvider);
+ }
+ protected override void SetupServices(IServiceCollection serviceCollection)
+ {
+ base.SetupServices(serviceCollection);
+ serviceCollection.AddSingleton(_registry);
+ }
+
+ [Theory]
+ [MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))]
+ public void FindGame_SearchFallbackGame_FocInstalledButEawNot_ThrowsGameNotFoundException(IGameIdentity identity)
+ {
+ if (identity.Type == GameType.Eaw)
+ return;
+
+ var foc = GameInfrastructureTesting.Game(identity, ServiceProvider);
+ GameInfrastructureTesting.Registry(ServiceProvider).CreateInstalled(foc.Game);
+
+ Assert.Throws(() => _finderService.FindGame(foc.Game.Directory.FullName, new GameFinderSettings
+ {
+ Engine = GameEngineType.Foc,
+ SearchFallbackGame = true
+ }));
+
+ Assert.Throws(() => _finderService.FindGame(foc.Game.Directory.FullName, new GameFinderSettings
+ {
+ Engine = null,
+ SearchFallbackGame = true
+ }));
+ }
+
+ [Theory]
+ [MemberData(nameof(GITestUtilities.RealGameIdentities), MemberType = typeof(GITestUtilities))]
+ public void FindGame_EngineEaw_SearchFallbackGameIsIgnored(IGameIdentity identity)
+ {
+ if (identity.Type == GameType.Foc)
+ return;
+
+ var eaw = GameInfrastructureTesting.Game(identity, ServiceProvider);
+ GameInfrastructureTesting.Registry(ServiceProvider).CreateInstalled(eaw.Game);
+
+ Assert.DoesNotThrowException(() => _finderService.FindGame(eaw.Game.Directory.FullName, new GameFinderSettings
+ {
+ Engine = GameEngineType.Eaw,
+ SearchFallbackGame = true
+ }));
+
+ Assert.DoesNotThrowException(() => _finderService.FindGame(eaw.Game.Directory.FullName, new GameFinderSettings
+ {
+ Engine = null,
+ SearchFallbackGame = true
+ }));
+ }
+}
\ No newline at end of file