Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a439bdb
fix debug disposal
AnakinRaW Apr 11, 2026
7679e74
Handle null `TextureData` during `GuiDialog` initialization.
AnakinRaW Apr 11, 2026
ac3dd96
Engine support for finding files on linux systems the way the game wo…
AnakinRaW Apr 11, 2026
5b8cedd
Add support for the `--useDefaultBaseline` option and improve baselin…
AnakinRaW Apr 11, 2026
07026bb
Remove obsolete filesystem abstraction utilities for directory and fi…
AnakinRaW Apr 12, 2026
54c833f
Introduce `PetroglyphFileSystem` abstraction for cross-platform files…
AnakinRaW Apr 12, 2026
5298cfd
Refactor `PetroglyphXmlFileParserBase`: remove redundant `IServicePro…
AnakinRaW Apr 12, 2026
5474043
Add `PG.StarWarsGame.Engine.FileSystem` project to solution
AnakinRaW Apr 12, 2026
e40030b
Update dependencies and add reference to `PG.StarWarsGame.Engine.File…
AnakinRaW Apr 12, 2026
3499030
Refactor: Replace generic filesystem abstraction with `PetroglyphFile…
AnakinRaW Apr 12, 2026
81dcf6f
Refactor: Simplify `NormalizePath` logic and remove unused return value
AnakinRaW Apr 12, 2026
5f87709
Refactor: Use `ValueStringBuilder` in `PathStartsWithDataDirectory` f…
AnakinRaW Apr 12, 2026
13f26c5
Refactor: Replace direct filename handling with `NormalizeFileName` f…
AnakinRaW Apr 12, 2026
3247dd5
Refactor path normalization to use PathNormalizer
AnakinRaW Apr 14, 2026
b4c03cc
Rename StringExtensions and add Enum.Parse<T> extension
AnakinRaW Apr 14, 2026
3b58ea8
Refactor AudioFileVerifier: move FriendlyName, remove Verify
AnakinRaW Apr 14, 2026
cfb5053
Add comprehensive unit tests for `PG.StarWarsGame.Engine.FileSystem` …
AnakinRaW Apr 14, 2026
f16178a
Remove unused test case for volume file names and update path handlin…
AnakinRaW Apr 14, 2026
cb9e2db
Optimize case-insensitive file existence check in `PetroglyphFileSyst…
AnakinRaW Apr 14, 2026
5401c7d
Update README with detailed mod verification examples for Windows and…
AnakinRaW Apr 14, 2026
359362e
Add Linux-specific build target to release workflow
AnakinRaW Apr 14, 2026
be6ba5d
Fix mod paths in Linux instructions
AnakinRaW Apr 14, 2026
7eb7357
revert to old impl
AnakinRaW Apr 14, 2026
01370d9
Add Linux-specific tests for case-insensitive file existence handling…
AnakinRaW Apr 14, 2026
62ce77a
Merge remote-tracking branch 'origin/feat/support-linux' into feat/su…
AnakinRaW Apr 14, 2026
df19dee
Update README: include `--engine` parameter in examples and add Linux…
AnakinRaW Apr 14, 2026
d61fce6
update dependencies
AnakinRaW Apr 15, 2026
2078875
Refactor file path handling in GetFileInfoFromMasterMeg
AnakinRaW Apr 15, 2026
d77f8d3
rename method to express intent
AnakinRaW Apr 15, 2026
f27972b
formatting
AnakinRaW Apr 15, 2026
71f7742
Update path normalization for Windows-like behavior on Linux
AnakinRaW Apr 15, 2026
821e5b3
Merge branch 'feat/support-linux' of https://github.com/AlamoEngine-T…
AnakinRaW Apr 15, 2026
7a7f2d3
documentation
AnakinRaW Apr 15, 2026
abce4b1
Refactor path handling to use platform-specific separators
AnakinRaW Apr 15, 2026
5519163
fix reporting of missing texutres
AnakinRaW Apr 16, 2026
12b170b
add new algorithm to evaluate
AnakinRaW Apr 16, 2026
1d5a8a2
Handle "none" GUI textures and unify repo lookups
AnakinRaW Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ jobs:
- name: Create Net Core Release
# use publish for .NET Core
run: dotnet publish ${{ env.TOOL_PROJ_PATH }} --configuration Release -f net10.0 --output ./releases/net10.0 /p:DebugType=None /p:DebugSymbols=false
- name: Create Linux Release
run: dotnet publish ${{ env.TOOL_PROJ_PATH }} --configuration Release -f net10.0 --runtime linux-x64 --self-contained true --output ./releases/linux-x64 /p:DebugType=None /p:DebugSymbols=false
- name: Upload a Build Artifact
uses: actions/upload-artifact@v7
with:
Expand Down Expand Up @@ -117,4 +119,5 @@ jobs:
generate_release_notes: true
files: |
./releases/net481/ModVerify.exe
./releases/ModVerify-Net10.zip
./releases/ModVerify-Net10.zip
./releases/linux-x64/ModVerify
2 changes: 2 additions & 0 deletions ModVerify.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
<Project Path="modules/ModdingToolBase/src/Updater/ExternalUpdater.Core/ExternalUpdater.Core.csproj" />
</Folder>
<Folder Name="/PetroglyphTools/">
<Project Path="src/PetroglyphTools/PG.StarWarsGame.Engine.FileSystem.Test/PG.StarWarsGame.Engine.FileSystem.Test.csproj" />
<Project Path="src/PetroglyphTools/PG.StarWarsGame.Engine.FileSystem/PG.StarWarsGame.Engine.FileSystem.csproj" />
<Project Path="src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj" />
<Project Path="src/PetroglyphTools/PG.StarWarsGame.Files.ALO/PG.StarWarsGame.Files.ALO.csproj" />
<Project Path="src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj" />
Expand Down
43 changes: 40 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,42 @@ In general ModVerify has two operation mods.
1. `verify` Verifying a game or mod
2. `createBaseline` Creating a baseline for a game or mod, that can be used for further verifications in order to verify you did not add more errors to your mods.

