diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b7146db..497ece0 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -77,7 +77,7 @@ jobs:
- name: Create Manifest
run: dotnet ./dev/${{env.MANIFEST_CREATOR}} -a binaries/${{env.TOOL_EXE}} --appDataFiles binaries/${{env.UPDATER_EXE}} --origin ${{env.ORIGIN_BASE}} -o ./binaries -b ${{env.BRANCH_NAME}}
- name: Upload Build
- run: dotnet ./dev/${{env.SFTP_UPLOADER}} -h $host --port $port -u ${{secrets.SFTP_USER}} -p ${{secrets.SFTP_PASSWORD}} --base $base_path -s $source
+ run: dotnet ./dev/${{env.SFTP_UPLOADER}} ftp --host $host --port $port -u ${{secrets.SFTP_USER}} -p ${{secrets.SFTP_PASSWORD}} --base $base_path -s $source
env:
host: republicatwar.com
port: 1579
diff --git a/Directory.Build.props b/Directory.Build.props
index e638e40..f9cd8ce 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -3,7 +3,7 @@
all
- 3.7.115
+ 3.9.50
\ No newline at end of file
diff --git a/ModVerify b/ModVerify
index 59b5a3f..4eb6a1c 160000
--- a/ModVerify
+++ b/ModVerify
@@ -1 +1 @@
-Subproject commit 59b5a3f00b5d5445fecf1c4a1013ce412d47b80f
+Subproject commit 4eb6a1c0567b68c6a0f858426c478c7572f4d81c
diff --git a/ModdingToolBase b/ModdingToolBase
index 7893e04..e12f6ce 160000
--- a/ModdingToolBase
+++ b/ModdingToolBase
@@ -1 +1 @@
-Subproject commit 7893e0437e9bc8880962b1b553839eb9cc57b1e9
+Subproject commit e12f6ceedb83fe9e3372dd89c68d508f8479cf92
diff --git a/RawDevTools.sln b/RawDevTools.sln
index 6f4e119..6f39e92 100644
--- a/RawDevTools.sln
+++ b/RawDevTools.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.6.33626.354
+# Visual Studio Version 18
+VisualStudioVersion = 18.4.11519.219 insiders
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevLauncher", "src\DevLauncher\DevLauncher.csproj", "{1BA491BC-2CD6-4270-9573-5FF9529D8D89}"
EndProject
@@ -39,8 +39,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PG.StarWarsGame.Files.ALO",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PG.StarWarsGame.Engine", "ModVerify\src\PetroglyphTools\PG.StarWarsGame.Engine\PG.StarWarsGame.Engine.csproj", "{301A0BA8-303F-415E-A961-4755FB3978D8}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModVerify.CliApp", "ModVerify\src\ModVerify.CliApp\ModVerify.CliApp.csproj", "{C5703F53-DC3D-4B4E-9E18-365124C72793}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RawDevTools", "src\RawDevTools\RawDevTools.csproj", "{E4F63116-B94A-43FB-ABA9-B9D491D9F6D9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TextCompile", "src\TextCompile\TextCompile.csproj", "{50E34854-BF6B-4170-BB4C-74830AB5609B}"
@@ -113,10 +111,6 @@ Global
{301A0BA8-303F-415E-A961-4755FB3978D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{301A0BA8-303F-415E-A961-4755FB3978D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{301A0BA8-303F-415E-A961-4755FB3978D8}.Release|Any CPU.Build.0 = Release|Any CPU
- {C5703F53-DC3D-4B4E-9E18-365124C72793}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C5703F53-DC3D-4B4E-9E18-365124C72793}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C5703F53-DC3D-4B4E-9E18-365124C72793}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C5703F53-DC3D-4B4E-9E18-365124C72793}.Release|Any CPU.Build.0 = Release|Any CPU
{E4F63116-B94A-43FB-ABA9-B9D491D9F6D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E4F63116-B94A-43FB-ABA9-B9D491D9F6D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E4F63116-B94A-43FB-ABA9-B9D491D9F6D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -163,7 +157,6 @@ Global
{D6AC444F-5CC4-4DAC-B974-E3E64B9761A3} = {FF4C3704-0C74-40B5-A38D-AF29B6385D85}
{94267A81-F31C-4381-9E2B-868C0AC0E65A} = {FF4C3704-0C74-40B5-A38D-AF29B6385D85}
{301A0BA8-303F-415E-A961-4755FB3978D8} = {FF4C3704-0C74-40B5-A38D-AF29B6385D85}
- {C5703F53-DC3D-4B4E-9E18-365124C72793} = {D4C9B8AA-96E5-49F4-B8A4-765144E16548}
{50E34854-BF6B-4170-BB4C-74830AB5609B} = {5A7785E7-C59E-4F2D-9596-352844693272}
{BB759AD8-EEAC-4994-80A8-34E60B2FDA86} = {5A7785E7-C59E-4F2D-9596-352844693272}
{846253D9-D766-8852-74B9-E1E04B9F2A81} = {3E986062-E81F-4833-A127-24FA73FBCB1B}
diff --git a/src/DevLauncher.Tests/DevLauncher.Tests.csproj b/src/DevLauncher.Tests/DevLauncher.Tests.csproj
index 65466d9..a54378b 100644
--- a/src/DevLauncher.Tests/DevLauncher.Tests.csproj
+++ b/src/DevLauncher.Tests/DevLauncher.Tests.csproj
@@ -10,15 +10,15 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/DevLauncher/DevLauncher.csproj b/src/DevLauncher/DevLauncher.csproj
index fd9c2b4..6f7f992 100644
--- a/src/DevLauncher/DevLauncher.csproj
+++ b/src/DevLauncher/DevLauncher.csproj
@@ -19,15 +19,15 @@
-
-
+
+
-
-
+
+
-
-
+
+
@@ -38,20 +38,28 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
- all
+
+
+
+
+
+ compile
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ compile
runtime; build; native; contentfiles; analyzers; buildtransitive
+
-
diff --git a/src/DevLauncher/DevLauncherEnvironment.cs b/src/DevLauncher/DevLauncherEnvironment.cs
index 67a4da7..76dc6d5 100644
--- a/src/DevLauncher/DevLauncherEnvironment.cs
+++ b/src/DevLauncher/DevLauncherEnvironment.cs
@@ -7,14 +7,13 @@
namespace RepublicAtWar.DevLauncher;
-internal class DevLauncherEnvironment(Assembly assembly, IFileSystem fileSystem) : UpdatableApplicationEnvironment(assembly, fileSystem)
+internal class DevLauncherEnvironment(Assembly assembly, IFileSystem fileSystem)
+ : UpdatableApplicationEnvironment(assembly, fileSystem)
{
private const string ToolPathName = "RawDevLauncher";
public override string ApplicationName => "Republic at War DevLauncher";
- public override Uri? RepositoryUrl => null;
-
public override ICollection UpdateMirrors { get; } = new List
{
new($"https://republicatwar.com/downloads/{ToolPathName}")
diff --git a/src/DevLauncher/Options/VerifyOption.cs b/src/DevLauncher/Options/VerifyOption.cs
deleted file mode 100644
index 3dca4d0..0000000
--- a/src/DevLauncher/Options/VerifyOption.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-using CommandLine;
-
-namespace RepublicAtWar.DevLauncher.Options;
-
-[Verb("verify")]
-internal class VerifyOption : RaWBuildOption;
\ No newline at end of file
diff --git a/src/DevLauncher/Pipelines/Actions/CreateLocalizationDiffsAction.cs b/src/DevLauncher/Pipelines/Actions/CreateLocalizationDiffsAction.cs
index 3c27909..8689dd9 100644
--- a/src/DevLauncher/Pipelines/Actions/CreateLocalizationDiffsAction.cs
+++ b/src/DevLauncher/Pipelines/Actions/CreateLocalizationDiffsAction.cs
@@ -44,7 +44,7 @@ protected override void RunAction(CancellationToken cancellationToken)
if (locFile.Language == LanguageType.English)
continue;
- Logger?.LogInformation($"Creating Diff for data '{langFile}'");
+ Logger?.LogInformation("Creating Diff for data '{LangFile}'", langFile);
var masterText = _localizationFileService.CreateModelFromLocalizationFile(locFile);
diff --git a/src/DevLauncher/Pipelines/Actions/InitializeLocalizationAction.cs b/src/DevLauncher/Pipelines/Actions/InitializeLocalizationAction.cs
index 71caca3..58c5aa0 100644
--- a/src/DevLauncher/Pipelines/Actions/InitializeLocalizationAction.cs
+++ b/src/DevLauncher/Pipelines/Actions/InitializeLocalizationAction.cs
@@ -31,7 +31,7 @@ internal class InitializeLocalizationAction(IServiceProvider serviceProvider) :
protected override void RunAction(CancellationToken cancellationToken)
{
- Logger?.LogInformation($"Processing data '{EnglishDAT}'");
+ Logger?.LogInformation("Processing data '{DatFile}'", EnglishDAT);
var englishMtfPath = _fileSystem.Path.Combine("Data\\Text", EnglishDAT);
var englishMasterText = _datFileService.LoadAs(englishMtfPath, DatFileType.OrderedByCrc32).Content;
@@ -40,7 +40,7 @@ protected override void RunAction(CancellationToken cancellationToken)
foreach (var datFile in datFiles)
{
- Logger?.LogInformation($"Processing data '{_fileSystem.Path.GetFileName(datFile)}'");
+ Logger?.LogInformation("Processing data '{FileName}'", _fileSystem.Path.GetFileName(datFile));
var language = _localizationFileService.LanguageNameFromFileName(datFile.AsSpan());
diff --git a/src/DevLauncher/Pipelines/BuildAndRunPipeline.cs b/src/DevLauncher/Pipelines/BuildAndRunPipeline.cs
index c355f5a..68ba836 100644
--- a/src/DevLauncher/Pipelines/BuildAndRunPipeline.cs
+++ b/src/DevLauncher/Pipelines/BuildAndRunPipeline.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
using AnakinRaW.CommonUtilities.SimplePipeline;
using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
@@ -22,7 +23,7 @@ internal class BuildAndRunPipeline(
private readonly BuildSettings _buildSettings = buildSettings ?? throw new ArgumentNullException(nameof(buildSettings));
private readonly LaunchSettings _launchSettings = launchSettings ?? throw new ArgumentNullException(nameof(launchSettings));
- protected override Task> BuildSteps()
+ protected override Task> CreateRunnerSteps(CancellationToken token)
{
return Task.FromResult>(new List
{
diff --git a/src/DevLauncher/Pipelines/BuildAndVerifyPipeline.cs b/src/DevLauncher/Pipelines/BuildAndVerifyPipeline.cs
deleted file mode 100644
index ff08d20..0000000
--- a/src/DevLauncher/Pipelines/BuildAndVerifyPipeline.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using AET.ModVerify.Pipeline;
-using AET.ModVerify.Pipeline.Progress;
-using AET.ModVerify.Reporting;
-using AET.ModVerify.Reporting.Settings;
-using AET.ModVerify.Settings;
-using AnakinRaW.CommonUtilities.SimplePipeline;
-using AnakinRaW.CommonUtilities.SimplePipeline.Progress;
-using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
-using Microsoft.Extensions.DependencyInjection;
-using PG.StarWarsGame.Engine;
-using PG.StarWarsGame.Infrastructure.Games;
-using PG.StarWarsGame.Infrastructure.Mods;
-using RepublicAtWar.DevTools.Steps.Settings;
-
-namespace RepublicAtWar.DevLauncher.Pipelines;
-
-internal class BuildAndVerifyPipeline(IPhysicalMod mod, IGame fallbackGame, BuildSettings buildSettings, IServiceProvider serviceProvider)
- : SequentialPipeline(serviceProvider)
-{
- public override string ToString()
- {
- return $"Build & Verify {mod.Name}";
- }
-
- protected override Task> BuildSteps()
- {
- return Task.FromResult>(new List
- {
- new RunPipelineStep(new BuildPipeline(mod, buildSettings, ServiceProvider), ServiceProvider),
- new RunPipelineStep(CreateVerifyPipeline(), ServiceProvider),
- });
- }
-
- private GameVerifyPipeline CreateVerifyPipeline()
- {
- var gameLocations = new GameLocations(
- [mod.Directory.FullName],
- mod.Game.Directory.FullName,
- [fallbackGame.Directory.FullName, LauncherConstants.RaWFallbackAssetPathEaW]
- );
-
- var settings = new VerifyPipelineSettings
- {
- GameVerifySettings = GameVerifySettings.Default,
- VerifiersProvider = new DefaultGameVerifiersProvider()
- };
-
- var globalReportSettings = new GlobalVerifyReportSettings
- {
- Baseline = VerificationBaseline.Empty,
- Suppressions = SuppressionList.Empty
- };
-
- var gameEngineService = ServiceProvider.GetRequiredService();
-
- var engineErrorReporter = new ConcurrentGameEngineErrorReporter();
-
- var gameEngine = gameEngineService.InitializeAsync(
- GameEngineType.Foc,
- gameLocations,
- engineErrorReporter,
- null,
- false,
- CancellationToken.None).GetAwaiter().GetResult();
-
- return new GameVerifyPipeline(gameEngine, engineErrorReporter, settings, globalReportSettings, new NullVerifyProgressReporter(), ServiceProvider);
- }
-
- private class NullVerifyProgressReporter : IVerifyProgressReporter
- {
- public void Report(double progress, string? progressText, ProgressType type, VerifyProgressInfo detailedProgress)
- {
-
- }
- }
-}
\ No newline at end of file
diff --git a/src/DevLauncher/Pipelines/BuildPipeline.cs b/src/DevLauncher/Pipelines/BuildPipeline.cs
index 7106e48..c124398 100644
--- a/src/DevLauncher/Pipelines/BuildPipeline.cs
+++ b/src/DevLauncher/Pipelines/BuildPipeline.cs
@@ -1,15 +1,14 @@
using System;
using System.Collections.Generic;
-using System.IO.Abstractions;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using AET.Modinfo.Spec;
using AnakinRaW.CommonUtilities.SimplePipeline;
using AnakinRaW.CommonUtilities.SimplePipeline.Runners;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using PG.StarWarsGame.Engine;
-using PG.StarWarsGame.Engine.Localization;
+using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
using PG.StarWarsGame.Infrastructure.Mods;
+using RepublicAtWar.DevTools.Services;
using RepublicAtWar.DevTools.Steps.Build;
using RepublicAtWar.DevTools.Steps.Build.Meg;
using RepublicAtWar.DevTools.Steps.Build.Meg.Config;
@@ -17,62 +16,53 @@
namespace RepublicAtWar.DevLauncher.Pipelines;
-internal sealed class BuildPipeline(IPhysicalMod mod, BuildSettings settings, IServiceProvider serviceProvider)
- : Pipeline(serviceProvider)
+internal sealed class BuildPipeline : SequentialPipeline
{
- private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService();
+ private readonly RepublicAtWarService _republicAtWarService;
+ private readonly BuildSettings _settings;
+ private readonly IPhysicalMod _mod;
- private readonly IGameLanguageManager _languageManager = serviceProvider
- .GetRequiredService().GetLanguageManager(GameEngineType.Foc);
-
- private readonly BuildSettings _settings = settings ?? throw new ArgumentNullException(nameof(settings));
-
- private readonly List _buildSteps = new();
- private readonly List _preBuildSteps = new();
-
- private readonly ParallelStepRunner _buildRunner = new(4, serviceProvider);
- private readonly SequentialStepRunner _preBuildRunner = new(serviceProvider);
-
- protected override bool FailFast => true;
+ public BuildPipeline(IPhysicalMod mod, BuildSettings settings, IServiceProvider serviceProvider) : base(serviceProvider)
+ {
+ _mod = mod;
+ _settings = settings ?? throw new ArgumentNullException(nameof(settings));
+ _republicAtWarService = new RepublicAtWarService(serviceProvider);
+ FailFast = true;
+ }
public override string ToString()
{
- return $"Building {mod.Name}";
+ return $"Building {_mod.Name}";
}
- protected override Task PrepareCoreAsync()
+ protected override Task> CreateRunnerSteps(CancellationToken token)
{
- _preBuildSteps.Clear();
- _preBuildSteps.AddRange(CreatePreBuildSteps());
- foreach (var buildStep in _preBuildSteps)
- _preBuildRunner.AddStep(buildStep);
-
- _buildSteps.Clear();
- _buildSteps.AddRange(CreateBuildSteps());
- foreach (var buildStep in _buildSteps)
- _buildRunner.AddStep(buildStep);
-
- return Task.FromResult(true);
+ return Task.FromResult>(new List
+ {
+ new RunPipelineStep(new PreBuildPipeline(this, ServiceProvider), ServiceProvider),
+ new RunPipelineStep(new CoreBuildPipeline(this, ServiceProvider), ServiceProvider)
+ });
}
private IEnumerable CreateBuildSteps()
{
- yield return new PackMegFileStep(new RawAiPackMegConfiguration(mod, ServiceProvider), _settings, ServiceProvider);
- yield return new PackMegFileStep(new RawCustomMapsPackMegConfiguration(mod, ServiceProvider), _settings, ServiceProvider);
- yield return new PackMegFileStep(new RawNonLocalizedSfxMegConfiguration(mod, ServiceProvider), _settings, ServiceProvider);
+ yield return new PackMegFileStep(new RawAiPackMegConfiguration(_mod, ServiceProvider), _settings, ServiceProvider);
+ yield return new PackMegFileStep(new RawCustomMapsPackMegConfiguration(_mod, ServiceProvider), _settings, ServiceProvider);
+ yield return new PackMegFileStep(new RawNonLocalizedSfxMegConfiguration(_mod, ServiceProvider), _settings, ServiceProvider);
yield return new PackIconsStep(_settings, ServiceProvider);
yield return new CompileLocalizationStep(_settings, ServiceProvider);
-
- foreach (var focLanguage in _languageManager.SupportedLanguages)
+ yield return new LocalizeUnsupportedSpeechStep(_mod, _settings, ServiceProvider);
+
+ foreach (var supportedLanguage in _republicAtWarService.GetSupportedLanguages())
{
- var isRaWSupported = IsSupportedByRaw(focLanguage);
+ var hasSfxSupport = supportedLanguage.support.HasFlag(LanguageSupportLevel.SFX);
// There is no need to build non-supported languages if we don't do a release or force a clean build
- if (!isRaWSupported && !_settings.CleanBuild)
+ if (!hasSfxSupport && !_settings.CleanBuild)
continue;
yield return new PackMegFileStep(
- new RawLocalizedSfx2DMegConfiguration(focLanguage, isRaWSupported, mod, ServiceProvider),
+ new RawLocalizedSfx2DMegConfiguration(supportedLanguage.langauge, hasSfxSupport, _mod, ServiceProvider),
_settings,
ServiceProvider);
}
@@ -82,44 +72,30 @@ private IList CreatePreBuildSteps()
{
return new List
{
- new CleanOutdatedAssetsStep(mod, ServiceProvider)
+ new CleanOutdatedAssetsStep(_mod, ServiceProvider)
};
}
- protected override async Task RunCoreAsync(CancellationToken token)
+ private class CoreBuildPipeline(BuildPipeline parent, IServiceProvider serviceProvider)
+ : StepRunnerPipeline(serviceProvider)
{
- try
+ protected override Task> CreateRunnerSteps(CancellationToken token)
{
- Logger?.LogInformation("Running Prebuild...");
- _preBuildRunner.Error -= OnError;
- await _preBuildRunner.RunAsync(token);
+ return Task.FromResult>(parent.CreateBuildSteps().ToList());
}
- finally
- {
- Logger?.LogInformation("Finished Prebuild...");
- _preBuildRunner.Error -= OnError;
- }
-
- ThrowIfAnyStepsFailed(_preBuildSteps);
- try
- {
- Logger?.LogInformation("Running Build...");
- _buildRunner.Error -= OnError;
- await _buildRunner.RunAsync(token);
- }
- finally
+ protected override IStepRunner CreateRunner()
{
- Logger?.LogInformation("Finished Build...");
- _buildRunner.Error -= OnError;
+ return new AsyncStepRunner(4, ServiceProvider);
}
-
- ThrowIfAnyStepsFailed(_buildSteps);
}
-
- private bool IsSupportedByRaw(LanguageType focLanguage)
+
+ private class PreBuildPipeline(BuildPipeline parent, IServiceProvider serviceProvider)
+ : SequentialPipeline(serviceProvider)
{
- var path = _fileSystem.Path.Combine(mod.Directory.FullName, "Data/Audio/Units", focLanguage.ToString());
- return _fileSystem.Directory.Exists(path);
+ protected override Task> CreateRunnerSteps(CancellationToken token)
+ {
+ return Task.FromResult(parent.CreatePreBuildSteps());
+ }
}
}
\ No newline at end of file
diff --git a/src/DevLauncher/Pipelines/ReleaseRawPipeline.cs b/src/DevLauncher/Pipelines/ReleaseRawPipeline.cs
index 59fdf6e..1936d5b 100644
--- a/src/DevLauncher/Pipelines/ReleaseRawPipeline.cs
+++ b/src/DevLauncher/Pipelines/ReleaseRawPipeline.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
+using AET.Modinfo.Model;
using AnakinRaW.CommonUtilities.SimplePipeline;
using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
using Microsoft.Extensions.DependencyInjection;
@@ -11,6 +8,12 @@
using RepublicAtWar.DevLauncher.Utilities;
using RepublicAtWar.DevTools.Steps.Releasing;
using RepublicAtWar.DevTools.Steps.Settings;
+using Semver;
+using System;
+using System.Collections.Generic;
+using System.IO.Abstractions;
+using System.Threading;
+using System.Threading.Tasks;
namespace RepublicAtWar.DevLauncher.Pipelines;
@@ -21,9 +24,12 @@ internal class ReleaseRawPipeline : SequentialPipeline
private readonly ReleaseSettings _releaseSettings;
private readonly IPhysicalMod _republicAtWar;
private readonly IGame _empireAtWarGame;
+ private readonly IFileSystem _fileSystem;
private ProgressBarReporter? _progressBarReporter;
+ private SemVersion _modVersion = null!;
+
public ReleaseRawPipeline(
IPhysicalMod republicAtWar,
IGame empireAtWarGame,
@@ -37,33 +43,19 @@ public ReleaseRawPipeline(
_republicAtWar = republicAtWar ?? throw new ArgumentNullException(nameof(republicAtWar));
_empireAtWarGame = empireAtWarGame;
_logger = serviceProvider.GetService()?.CreateLogger(GetType());
- }
- protected override Task RunCoreAsync(CancellationToken token)
- {
- _logger?.LogInformation("Release Republic at War");
+ _fileSystem = serviceProvider.GetRequiredService();
- if (!_buildSettings.CleanBuild)
- {
- _logger?.LogWarning("Releasing without Clean build!!!");
- _logger?.LogWarning("Releasing without Clean build!!!");
- _logger?.LogWarning("Releasing without Clean build!!!");
- }
- return base.RunCoreAsync(token);
+ FailFast = true;
}
- protected override void DisposeResources()
+ protected override Task> CreateRunnerSteps(CancellationToken token)
{
- _progressBarReporter?.Dispose();
- _progressBarReporter = null;
- base.DisposeResources();
- }
+ _modVersion = SemVersion.Parse(_fileSystem.File.ReadAllText("version.txt"), SemVersionStyles.Strict);
- protected override Task> BuildSteps()
- {
return Task.Run>(() =>
{
- var createArtifactStep = new CreateUploadMetaArtifactsStep(ServiceProvider);
-
+ var createArtifactStep = new CreateUploadMetaArtifactsStep(_modVersion, ServiceProvider);
+
var copyStep = new CopyReleaseStep(createArtifactStep, _releaseSettings, ServiceProvider);
_progressBarReporter = new(copyStep);
@@ -73,11 +65,44 @@ protected override Task> BuildSteps()
new RunPipelineStep(new BuildPipeline(_republicAtWar, _buildSettings, ServiceProvider), ServiceProvider),
// Verify
// new RunPipelineStep(new VerifyPipeline(_options, _republicAtWar, _empireAtWarGame, ServiceProvider), ServiceProvider),
+
+ new VerifyLocalizationStep(_republicAtWar, _modVersion.IsPrerelease, ServiceProvider),
+
// Build Release artifacts
createArtifactStep,
// Copy to Release
copyStep
};
- });
+ }, CancellationToken.None);
+ }
+
+ protected override void OnExecuteStarted()
+ {
+ base.OnExecuteStarted();
+
+ _logger?.LogInformation("Releasing {Raw} v{Version}...", "Republic at War", _modVersion);
+
+ if (!_buildSettings.CleanBuild)
+ {
+ _logger?.LogWarning("Releasing without Clean build!!!");
+ _logger?.LogWarning("Releasing without Clean build!!!");
+ _logger?.LogWarning("Releasing without Clean build!!!");
+ }
+
+ if (_modVersion.IsPrerelease)
+ {
+ Console.ForegroundColor = ConsoleColor.DarkYellow;
+ Console.WriteLine("Building a preview version!!!");
+ Console.WriteLine("Building a preview version!!!");
+ Console.WriteLine("Building a preview version!!!");
+ Console.ResetColor();
+ }
+ }
+
+ protected override void DisposeResources()
+ {
+ _progressBarReporter?.Dispose();
+ _progressBarReporter = null;
+ base.DisposeResources();
}
}
\ No newline at end of file
diff --git a/src/DevLauncher/Pipelines/Steps/LaunchStep.cs b/src/DevLauncher/Pipelines/Steps/LaunchStep.cs
index cb60b1e..7d0d4b1 100644
--- a/src/DevLauncher/Pipelines/Steps/LaunchStep.cs
+++ b/src/DevLauncher/Pipelines/Steps/LaunchStep.cs
@@ -1,5 +1,6 @@
using System;
using System.Threading;
+using System.Threading.Tasks;
using AET.Modinfo.Model;
using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
using PG.StarWarsGame.Infrastructure.Clients.Arguments;
@@ -14,11 +15,12 @@ internal class LaunchStep(LaunchSettings options, IPhysicalMod mod, IServiceProv
{
private readonly IPhysicalMod _mod = mod ?? throw new ArgumentNullException(nameof(mod));
- protected override void RunCore(CancellationToken token)
+ protected override Task RunCoreAsync(CancellationToken token)
{
var launcher = new GameLauncher(options, _mod, Services);
var args = CreateGameArgs();
launcher.Launch(args);
+ return Task.CompletedTask;
}
private ArgumentCollection CreateGameArgs()
diff --git a/src/DevLauncher/Program.cs b/src/DevLauncher/Program.cs
index 9be543b..50944ba 100644
--- a/src/DevLauncher/Program.cs
+++ b/src/DevLauncher/Program.cs
@@ -1,5 +1,4 @@
-using AET.ModVerify.Reporting.Reporters;
-using AET.SteamAbstraction;
+using AET.SteamAbstraction;
using AnakinRaW.ApplicationBase;
using AnakinRaW.ApplicationBase.Environment;
using AnakinRaW.ApplicationBase.Update;
@@ -87,7 +86,7 @@ private async Task RunAppCoreAsync(string[] args, IServiceProvider appServi
{
var returnCode = await new RawDevLauncher(UpdatableApplicationEnvironment!, appServiceProvider)
.RunAsync(args);
- logger?.LogInformation($"RaW DevLauncher finished with code: {returnCode}");
+ logger?.LogInformation("RaW DevLauncher finished with code: {ExitCode}", returnCode);
return returnCode;
}
catch (Exception e)
@@ -107,16 +106,19 @@ private async Task RunAppCoreAsync(string[] args, IServiceProvider appServi
}
}
- protected override void ResetApp(Microsoft.Extensions.Logging.ILogger? logger)
+ protected override void ResetApp()
{
- logger?.LogDebug("Resetting Application");
+ Logger?.LogDebug("Resetting Application...");
+
+ base.ResetApp();
+
var deleteResult = ApplicationEnvironment.ApplicationLocalDirectory.TryDeleteWithRetry();
if (!deleteResult)
- logger?.LogWarning("Failed to delete application local directory.");
+ Logger?.LogWarning("Failed to delete application local directory.");
ApplicationEnvironment.ApplicationLocalDirectory.Create();
}
- protected override void CreateAppServices(IServiceCollection services, IReadOnlyCollection args)
+ protected override void CreateAppServices(IServiceCollection services, IReadOnlyList args)
{
var verboseLogging = false;
@@ -142,8 +144,6 @@ protected override void CreateAppServices(IServiceCollection services, IReadOnly
PetroglyphCommons.ContributeServices(services);
PetroglyphEngineServiceContribution.ContributeServices(services);
- services.RegisterJsonReporter();
- services.RegisterTextFileReporter();
services.AddSingleton(sp => new GitService(".", sp));
diff --git a/src/DevLauncher/Properties/launchSettings.json b/src/DevLauncher/Properties/launchSettings.json
index 98701b1..e517f5d 100644
--- a/src/DevLauncher/Properties/launchSettings.json
+++ b/src/DevLauncher/Properties/launchSettings.json
@@ -2,7 +2,7 @@
"profiles": {
"Build & Run Mod": {
"commandName": "Project",
- "commandLineArgs": "--verboseBootstrapLogging -verbose --skipUpdate",
+ "commandLineArgs": "--verboseBootstrapLogging -verbose",
"workingDirectory": "C:\\Privat\\Steam\\steamapps\\common\\Star Wars Empire at War\\corruption\\Mods\\republic-at-war"
},
"DAT2LocFile": {
@@ -17,18 +17,13 @@
},
"Release Republic at War": {
"commandName": "Project",
- "commandLineArgs": "release --uploaderDir D:\\Modding\\eawuploader --skipUpdate",
+ "commandLineArgs": "release --uploaderDir E:\\Modding\\eawuploader --skipUpdate",
"workingDirectory": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Star Wars Empire at War\\corruption\\Mods\\Republic_at_War"
},
"Merge Diffs into Localization": {
"commandName": "Project",
"commandLineArgs": "mergeLoc --skipUpdate",
"workingDirectory": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Star Wars Empire at War\\corruption\\Mods\\Republic_at_War"
- },
- "Build & Verify Republic at War": {
- "commandName": "Project",
- "commandLineArgs": "verify --skipUpdate",
- "workingDirectory": "C:\\Privat\\Steam\\steamapps\\common\\Star Wars Empire at War\\corruption\\Mods\\republic-at-war"
}
}
}
\ No newline at end of file
diff --git a/src/DevLauncher/RawDevLauncher.cs b/src/DevLauncher/RawDevLauncher.cs
index 17b3145..f74e6fb 100644
--- a/src/DevLauncher/RawDevLauncher.cs
+++ b/src/DevLauncher/RawDevLauncher.cs
@@ -69,7 +69,6 @@ private async Task UpdateLauncher(IReadOnlyList args)
typeof(PrepareLocalizationsOption),
typeof(MergeLocalizationOption),
typeof(ReleaseRepublicAtWarOption),
- typeof(VerifyOption)
];
var parseResult = _looseArgumentParser.ParseArguments(args, optionTypes);
@@ -106,9 +105,6 @@ private async Task RunCore(DevToolsOptionBase options)
case MergeLocalizationOption:
launcherPipeline = new MergeLocalizationsAction(serviceProvider);
break;
- case VerifyOption verifyOption:
- launcherPipeline = CreateBuildVerifyPipeline(verifyOption, gameFinderResult, serviceProvider);
- break;
default:
throw new ArgumentException($"The option '{options.GetType().FullName}' is not implemented",
nameof(options));
@@ -127,16 +123,6 @@ private async Task RunCore(DevToolsOptionBase options)
}
}
- private static IPipeline CreateBuildVerifyPipeline(VerifyOption options, GameFinderResult gameFinderResult, IServiceProvider services)
- {
- var buildSettings = new BuildSettings
- {
- WarnAsError = options.WarnAsError,
- CleanBuild = options.CleanBuild
- };
- return new BuildAndVerifyPipeline(gameFinderResult.RepublicAtWar, gameFinderResult.FallbackGame, buildSettings, services);
- }
-
private static IPipeline CreateBuildRunPipeline(BuildAndRunOption options, GameFinderResult gameFinderResult, IServiceProvider services)
{
var buildSettings = new BuildSettings
diff --git a/src/DevLauncher/Services/GitService.cs b/src/DevLauncher/Services/GitService.cs
index bc94c6b..894a8e7 100644
--- a/src/DevLauncher/Services/GitService.cs
+++ b/src/DevLauncher/Services/GitService.cs
@@ -91,7 +91,7 @@ public void Fetch()
};
- if (!Process.Start(startInfo)!.WaitForExit(3000))
+ if (!Process.Start(startInfo)!.WaitForExit(10000))
throw new InvalidOperationException("Unable to fetch from origin.");
}
}
\ No newline at end of file
diff --git a/src/DevLauncher/Update/RawDevLauncherUpdater.cs b/src/DevLauncher/Update/RawDevLauncherUpdater.cs
index 4785455..ac4d185 100644
--- a/src/DevLauncher/Update/RawDevLauncherUpdater.cs
+++ b/src/DevLauncher/Update/RawDevLauncherUpdater.cs
@@ -34,7 +34,7 @@ public async Task AutoUpdateApplication(ProductBranch branch)
{
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine($"Error while {currentAction}: {e.Message}");
- Logger?.LogError(e, $"Unable to check for updates: {e.Message}");
+ Logger?.LogError(e, "Unable to check for updates: {Message}", e.Message);
Console.ResetColor();
}
}
diff --git a/src/MegCompile/MegCompile.csproj b/src/MegCompile/MegCompile.csproj
index f0186c2..b319200 100644
--- a/src/MegCompile/MegCompile.csproj
+++ b/src/MegCompile/MegCompile.csproj
@@ -12,21 +12,22 @@
$(SolutionDir)RAW.ico
-
+ disable
+
-
-
-
-
-
-
+
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
diff --git a/src/MegCompile/PackSfxMegPipeline.cs b/src/MegCompile/PackSfxMegPipeline.cs
index f799c3f..d50d45f 100644
--- a/src/MegCompile/PackSfxMegPipeline.cs
+++ b/src/MegCompile/PackSfxMegPipeline.cs
@@ -1,47 +1,43 @@
-using System;
-using System.Collections.Generic;
-using System.IO.Abstractions;
-using System.Threading.Tasks;
+using AET.Modinfo.Spec;
using AnakinRaW.CommonUtilities.SimplePipeline;
-using Microsoft.Extensions.DependencyInjection;
-using PG.StarWarsGame.Engine;
-using PG.StarWarsGame.Engine.Localization;
+using AnakinRaW.CommonUtilities.SimplePipeline.Runners;
using PG.StarWarsGame.Infrastructure.Mods;
+using RepublicAtWar.DevTools.Services;
using RepublicAtWar.DevTools.Steps.Build.Meg;
using RepublicAtWar.DevTools.Steps.Build.Meg.Config;
using RepublicAtWar.DevTools.Steps.Settings;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
namespace RepublicAtWar.MegCompile;
-internal class PackSfxMegPipeline(IPhysicalMod mod, BuildSettings settings, IServiceProvider serviceProvider) : ParallelPipeline(serviceProvider, 2)
+internal class PackSfxMegPipeline(IPhysicalMod mod, BuildSettings settings, IServiceProvider serviceProvider)
+ : StepRunnerPipeline(serviceProvider)
{
- private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService();
-
- protected override Task> BuildSteps()
+ private readonly RepublicAtWarService _republicAtWarService = new(serviceProvider);
+
+ protected override IStepRunner CreateRunner()
{
- var languageManager = ServiceProvider.GetRequiredService()
- .GetLanguageManager(GameEngineType.Foc);
+ return new AsyncStepRunner(2, ServiceProvider);
+ }
+ protected override Task> CreateRunnerSteps(CancellationToken token)
+ {
IList steps = new List();
- foreach (var focLanguage in languageManager.SupportedLanguages)
+ foreach (var supportedLanguage in _republicAtWarService.GetSupportedLanguages())
{
- var isRaWSupported = IsSupportedByRaw(focLanguage);
+ var hasSfxSupport = supportedLanguage.support.HasFlag(LanguageSupportLevel.SFX);
// There is no need to build non-supported languages if we don't do a release or force a clean build
- if (!isRaWSupported)
+ if (!hasSfxSupport)
continue;
steps.Add(new PackMegFileStep(
- new RawLocalizedSfx2DMegConfiguration(focLanguage, isRaWSupported, mod, ServiceProvider), settings,
+ new RawLocalizedSfx2DMegConfiguration(supportedLanguage.langauge, hasSfxSupport, mod, ServiceProvider), settings,
ServiceProvider));
}
-
return Task.FromResult(steps);
}
-
- private bool IsSupportedByRaw(LanguageType focLanguage)
- {
- var path = _fileSystem.Path.Combine(mod.Directory.FullName, "Data/Audio/Units", focLanguage.ToString());
- return _fileSystem.Directory.Exists(path);
- }
}
\ No newline at end of file
diff --git a/src/MegCompile/Program.cs b/src/MegCompile/Program.cs
index 9f9ac26..c55a61c 100644
--- a/src/MegCompile/Program.cs
+++ b/src/MegCompile/Program.cs
@@ -48,7 +48,8 @@ static async Task Main()
private async Task Run()
{
var gameFinderResult = new ModFinderService(serviceProvider).FindAndAddModInCurrentDirectory();
- var pipeline = new PackSfxMegPipeline(gameFinderResult.RepublicAtWar, new BuildSettings { CleanBuild = true }, serviceProvider);
+ var pipeline = new PackSfxMegPipeline(gameFinderResult.RepublicAtWar,
+ new BuildSettings { CleanBuild = true }, serviceProvider);
await pipeline.RunAsync();
}
diff --git a/src/RawDevTools/Localization/LocalizationFileValidator.cs b/src/RawDevTools/Localization/LocalizationFileValidator.cs
index 1bf9a9c..5c0d5fe 100644
--- a/src/RawDevTools/Localization/LocalizationFileValidator.cs
+++ b/src/RawDevTools/Localization/LocalizationFileValidator.cs
@@ -121,7 +121,7 @@ public LanguageType GetLanguage(string language)
{
LogOrThrow($"Unrecognized language '{language}'");
// If we don't throw, we return English
- _logger?.LogWarning($"Language '{language}' is not supported. Fallback to English!");
+ _logger?.LogWarning("Language '{Language}' is not supported. Fallback to English!", language);
}
return languageType;
}
diff --git a/src/RawDevTools/RawDevTools.csproj b/src/RawDevTools/RawDevTools.csproj
index d5cccdf..b47feff 100644
--- a/src/RawDevTools/RawDevTools.csproj
+++ b/src/RawDevTools/RawDevTools.csproj
@@ -43,13 +43,13 @@
-
+
-
-
-
-
-
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/RawDevTools/Services/LocalizationFileService.cs b/src/RawDevTools/Services/LocalizationFileService.cs
index c98e411..0dd727d 100644
--- a/src/RawDevTools/Services/LocalizationFileService.cs
+++ b/src/RawDevTools/Services/LocalizationFileService.cs
@@ -19,8 +19,8 @@ namespace RepublicAtWar.DevTools.Services;
public class LocalizationFileService(IServiceProvider serviceProvider, bool warningAsError = false)
{
- private const string EnglishDAT = "MasterTextFile_English.DAT";
- private const string EnglishText = "MasterTextFile_English.txt";
+ internal const string EnglishDAT = "MasterTextFile_English.DAT";
+ internal const string EnglishText = "MasterTextFile_English.txt";
private readonly IServiceProvider _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService();
@@ -104,7 +104,8 @@ public void CompileLocalizationFile(LocalizationFile localizationFile, string da
{
var result = builder.AddEntry(entry.Key, entry.Value);
if (!result.Added)
- _logger?.LogWarning($"Unable to add KEY '{entry.Key}' to the DAT for language {localizationFile.Language}: {result.Message}");
+ _logger?.LogWarning("Unable to add KEY '{Key}' to the DAT for language {Language}: {Message}",
+ entry.Key, localizationFile.Language, result.Message);
}
builder.Build(new DatFileInformation { FilePath = _fileSystem.Path.GetFullPath(datFile) }, overwrite);
diff --git a/src/RawDevTools/Services/ModFinderService.cs b/src/RawDevTools/Services/ModFinderService.cs
index 540b88b..3363ba5 100644
--- a/src/RawDevTools/Services/ModFinderService.cs
+++ b/src/RawDevTools/Services/ModFinderService.cs
@@ -52,7 +52,8 @@ public GameFinderResult FindAndAddModInCurrentDirectory()
if (focDetectionResult.GameLocation is null)
throw new GameException("Unable to find game installation: Wrong install path?");
- _logger?.LogInformation($"Found game {focDetectionResult.GameIdentity} at '{focDetectionResult.GameLocation.FullName}'");
+ _logger?.LogInformation("Found game {GameIdentity} at '{Name}'",
+ focDetectionResult.GameIdentity, focDetectionResult.GameLocation.FullName);
var foc = _gameFactory.CreateGame(focDetectionResult, CultureInfo.InvariantCulture);
@@ -69,7 +70,8 @@ public GameFinderResult FindAndAddModInCurrentDirectory()
var eawDetectionResult = gd.Detect(GameType.Eaw);
if (eawDetectionResult.GameLocation is null)
throw new GameException("Unable to find Empire at War installation.");
- _logger?.LogInformation($"Found game {eawDetectionResult.GameIdentity} at '{eawDetectionResult.GameLocation.FullName}'");
+ _logger?.LogInformation("Found game {GameIdentity} at '{Name}'",
+ eawDetectionResult.GameIdentity, eawDetectionResult.GameLocation.FullName);
var eaw = _gameFactory.CreateGame(eawDetectionResult, CultureInfo.InvariantCulture);
diff --git a/src/RawDevTools/Services/RepublicAtWarService.cs b/src/RawDevTools/Services/RepublicAtWarService.cs
new file mode 100644
index 0000000..383d086
--- /dev/null
+++ b/src/RawDevTools/Services/RepublicAtWarService.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using AET.Modinfo.Spec;
+using Microsoft.Extensions.DependencyInjection;
+using PG.StarWarsGame.Engine;
+using PG.StarWarsGame.Engine.Localization;
+
+namespace RepublicAtWar.DevTools.Services;
+
+public sealed class RepublicAtWarService(IServiceProvider serviceProvider)
+{
+ private readonly IGameLanguageManager _languageManager = serviceProvider
+ .GetRequiredService().GetLanguageManager(GameEngineType.Foc);
+
+ public IEnumerable<(LanguageType langauge, LanguageSupportLevel support)> GetSupportedLanguages()
+ {
+ var supportedLanguages = _languageManager.SupportedLanguages;
+
+ foreach (var language in supportedLanguages)
+ {
+ if (language is LanguageType.English or LanguageType.German)
+ yield return (language, LanguageSupportLevel.FullLocalized);
+ else
+ yield return (language, LanguageSupportLevel.Text);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RawDevTools/Steps/Build/CleanOutdatedAssetsStep.cs b/src/RawDevTools/Steps/Build/CleanOutdatedAssetsStep.cs
index af12d65..dff76f6 100644
--- a/src/RawDevTools/Steps/Build/CleanOutdatedAssetsStep.cs
+++ b/src/RawDevTools/Steps/Build/CleanOutdatedAssetsStep.cs
@@ -1,8 +1,12 @@
using System;
using System.IO.Abstractions;
using System.Threading;
+using System.Threading.Tasks;
using AnakinRaW.CommonUtilities.FileSystem;
using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
+using AET.Modinfo.Spec;
+using PG.StarWarsGame.Engine.Localization;
+using RepublicAtWar.DevTools.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.Extensions.Logging;
@@ -15,18 +19,38 @@ public class CleanOutdatedAssetsStep(IPhysicalMod mod, IServiceProvider serviceP
{
private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService();
- protected override void RunCore(CancellationToken token)
+ protected override Task RunCoreAsync(CancellationToken token)
{
- Logger?.LogInformation("Cleaning outdated assets...");
- var matcher = new Matcher();
- matcher.AddInclude("Data/Audio/SFX/sfx2d_*.meg");
-
- foreach (var fileToDelete in matcher.GetResultsInFullPath(mod.Directory.FullName))
+ return Task.Run(() =>
{
- Logger?.LogDebug($"Deleting old asset '{fileToDelete}'");
- _fileSystem.File.DeleteWithRetry(fileToDelete);
- }
+ Logger?.LogInformation("Cleaning outdated assets...");
+ var matcher = new Matcher();
+ matcher.AddInclude("Data/Audio/SFX/sfx2d_*.meg");
+
+ foreach (var fileToDelete in matcher.GetResultsInFullPath(mod.Directory.FullName))
+ {
+ Logger?.LogDebug("Deleting old asset '{File}'", fileToDelete);
+ _fileSystem.File.DeleteWithRetry(fileToDelete);
+ }
+
+ var republicAtWarService = new RepublicAtWarService(Services);
+ foreach (var (language, support) in republicAtWarService.GetSupportedLanguages())
+ {
+ if (language == LanguageType.English)
+ continue;
+
+ if (support.HasFlag(LanguageSupportLevel.Speech))
+ continue;
+
+ var targetDir = _fileSystem.Path.Combine(mod.Directory.FullName, "Data\\Audio\\Speech", language.ToString());
+ if (_fileSystem.Directory.Exists(targetDir))
+ {
+ Logger?.LogDebug("Deleting (unsupported) localized speech directory '{Directory}'", targetDir);
+ _fileSystem.Directory.Delete(targetDir, true);
+ }
+ }
- Logger?.LogInformation("Finished cleaning outdated assets.");
+ Logger?.LogInformation("Finished cleaning outdated assets.");
+ }, CancellationToken.None);
}
}
\ No newline at end of file
diff --git a/src/RawDevTools/Steps/Build/CompileLocalizationStep.cs b/src/RawDevTools/Steps/Build/CompileLocalizationStep.cs
index 35f5275..5262dfb 100644
--- a/src/RawDevTools/Steps/Build/CompileLocalizationStep.cs
+++ b/src/RawDevTools/Steps/Build/CompileLocalizationStep.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO.Abstractions;
using System.Threading;
+using System.Threading.Tasks;
using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -15,12 +16,16 @@ public class CompileLocalizationStep(BuildSettings settings, IServiceProvider se
private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService();
private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(CompileLocalizationStep));
- protected override void RunCore(CancellationToken token)
+ protected override Task RunCoreAsync(CancellationToken token)
{
- var localizationFiles = _fileSystem.Directory.EnumerateFiles("Data\\Text", "MasterTextFile_*.txt");
+ return Task.Run(() =>
+ {
+ var localizationFiles = _fileSystem.Directory
+ .EnumerateFiles("Data\\Text", "MasterTextFile_*.txt");
- foreach (var localizationFile in localizationFiles)
- CompileDatFromLocalizationFile(localizationFile);
+ foreach (var localizationFile in localizationFiles)
+ CompileDatFromLocalizationFile(localizationFile);
+ }, CancellationToken.None);
}
private void CompileDatFromLocalizationFile(string file)
@@ -31,11 +36,11 @@ private void CompileDatFromLocalizationFile(string file)
var updateChecker = new TimeStampBasesUpdateChecker(settings.CleanBuild, Services);
if (!settings.CleanBuild && !updateChecker.RequiresUpdate(datFilePath, new List { file }))
{
- _logger?.LogDebug($"DAT data '{datFileName}' is already up to date. Skipping build.");
+ _logger?.LogDebug("DAT data '{DatFile}' is already up to date. Skipping build.", datFileName);
return;
}
- _logger?.LogInformation($"Writing DAT data '{datFileName}'...");
+ _logger?.LogInformation("Writing DAT data '{DatFile}'...", datFileName);
var locFileService = new LocalizationFileService(Services, settings.WarnAsError);
@@ -46,6 +51,6 @@ private void CompileDatFromLocalizationFile(string file)
locFileService.CompileLocalizationFile(localizationFile, datFilePath, true);
- _logger?.LogInformation($"Finished writing DAT data for language {localizationFile.Language}");
+ _logger?.LogInformation("Finished writing DAT data for language {Language}", localizationFile.Language);
}
}
\ No newline at end of file
diff --git a/src/RawDevTools/Steps/Build/LocalizeUnsupportedSpeechStep.cs b/src/RawDevTools/Steps/Build/LocalizeUnsupportedSpeechStep.cs
new file mode 100644
index 0000000..81fab7b
--- /dev/null
+++ b/src/RawDevTools/Steps/Build/LocalizeUnsupportedSpeechStep.cs
@@ -0,0 +1,78 @@
+using System;
+using System.IO.Abstractions;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using AET.Modinfo.Spec;
+using AnakinRaW.CommonUtilities.FileSystem;
+using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using PG.StarWarsGame.Engine;
+using PG.StarWarsGame.Engine.Localization;
+using PG.StarWarsGame.Infrastructure;
+using RepublicAtWar.DevTools.Services;
+using RepublicAtWar.DevTools.Steps.Settings;
+
+namespace RepublicAtWar.DevTools.Steps.Build;
+
+public class LocalizeUnsupportedSpeechStep(IPhysicalPlayableObject physicalGameObject, BuildSettings settings, IServiceProvider serviceProvider)
+ : PipelineStep(serviceProvider)
+{
+ private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService();
+ private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(LocalizeUnsupportedSpeechStep));
+ private readonly RepublicAtWarService _republicAtWarService = new(serviceProvider);
+ private readonly IGameLanguageManager _gameLanguageManager = serviceProvider.GetRequiredService()
+ .GetLanguageManager(GameEngineType.Foc);
+
+ protected override Task RunCoreAsync(CancellationToken token)
+ {
+ return Task.Run(() =>
+ {
+ var languages = _republicAtWarService.GetSupportedLanguages().ToList();
+ var englishSpeechDir = _fileSystem.Path.Combine(physicalGameObject.Directory.FullName, "Data\\Audio\\Speech\\English");
+
+ if (!_fileSystem.Directory.Exists(englishSpeechDir))
+ throw new InvalidOperationException("English speech directory not found. Skipping localization.");
+
+ foreach (var (language, support) in languages)
+ {
+ if (language == LanguageType.English)
+ continue;
+
+ if (support.HasFlag(LanguageSupportLevel.Speech))
+ continue;
+
+ LocalizeSpeechFromEnglish(language, englishSpeechDir);
+ }
+ }, CancellationToken.None);
+ }
+
+ private void LocalizeSpeechFromEnglish(LanguageType language, string englishSpeechDir)
+ {
+ var targetDir = _fileSystem.Path.Combine(physicalGameObject.Directory.FullName, "Data\\Audio\\Speech", language.ToString());
+
+ _logger?.LogInformation("Localizing speech for language {Language} using English language files...", language);
+
+ var overwriteOption = settings.CleanBuild
+ ? DirectoryOverwriteOption.CleanOverwrite
+ : DirectoryOverwriteOption.MergeOverwrite;
+
+ _fileSystem.DirectoryInfo.New(englishSpeechDir).Copy(targetDir, null, overwriteOption);
+
+ var speechFiles = _fileSystem.Directory.GetFiles(targetDir, "*.mp3");
+ foreach (var speechFile in speechFiles)
+ {
+ var fileName = _fileSystem.Path.GetFileName(speechFile);
+ var localizedFileName = _gameLanguageManager.LocalizeFileName(fileName, language, out var localized);
+
+ if (!localized)
+ continue;
+
+ var targetPath = _fileSystem.Path.Combine(targetDir, localizedFileName);
+ _fileSystem.File.Move(speechFile, targetPath);
+ }
+
+ _logger?.LogInformation("Finished localizing speech for language {Language}", language);
+ }
+}
diff --git a/src/RawDevTools/Steps/Build/Meg/Config/RawLocalizedSFX2DMegConfiguration.cs b/src/RawDevTools/Steps/Build/Meg/Config/RawLocalizedSFX2DMegConfiguration.cs
index 65a22e7..491cde6 100644
--- a/src/RawDevTools/Steps/Build/Meg/Config/RawLocalizedSFX2DMegConfiguration.cs
+++ b/src/RawDevTools/Steps/Build/Meg/Config/RawLocalizedSFX2DMegConfiguration.cs
@@ -54,7 +54,7 @@ private IEnumerable GetFilesToPack()
if (IsLanguageSupported)
throw new DirectoryNotFoundException($"Unable to find SFX directory: '{path}'");
- Logger?.LogDebug($"Unsupported Language {_language} - Switching to English");
+ Logger?.LogDebug("Unsupported Language {Language} - Switching to English", _language);
path = $"Data\\Audio\\Units\\{LanguageType.English}";
}
@@ -71,7 +71,7 @@ private string LocalizeFileName(string fileName)
{
var newFileName = _gameLanguageManager.LocalizeFileName(fileName, _language, out var localized);
if (!localized)
- Logger?.LogWarning($"Unable to localize file '{fileName}'");
+ Logger?.LogWarning("Unable to localize file '{File}'", fileName);
return newFileName;
}
}
\ No newline at end of file
diff --git a/src/RawDevTools/Steps/Build/Meg/PackMegFileStep.cs b/src/RawDevTools/Steps/Build/Meg/PackMegFileStep.cs
index 1cf5a15..866b3be 100644
--- a/src/RawDevTools/Steps/Build/Meg/PackMegFileStep.cs
+++ b/src/RawDevTools/Steps/Build/Meg/PackMegFileStep.cs
@@ -3,6 +3,7 @@
using System.IO.Abstractions;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileSystemGlobbing;
@@ -25,7 +26,12 @@ public class PackMegFileStep(IPackMegConfiguration config, BuildSettings setting
private readonly IPackMegConfiguration _config = config ?? throw new ArgumentNullException(nameof(config));
- protected override void RunCore(CancellationToken token)
+ protected override Task RunCoreAsync(CancellationToken token)
+ {
+ return Task.Run(() => RunCore(token), CancellationToken.None);
+ }
+
+ private void RunCore(CancellationToken token)
{
var matcher = new Matcher(StringComparison.OrdinalIgnoreCase);
foreach (var fileToPack in _config.FilesToPack)
@@ -42,11 +48,11 @@ protected override void RunCore(CancellationToken token)
if (!settings.CleanBuild && !updateChecker.RequiresUpdate(megFilePath, files))
{
- _logger?.LogDebug($"MEG data '{megFileName}' is already up to date. Skipping build.");
+ _logger?.LogDebug("MEG data '{MegFile}' is already up to date. Skipping build.", megFileName);
return;
}
- _logger?.LogInformation($"Writing MEG data '{megFileName}'...");
+ _logger?.LogInformation("Writing MEG data '{MegFile}'...", megFileName);
using var megBuilder = new EmpireAtWarMegBuilder(_config.VirtualRootDirectory.FullName, _serviceProvider);
@@ -70,6 +76,6 @@ protected override void RunCore(CancellationToken token)
}
megBuilder.Build(new MegFileInformation(megFilePath, MegFileVersion.V1), true);
- _logger?.LogInformation($"Finished writing MEG data '{megFileName}'...");
+ _logger?.LogInformation("Finished writing MEG data '{MegFile}'...", megFileName);
}
}
\ No newline at end of file
diff --git a/src/RawDevTools/Steps/Build/PackIconsStep.cs b/src/RawDevTools/Steps/Build/PackIconsStep.cs
index 855191f..846e756 100644
--- a/src/RawDevTools/Steps/Build/PackIconsStep.cs
+++ b/src/RawDevTools/Steps/Build/PackIconsStep.cs
@@ -5,6 +5,7 @@
using System.IO.Abstractions;
using System.Reflection;
using System.Threading;
+using System.Threading.Tasks;
using AnakinRaW.CommonUtilities;
using AnakinRaW.CommonUtilities.FileSystem;
using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
@@ -28,7 +29,12 @@ public class PackIconsStep(BuildSettings settings, IServiceProvider serviceProvi
private const string DummyMasterTextFileXml = "Data\\Text\\MasterTextFile.xml";
private const string ModCompileExe = "ModCompile.exe";
- protected override void RunCore(CancellationToken token)
+ protected override Task RunCoreAsync(CancellationToken token)
+ {
+ return Task.Run(() => RunCore(token), CancellationToken.None);
+ }
+
+ private void RunCore(CancellationToken token)
{
if (!_fileSystem.Directory.Exists(IconsDirectory))
{
diff --git a/src/RawDevTools/Steps/Releasing/CopyReleaseStep.cs b/src/RawDevTools/Steps/Releasing/CopyReleaseStep.cs
index 6aa4734..32bda17 100644
--- a/src/RawDevTools/Steps/Releasing/CopyReleaseStep.cs
+++ b/src/RawDevTools/Steps/Releasing/CopyReleaseStep.cs
@@ -75,10 +75,10 @@ private Matcher CreateBlacklist()
return matcher;
}
- protected override void RunCore(CancellationToken token)
+ protected override async Task RunCoreAsync(CancellationToken token)
{
- _buildArtifactsStep.Wait();
-
+ await _buildArtifactsStep;
+
_logger?.LogInformation("Copying Release to SteamUploader ...");
if (!_fileSystem.Directory.Exists(_settings.UploaderDirectory))
@@ -101,17 +101,14 @@ protected override void RunCore(CancellationToken token)
var steamJsonFile = _buildArtifactsStep.SteamJsonName;
_fileSystem.File.Copy(steamJsonFile, _fileSystem.Path.Combine(uploaderWsContentPath, steamJsonFile), true);
- Task.Run(async () =>
- {
- await new DirectoryCopier(_fileSystem).CopyDirectoryAsync(source,
- assetCopyPath,
- new CopyProgress(this),
- ShallCopyFile, 4,
- token);
- }, CancellationToken.None)
- .Wait(token);
-
- _logger?.LogInformation($"Copied assets to SteamUploader at '{assetCopyPath}'");
+ await new DirectoryCopier(_fileSystem).CopyDirectoryAsync(source,
+ assetCopyPath,
+ new CopyProgress(this),
+ ShallCopyFile,
+ 4,
+ token);
+
+ _logger?.LogInformation("Copied assets to SteamUploader at '{Path}'", assetCopyPath);
}
private bool ShallCopyFile(string fileToCopy)
diff --git a/src/RawDevTools/Steps/Releasing/CreateUploadMetaArtifactsStep.cs b/src/RawDevTools/Steps/Releasing/CreateUploadMetaArtifactsStep.cs
index d139674..b394a94 100644
--- a/src/RawDevTools/Steps/Releasing/CreateUploadMetaArtifactsStep.cs
+++ b/src/RawDevTools/Steps/Releasing/CreateUploadMetaArtifactsStep.cs
@@ -3,16 +3,18 @@
using System.IO.Abstractions;
using System.Text.RegularExpressions;
using System.Threading;
+using System.Threading.Tasks;
using AET.Modinfo.Model;
using AET.Modinfo.Spec;
using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Semver;
+using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace RepublicAtWar.DevTools.Steps.Releasing;
-public class CreateUploadMetaArtifactsStep(IServiceProvider serviceProvider) : SynchronizedStep(serviceProvider)
+public class CreateUploadMetaArtifactsStep(SemVersion version, IServiceProvider serviceProvider) : PipelineStep(serviceProvider)
{
private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService();
private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(CreateUploadMetaArtifactsStep));
@@ -23,24 +25,24 @@ public class CreateUploadMetaArtifactsStep(IServiceProvider serviceProvider) : S
internal string SteamJsonName { get; private set; } = null!;
- protected override void RunSynchronized(CancellationToken token)
+ protected override Task RunCoreAsync(CancellationToken token)
+ {
+ return Task.Run(() => RunCore(token), CancellationToken.None);
+ }
+
+ private void RunCore(CancellationToken token)
{
_logger?.LogInformation("Creating Modinfo, Steam json and splashes...");
- var version = SemVersion.Parse(_fileSystem.File.ReadAllText("version.txt"), SemVersionStyles.Strict);
_replacementVariables.Add("version", version.ToString());
_replacementVariables.Add("version-minor", ToMinorOnly(version));
var baseInfo = ModinfoData.Parse(_fileSystem.File.ReadAllText("modinfo-base.json"));
-
+
IModinfo releaseInfo;
string steamDescription;
if (version.IsPrerelease)
{
- Console.WriteLine("Building a preview version!!!");
- Console.WriteLine("Building a preview version!!!");
- Console.WriteLine("Building a preview version!!!");
-
releaseInfo = ModinfoData.Parse(_fileSystem.File.ReadAllText("modinfo-beta.json"));
steamDescription = _fileSystem.File.ReadAllText("SteamText-Beta.txt");
_fileSystem.File.Copy("splash-beta.png", "splash.png", true);
diff --git a/src/RawDevTools/Steps/Releasing/VerifyLocalizationStep.cs b/src/RawDevTools/Steps/Releasing/VerifyLocalizationStep.cs
new file mode 100644
index 0000000..089a18d
--- /dev/null
+++ b/src/RawDevTools/Steps/Releasing/VerifyLocalizationStep.cs
@@ -0,0 +1,316 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Abstractions;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using PG.StarWarsGame.Engine;
+using PG.StarWarsGame.Engine.Localization;
+using PG.StarWarsGame.Files.DAT.Data;
+using PG.StarWarsGame.Files.DAT.Files;
+using PG.StarWarsGame.Files.DAT.Services;
+using PG.StarWarsGame.Files.MEG.Data.Archives;
+using PG.StarWarsGame.Files.MEG.Services;
+using PG.StarWarsGame.Infrastructure.Mods;
+using RepublicAtWar.DevTools.Services;
+
+namespace RepublicAtWar.DevTools.Steps.Releasing;
+
+public class VerifyLocalizationStep(
+ IPhysicalMod republicAtWar,
+ bool isPrerelease,
+ IServiceProvider serviceProvider) : PipelineStep(serviceProvider)
+{
+ private readonly ILogger? _logger =
+ serviceProvider.GetService()?.CreateLogger(typeof(VerifyLocalizationStep));
+
+ private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService();
+ private readonly IDatFileService _datFileService = serviceProvider.GetRequiredService();
+ private readonly IDatModelService _datModelService = serviceProvider.GetRequiredService();
+ private readonly IMegFileService _megFileService = serviceProvider.GetRequiredService();
+ private readonly RepublicAtWarService _republicAtWarService = new(serviceProvider);
+ private readonly IGameLanguageManager _languageManager = serviceProvider
+ .GetRequiredService().GetLanguageManager(GameEngineType.Foc);
+
+
+ protected override Task RunCoreAsync(CancellationToken token)
+ {
+ return Task.Run(() => RunCore(token), CancellationToken.None);
+ }
+
+ private void RunCore(CancellationToken token)
+ {
+ _logger?.LogInformation("Verifying localization...");
+
+ var reportBuilder = new StringBuilder();
+ var hasErrors = false;
+
+ var supportedLanguages = _republicAtWarService.GetSupportedLanguages()
+ .Select(x => x.langauge)
+ .ToList();
+
+ hasErrors |= VerifyText(reportBuilder, supportedLanguages, token);
+ hasErrors |= VerifySpeech(reportBuilder, supportedLanguages, token);
+ hasErrors |= VerifySfx(reportBuilder, supportedLanguages, token);
+
+ var reportPath = _fileSystem.Path.Combine(republicAtWar.Directory.FullName, "localization_verification.txt");
+ if (!hasErrors)
+ {
+ if (_fileSystem.File.Exists(reportPath))
+ _fileSystem.File.Delete(reportPath);
+ _logger?.LogInformation("Localization verification passed.");
+ return;
+ }
+
+ _fileSystem.File.WriteAllText(reportPath, reportBuilder.ToString());
+
+ LogOrThrow(reportPath);
+ }
+
+ private void LogOrThrow(string reportPath)
+ {
+ if (!isPrerelease)
+ throw new InvalidOperationException("Localization verification failed. See localization_verification.txt for details.");
+ _logger?.LogWarning("Localization verification failed. Report written to {ReportPath}", reportPath);
+ }
+
+ private bool VerifyText(
+ StringBuilder reportBuilder,
+ IReadOnlyCollection supportedLanguages,
+ CancellationToken token)
+ {
+ var hasErrors = false;
+ var textDir = _fileSystem.Path.Combine(republicAtWar.Directory.FullName, "Data", "Text");
+ if (!_fileSystem.Directory.Exists(textDir))
+ {
+ reportBuilder.AppendLine($"Text directory not found: {textDir}");
+ return false;
+ }
+
+ var englishDatModel = GetDatModel(LanguageType.English);
+
+ if (englishDatModel == null)
+ throw new FileNotFoundException($"Missing required English reference file: MasterTextFile_English.dat in {textDir}");
+
+ token.ThrowIfCancellationRequested();
+
+ foreach (var language in supportedLanguages)
+ {
+ if (language == LanguageType.English)
+ continue;
+
+ var langDatModel = GetDatModel(language);
+
+ if (langDatModel == null)
+ {
+ reportBuilder.AppendLine($"Missing DAT file for {language}: MasterTextFile_{language}.dat");
+ hasErrors = true;
+ continue;
+ }
+
+ var missingKeys = _datModelService.GetMissingKeysFromBase(englishDatModel, langDatModel).ToList();
+ if (missingKeys.Any())
+ {
+ reportBuilder.AppendLine($"Language {language} is missing {missingKeys.Count} keys from MasterTextFile_{language}.dat:");
+ foreach (var key in missingKeys)
+ reportBuilder.AppendLine($" - {key}");
+ hasErrors = true;
+ }
+
+ var additionalKeys = _datModelService.GetMissingKeysFromBase(langDatModel, englishDatModel).ToList();
+ if (additionalKeys.Any())
+ {
+ reportBuilder.AppendLine($"Language {language} has {additionalKeys.Count} additional keys in MasterTextFile_{language}.dat (not in English):");
+ foreach (var key in additionalKeys)
+ reportBuilder.AppendLine($" - {key}");
+ hasErrors = true;
+ }
+ }
+ return hasErrors;
+ }
+
+ private IDatModel? GetDatModel(LanguageType language)
+ {
+ var textDir = _fileSystem.Path.Combine(republicAtWar.Directory.FullName, "Data", "Text");
+ if (!_fileSystem.Directory.Exists(textDir))
+ return null;
+
+ var expectedFileName = $"MasterTextFile_{language}.dat";
+
+ var filePath = _fileSystem.Directory.EnumerateFiles(textDir, "*.dat")
+ .FirstOrDefault(f => _fileSystem.Path.GetFileName(f).Equals(expectedFileName, StringComparison.OrdinalIgnoreCase));
+
+ return filePath == null ? null : _datFileService.LoadAs(filePath, DatFileType.OrderedByCrc32).Content;
+ }
+
+ private bool VerifySpeech(StringBuilder reportBuilder,
+ IReadOnlyCollection supportedLanguages,
+ CancellationToken token)
+ {
+ var speechDir = _fileSystem.Path.Combine(republicAtWar.Directory.FullName, "Data", "Audio", "Speech");
+ var englishSpeechDir = _fileSystem.Path.Combine(speechDir, "English");
+
+ if (!_fileSystem.Directory.Exists(englishSpeechDir))
+ throw new DirectoryNotFoundException($"Missing required English reference speech directory: {englishSpeechDir}");
+
+ var englishFiles = _fileSystem.Directory.GetFiles(englishSpeechDir, "*", SearchOption.TopDirectoryOnly)
+ .Select(f => _fileSystem.Path.GetFileName(f))
+ .ToList();
+
+ var hasErrors = VerifyEnglishFilesLocalizable(reportBuilder, englishFiles, "Speech");
+
+ var nonMp3Files = englishFiles.Where(f => !_fileSystem.Path.GetExtension(f).Equals(".mp3", StringComparison.OrdinalIgnoreCase)).ToList();
+ if (nonMp3Files.Any())
+ {
+ reportBuilder.AppendLine("The following English files in Speech are not .mp3 files:");
+ foreach (var f in nonMp3Files)
+ reportBuilder.AppendLine($" - {f}");
+ hasErrors = true;
+ }
+
+ foreach (var language in supportedLanguages)
+ {
+ if (language == LanguageType.English)
+ continue;
+
+ token.ThrowIfCancellationRequested();
+
+ var langSpeechDir = _fileSystem.Path.Combine(speechDir, language.ToString());
+ if (!_fileSystem.Directory.Exists(langSpeechDir))
+ {
+ reportBuilder.AppendLine($"Missing speech directory for {language}: {langSpeechDir}");
+ hasErrors = true;
+ continue;
+ }
+
+ var langFiles = new HashSet(_fileSystem.Directory.GetFiles(langSpeechDir, "*", SearchOption.TopDirectoryOnly)
+ .Select(f => _fileSystem.Path.GetFileName(f)), StringComparer.OrdinalIgnoreCase);
+
+ hasErrors |= VerifyFileParity(reportBuilder, language, englishFiles, langFiles, langSpeechDir);
+ }
+
+ return hasErrors;
+ }
+
+ private bool VerifyEnglishFilesLocalizable(StringBuilder reportBuilder, IEnumerable englishFiles, string category)
+ {
+ var nonLocalizable = englishFiles.Where(f => !_languageManager.IsFileNameLocalizable(f, true)).ToList();
+ if (nonLocalizable.Any())
+ {
+ reportBuilder.AppendLine($"The following English files in {category} are not localizable and should be renamed:");
+ foreach (var f in nonLocalizable)
+ reportBuilder.AppendLine($" - {f}");
+ return true;
+ }
+ return false;
+ }
+
+ private bool VerifyFileParity(
+ StringBuilder reportBuilder,
+ LanguageType language,
+ IEnumerable englishFiles,
+ HashSet langFiles,
+ string location)
+ {
+ var hasErrors = false;
+ var missingInLang = new List();
+ var expectedLocalizedNames = new HashSet(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var englishFile in englishFiles)
+ {
+ var localizedName = _languageManager.LocalizeFileName(englishFile, language, out _);
+ expectedLocalizedNames.Add(localizedName);
+ if (!langFiles.Contains(localizedName))
+ missingInLang.Add(localizedName);
+ }
+
+ if (missingInLang.Any())
+ {
+ reportBuilder.AppendLine($"Language {language} has {missingInLang.Count} missing/mismatching files in {location}.");
+ foreach (var missingFile in missingInLang)
+ reportBuilder.AppendLine($" - {missingFile}");
+ hasErrors = true;
+ }
+
+ var additionalInLang = langFiles.Where(f => !expectedLocalizedNames.Contains(f)).ToList();
+ if (additionalInLang.Any())
+ {
+ reportBuilder.AppendLine($"Language {language} has {additionalInLang.Count} additional files in {location} (not in English):");
+ foreach (var extraFile in additionalInLang)
+ reportBuilder.AppendLine($" - {extraFile}");
+ hasErrors = true;
+ }
+
+ return hasErrors;
+ }
+
+ private bool VerifySfx(StringBuilder reportBuilder,
+ IReadOnlyCollection supportedLanguages,
+ CancellationToken token)
+ {
+ var englishMeg = GetMegArchive("Data/Audio/SFX/voices_English.meg");
+ if (englishMeg == null)
+ throw new FileNotFoundException("Missing required English reference SFX MEG: Data/Audio/SFX/voices_English.meg");
+
+ return VerifyMegInternal(reportBuilder, supportedLanguages, englishMeg, l => $"Data/Audio/SFX/voices_{l}.meg", token);
+ }
+
+ private bool VerifyMegInternal(StringBuilder reportBuilder,
+ IReadOnlyCollection supportedLanguages,
+ IMegArchive englishArchive,
+ Func megPathFunc,
+ CancellationToken token)
+ {
+ var englishFiles = englishArchive.Select(entry => entry.Path).ToList();
+
+ var hasErrors = VerifyEnglishFilesLocalizable(reportBuilder, englishFiles, "SFX");
+
+ var nonWavFiles = englishFiles.Where(f => !_fileSystem.Path.GetExtension(f).Equals(".wav", StringComparison.OrdinalIgnoreCase)).ToList();
+ if (nonWavFiles.Any())
+ {
+ reportBuilder.AppendLine("The following English files in SFX are not .wav files:");
+ foreach (var f in nonWavFiles)
+ reportBuilder.AppendLine($" - {f}");
+ hasErrors = true;
+ }
+
+ foreach (var language in supportedLanguages)
+ {
+ if (language == LanguageType.English)
+ continue;
+
+ token.ThrowIfCancellationRequested();
+
+ var megPath = megPathFunc(language);
+ var langArchive = GetMegArchive(megPath);
+
+ if (langArchive == null)
+ {
+ reportBuilder.AppendLine($"Missing MEG file for {language}: {megPath}");
+ hasErrors = true;
+ continue;
+ }
+
+ var langFiles = new HashSet(StringComparer.OrdinalIgnoreCase);
+ foreach (var entry in langArchive)
+ langFiles.Add(entry.Path);
+
+ hasErrors |= VerifyFileParity(reportBuilder, language, englishFiles, langFiles, megPath);
+ }
+
+ return hasErrors;
+ }
+
+ private IMegArchive? GetMegArchive(string relativePath)
+ {
+ var fullPath = _fileSystem.Path.Combine(republicAtWar.Directory.FullName, relativePath);
+ return !_fileSystem.File.Exists(fullPath)
+ ? null
+ : _megFileService.Load(fullPath).Archive;
+ }
+}
\ No newline at end of file
diff --git a/src/RawDevTools/Steps/SingleActionPipeline.cs b/src/RawDevTools/Steps/SingleActionPipeline.cs
index 3b822c0..1709a3e 100644
--- a/src/RawDevTools/Steps/SingleActionPipeline.cs
+++ b/src/RawDevTools/Steps/SingleActionPipeline.cs
@@ -8,22 +8,25 @@
namespace RepublicAtWar.DevTools.Steps;
-public abstract class SingleActionPipeline(IServiceProvider serviceProvider, bool warningAsError) : SequentialPipeline(serviceProvider)
+public abstract class SingleActionPipeline(IServiceProvider serviceProvider, bool warningAsError)
+ : SequentialPipeline(serviceProvider)
{
- protected override Task> BuildSteps()
+
+ private class SimpleRunnerStep(Action action, IServiceProvider serviceProvider)
+ : PipelineStep(serviceProvider)
{
- return Task.FromResult>(new List
+ protected override Task RunCoreAsync(CancellationToken token)
{
- new SimpleRunnerStep(RunAction, ServiceProvider)
- });
+ return Task.Run(() => action(token), CancellationToken.None);
+ }
}
- private class SimpleRunnerStep(Action action, IServiceProvider serviceProvider) : PipelineStep(serviceProvider)
+ protected override Task> CreateRunnerSteps(CancellationToken token)
{
- protected override void RunCore(CancellationToken token)
+ return Task.FromResult>(new List
{
- action(token);
- }
+ new SimpleRunnerStep(RunAction, ServiceProvider)
+ });
}
protected abstract void RunAction(CancellationToken cancellationToken);
diff --git a/src/TextCompile/CompileTextDiffsPipeline.cs b/src/TextCompile/CompileTextDiffsPipeline.cs
index e2efe7f..ed6e2dd 100644
--- a/src/TextCompile/CompileTextDiffsPipeline.cs
+++ b/src/TextCompile/CompileTextDiffsPipeline.cs
@@ -1,19 +1,27 @@
using System;
using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
using AnakinRaW.CommonUtilities.SimplePipeline;
using RepublicAtWar.DevTools.Steps.Settings;
namespace RepublicAtWar.TextCompile;
-internal class CompileTextDiffsPipeline(BuildSettings settings, IServiceProvider serviceProvider, bool failFast = true)
- : SequentialPipeline(serviceProvider, failFast)
+internal class CompileTextDiffsPipeline : SequentialPipeline
{
- protected override Task> BuildSteps()
+ private readonly BuildSettings _settings;
+
+ public CompileTextDiffsPipeline(BuildSettings settings, IServiceProvider serviceProvider) : base(serviceProvider)
+ {
+ _settings = settings;
+ FailFast = true;
+ }
+
+ protected override Task> CreateRunnerSteps(CancellationToken token)
{
IList steps = new List
{
- new MergeDiffIntoDatStep(ServiceProvider, settings)
+ new MergeDiffIntoDatStep(ServiceProvider, _settings)
};
return Task.FromResult(steps);
}
diff --git a/src/TextCompile/MergeDiffIntoDatStep.cs b/src/TextCompile/MergeDiffIntoDatStep.cs
index 93527b1..ecf9c31 100644
--- a/src/TextCompile/MergeDiffIntoDatStep.cs
+++ b/src/TextCompile/MergeDiffIntoDatStep.cs
@@ -3,6 +3,7 @@
using System.IO.Abstractions;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using AnakinRaW.CommonUtilities.SimplePipeline.Steps;
using Microsoft.Extensions.DependencyInjection;
using PG.StarWarsGame.Engine.Localization;
@@ -19,7 +20,7 @@ internal class MergeDiffIntoDatStep(IServiceProvider serviceProvider, BuildSetti
private readonly LocalizationFileService _localizationFileService = new(serviceProvider, buildSettings.WarnAsError);
- protected override void RunCore(CancellationToken token)
+ protected override Task RunCoreAsync(CancellationToken token)
{
var diffFiles = _fileSystem.Directory.EnumerateFiles("Data\\Text", "Diff_MasterTextFile_*.txt");
var textFiles = _fileSystem.Directory.EnumerateFiles("Data\\Text", "MasterTextFile_*.txt");
@@ -33,6 +34,8 @@ protected override void RunCore(CancellationToken token)
var locFile = _localizationFileService.ReadLocalizationFile(textFile);
_localizationFileService.CompileLocalizationFile(locFile, datFile , true);
}
+
+ return Task.CompletedTask;
}
private void MergeDiffIntoDatOrText(string diffFile)
diff --git a/src/TextCompile/TextCompile.csproj b/src/TextCompile/TextCompile.csproj
index 5c129c8..e060ac5 100644
--- a/src/TextCompile/TextCompile.csproj
+++ b/src/TextCompile/TextCompile.csproj
@@ -15,18 +15,18 @@
-
-
-
-
-
-
+
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+