### Example
This is an example run configuration that analyzes a specific mod, uses a the FoC basline and writes the output into a dedicated directory:
### Examples

```bash
#### Example 1: Auto-detection with a custom baseline
Analyzes a specific mod, uses the FoC baseline and writes the output into a dedicated directory:

**Windows:**
```bat
.\ModVerify.exe verify --path "C:\My Games\FoC\Mods\MyMod" --outDir "C:\My Games\FoC\Mods\MyMod\verifyResults" --baseline ./focBaseline.json
```

**Linux:**
```bash
./ModVerify verify \
--path "/home/user/games/FoC/Mods/MyMod" \
--outDir "/home/user/games/FoC/Mods/MyMod/verifyResults" \
--baseline ./focBaseline.json
```

#### Example 2: Manual mod setup with sub-mods, EaW fallback and default baseline
Uses manual mod setup, including sub-mods and the EaW fallback game, and uses the default embedded baseline:

**Windows:**
```bat
.\ModVerify.exe verify --mods "C:\My Games\FoC\Mods\MySubMod;C:\My Games\FoC\Mods\MyMod" --game "C:\My Games\FoC" --fallbackGame "C:\My Games\EaW" --engine FOC --useDefaultBaseline
```

**Linux:**
```bash
./ModVerify verify \
--mods "/home/user/games/FoC/Mods/MySubMod:/home/user/games/FoC/Mods/MyMod" \
--game "/home/user/games/FoC" \
--fallbackGame "/home/user/games/EaW" \
--engine FOC \
--useDefaultBaseline
```

---

## Available Checks
Expand Down Expand Up @@ -116,6 +145,14 @@ The following verifiers are currently implemented:
If you want to create your own baseline use the `createBaseline` option.

### Example

**Windows**
```bash
ModVerify.exe createBaseline --outFile myBaseline.json --path "C:\My Games\FoC\Mods\MyMod"
```
**Linux**
```bash
./ModVerify createBaseline \
--outFile myBaseline.json \
--path "C:\My Games\FoC\Mods\MyMod"
```
3 changes: 2 additions & 1 deletion src/ModVerify.CliApp/App/VerifyAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ protected override VerificationBaseline GetBaseline(VerificationTarget verificat
{
Console.WriteLine();
ModVerifyConsoleUtilities.WriteBaselineInfo(baseline, baselinePath);
Logger?.LogDebug("Using baseline {Baseline} from location '{Path}'", baseline.ToString(), baselinePath);
Logger?.LogDebug("Using baseline {Baseline} from location '{Path}'",
baseline.ToString(), baselinePath ?? "Embedded");
Console.WriteLine();
}
return baseline;
Expand Down
20 changes: 12 additions & 8 deletions src/ModVerify.CliApp/ModVerify.CliApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@
<PackageReference Include="Serilog.Extensions.Logging" Version="10.0.0" />
<PackageReference Include="AlamoEngineTools.SteamAbstraction" Version="5.0.7" />
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.6" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.6" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.6" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.6" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.6" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
Expand All @@ -62,15 +62,15 @@

<!-- Exclude System.Index from all dependencies, excluding Microsoft.Bcl.Memory -->
<ItemGroup>
<PackageReference Include="Vanara.Core" Version="5.0.1" Condition="'$(TargetFramework)' == 'net481'">
<PackageReference Include="Vanara.Core" Version="5.0.4" Condition="'$(TargetFramework)' == 'net481'">
<ExcludeAssets>compile</ExcludeAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="IndexRange" Version="1.1.0" Condition="'$(TargetFramework)' == 'net481'">
<PackageReference Include="IndexRange" Version="1.1.1" Condition="'$(TargetFramework)' == 'net481'">
<ExcludeAssets>compile</ExcludeAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Bcl.Memory" Version="10.0.5" Condition="'$(TargetFramework)' == 'net481'" />
<PackageReference Include="Microsoft.Bcl.Memory" Version="10.0.6" Condition="'$(TargetFramework)' == 'net481'" />
</ItemGroup>

<ItemGroup>
Expand All @@ -82,6 +82,10 @@
<ProjectReference Include="..\ModVerify\ModVerify.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="10.0.202" />
</ItemGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<WeaverConfiguration>
<Weavers>
Expand Down
18 changes: 16 additions & 2 deletions src/ModVerify.CliApp/Reporting/BaselineSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public VerificationBaseline SelectBaseline(VerificationTarget verificationTarget
}
}

if (!settings.ReportSettings.SearchBaselineLocally)
if (settings.ReportSettings is { SearchBaselineLocally: false, UseDefaultBaseline: false })
{
_logger?.LogDebug(ModVerifyConstants.ConsoleEventId,
"No baseline path specified and local search is not enabled. Using empty baseline.");
Expand Down Expand Up @@ -134,7 +134,7 @@ internal static VerificationBaseline LoadEmbeddedBaseline(GameEngineType engineT
private VerificationBaseline FindBaselineNonInteractive(VerificationTarget target, out string? usedPath)
{
if (_baselineFactory.TryFindBaselineInDirectory(
target.Location.TargetPath,
target.Location.TargetPath,
b => IsBaselineCompatible(b, target),
out var baseline,
out usedPath))
Expand All @@ -144,6 +144,20 @@ private VerificationBaseline FindBaselineNonInteractive(VerificationTarget targe
}
_logger?.LogTrace("No baseline file found in taget path '{TargetPath}'.", target.Location.TargetPath);
usedPath = null;
if (settings.ReportSettings.UseDefaultBaseline)
{
try
{
var defaultBaseline = LoadEmbeddedBaseline(target.Engine);
_logger?.LogInformation(ModVerifyConstants.ConsoleEventId, "Automatically applying default embedded baseline for engine '{Engine}'.", target.Engine);
return defaultBaseline;
}
catch (InvalidBaselineException)
{
throw new InvalidOperationException(
"Invalid baseline packed along ModVerify App. Please reach out to the creators. Thanks!");
}
}
return VerificationBaseline.Empty;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ internal abstract class BaseModVerifyOptions
"The argument cannot be combined with any of --mods, --game or --fallbackGame")]
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. " +
[Option("mods", SetName = "manualPaths", Required = false, Default = null,
HelpText = "The path of the mod to verify. To support submods, multiple paths can be separated using the platform-specific path separator (';' on Windows, ':' on Linux). " +
"Leave empty, if you want to verify a game. If you want to use the interactive mode, leave this, --game and --fallbackGame empty.")]
public IList<string>? ModPaths { get; init; }
public string? ModPaths { get; init; }

[Option("game", SetName = "manualPaths", Required = false, Default = null,
HelpText = "The path of the base game. For FoC mods this points to the FoC installation, for EaW mods this points to the EaW installation. " +
Expand All @@ -47,10 +47,10 @@ internal abstract class BaseModVerifyOptions
public GameEngineType? Engine { get; init; }


[Option("additionalFallbackPaths", Required = false, Separator = ';',
[Option("additionalFallbackPaths", Required = false,
HelpText = "Additional fallback paths, which may contain assets that shall be included when doing the verification. Do not add EaW here. " +
"Multiple paths can be separated using the ';' (semicolon) character.")]
public IList<string>? AdditionalFallbackPath { get; init; }
"Multiple paths can be separated using the platform-specific path separator (';' on Windows, ':' on Linux).")]
public string? AdditionalFallbackPath { get; init; }

[Option("parallel", Default = false,
HelpText = "When set, game verifiers will run in parallel. " +
Expand Down
12 changes: 8 additions & 4 deletions src/ModVerify.CliApp/Settings/CommandLine/VerifyVerbOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@ internal sealed class VerifyVerbOption : BaseModVerifyOptions
public bool IgnoreAsserts { get; init; }


[Option("baseline", SetName = "baselineSelection", Required = false,
HelpText = "Path to a JSON baseline file. Cannot be used together with --searchBaseline.")]
[Option("baseline", Required = false,
HelpText = "Path to a JSON baseline file. Cannot be used together with --searchBaseline or --useDefaultBaseline.")]
public string? Baseline { get; init; }

[Option("searchBaseline", SetName = "baselineSelection", Required = false,
HelpText = "When set, the application will search for baseline files and use them for verification. Cannot be used together with --baseline")]
[Option("searchBaseline", Required = false,
HelpText = "When set, the application will search for baseline files and use them for verification. Cannot be used together with --baseline or --useDefaultBaseline")]
public bool SearchBaselineLocally { get; init; }

[Option("useDefaultBaseline", Required = false,
HelpText = "When set, the application will use the default embedded baseline for the detected game engine. Cannot be used together with --baseline or --searchBaseline.")]
public bool UseDefaultBaseline { get; init; }

public bool IsRunningWithoutArguments { get; init; }
}
1 change: 1 addition & 0 deletions src/ModVerify.CliApp/Settings/ModVerifyAppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public sealed class VerifyReportSettings : AppReportSettings
{
public string? BaselinePath { get; init; }
public bool SearchBaselineLocally { get; init; }
public bool UseDefaultBaseline { get; init; }
}

internal abstract class AppSettingsBase(AppReportSettings reportSettings)
Expand Down
36 changes: 24 additions & 12 deletions src/ModVerify.CliApp/Settings/SettingsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO.Abstractions;
using System.Linq;

namespace AET.ModVerify.App.Settings;

Expand Down Expand Up @@ -57,6 +58,20 @@ void ValidateVerb()
throw new AppArgumentException($"Options {searchOption} and {baselineOption} cannot be used together.");
}

if (verifyOptions.UseDefaultBaseline && !string.IsNullOrEmpty(verifyOptions.Baseline))
{
var useDefaultOption = typeof(VerifyVerbOption).GetOptionName(nameof(VerifyVerbOption.UseDefaultBaseline));
var baselineOption = typeof(VerifyVerbOption).GetOptionName(nameof(VerifyVerbOption.Baseline));
throw new AppArgumentException($"Options {useDefaultOption} and {baselineOption} cannot be used together.");
}

if (verifyOptions is { UseDefaultBaseline: true, SearchBaselineLocally: true })
{
var useDefaultOption = typeof(VerifyVerbOption).GetOptionName(nameof(VerifyVerbOption.UseDefaultBaseline));
var searchOption = typeof(VerifyVerbOption).GetOptionName(nameof(VerifyVerbOption.SearchBaselineLocally));
throw new AppArgumentException($"Options {useDefaultOption} and {searchOption} cannot be used together.");
}

if (verifyOptions is { FailFast: true, MinimumFailureSeverity: null })
{
var failFast = typeof(VerifyVerbOption).GetOptionName(nameof(VerifyVerbOption.FailFast));
Expand Down Expand Up @@ -86,6 +101,7 @@ VerifyReportSettings BuildReportSettings()
BaselinePath = verifyOptions.Baseline,
MinimumReportSeverity = verifyOptions.MinimumSeverity,
SearchBaselineLocally = verifyOptions.SearchBaselineLocally,
UseDefaultBaseline = verifyOptions.UseDefaultBaseline,
SuppressionsPath = verifyOptions.Suppressions,
Verbose = verifyOptions.Verbose
};
Expand Down Expand Up @@ -121,24 +137,20 @@ AppReportSettings BuildReportSettings()

private VerificationTargetSettings BuildTargetSettings(BaseModVerifyOptions options)
{
var separator = _fileSystem.Path.PathSeparator;

var modPaths = new List<string>();
if (options.ModPaths is not null)
if (!string.IsNullOrEmpty(options.ModPaths))
{
foreach (var mod in options.ModPaths)
{
if (!string.IsNullOrEmpty(mod))
modPaths.Add(_fileSystem.Path.GetFullPath(mod));
}
var split = options.ModPaths!.Split([separator], StringSplitOptions.RemoveEmptyEntries);
modPaths.AddRange(split.Select(s => _fileSystem.Path.GetFullPath(s)));
}

var fallbackPaths = new List<string>();
if (options.AdditionalFallbackPath is not null)
if (!string.IsNullOrEmpty(options.AdditionalFallbackPath))
{
foreach (var fallback in options.AdditionalFallbackPath)
{
if (!string.IsNullOrEmpty(fallback))
fallbackPaths.Add(_fileSystem.Path.GetFullPath(fallback));
}
var split = options.AdditionalFallbackPath!.Split([separator], StringSplitOptions.RemoveEmptyEntries);
fallbackPaths.AddRange(split.Select(s => _fileSystem.Path.GetFullPath(s)));
}

var gamePath = options.GamePath;
Expand Down
8 changes: 6 additions & 2 deletions src/ModVerify/ModVerify.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@

<ItemGroup>
<PackageReference Include="AnakinRaW.CommonUtilities.SimplePipeline" Version="13.0.18" />
<PackageReference Include="System.Text.Json" Version="10.0.5" />
<PackageReference Include="JsonSchema.Net" Version="9.1.3" />
<PackageReference Include="System.Text.Json" Version="10.0.6" />
<PackageReference Include="JsonSchema.Net" Version="9.2.0" />
</ItemGroup>

<ItemGroup>
Expand All @@ -42,4 +42,8 @@
<ProjectReference Include="..\PetroglyphTools\PG.StarWarsGame.Engine\PG.StarWarsGame.Engine.csproj" />
<ProjectReference Include="..\PetroglyphTools\PG.StarWarsGame.Files.ALO\PG.StarWarsGame.Files.ALO.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="10.0.202" />
</ItemGroup>
</Project>
4 changes: 2 additions & 2 deletions src/ModVerify/Verifiers/Commons/AudioFileVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public class AudioFileVerifier : GameVerifier<AudioFileInfo>
{
private readonly IAlreadyVerifiedCache? _alreadyVerifiedCache;

public override string FriendlyName => "Audio File format";

public AudioFileVerifier(GameVerifierBase parent) : base(parent)
{
_alreadyVerifiedCache = Services.GetService<IAlreadyVerifiedCache>();
Expand All @@ -27,8 +29,6 @@ public AudioFileVerifier(IGameVerifierInfo? parent,
_alreadyVerifiedCache = serviceProvider.GetService<IAlreadyVerifiedCache>();
}

public override string FriendlyName => "Audio File format";

public override void Verify(AudioFileInfo sampleInfo, IReadOnlyCollection<string> contextInfo, CancellationToken token)
{
var cached = _alreadyVerifiedCache?.GetEntry(sampleInfo.SampleName);
Expand Down
Loading
Loading