diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ade1755..9319d53 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,29 +1 @@ -FROM mcr.microsoft.com/vscode/devcontainers/base:ubuntu-20.04 - -RUN apt-get update \ - && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - wget \ - ca-certificates \ - \ - # .NET dependencies - libc6 \ - libgcc1 \ - libgssapi-krb5-2 \ - libicu66 \ - libssl1.1 \ - libstdc++6 \ - zlib1g \ - \ - # Mono - mono-devel \ - # Install Microsoft package feed - && wget -q https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \ - && dpkg -i packages-microsoft-prod.deb \ - && rm packages-microsoft-prod.deb \ - \ - # Install .NET 6 & 7 - && apt-get update \ - && apt-get install -y --no-install-recommends \ - dotnet-sdk-6.0 \ - dotnet-sdk-7.0 \ - && rm -rf /var/lib/apt/lists/* \ No newline at end of file +FROM ghcr.io/butr/devcontainer:latest diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 5940eaa..9b17a2d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,11 +1,79 @@ { - "name": ".NET Core & Mono", + "name": ".NET & Mono & Powershell", "build": { - "dockerfile": "Dockerfile" + "dockerfile": "Dockerfile", + "cacheFrom": [ + "ghcr.io/butr/bannerlord.blse-devcontainer:latest" + ] }, "features": { + "ghcr.io/butr/devcontainer/upgrade:1": { + + }, + "ghcr.io/devcontainers/features/git:1": { + "version": "latest", + "ppa": "false" + }, + "ghcr.io/butr/devcontainer/mono:1": { + + }, + "ghcr.io/devcontainers/features/dotnet:2": { + + }, "ghcr.io/devcontainers/features/powershell:1": { - "version": "latest" + "version": "latest" + } + }, + "overrideFeatureInstallOrder": [ + "ghcr.io/butr/devcontainer/upgrade", + "ghcr.io/devcontainers/features/git", + "ghcr.io/butr/devcontainer/mono", + "ghcr.io/devcontainers/features/dotnet", + "ghcr.io/devcontainers/features/powershell", + ], + "containerEnv": { + "DOTNET_CLI_TELEMETRY_OPTOUT": "true", + "DOTNET_HTTPREPL_TELEMETRY_OPTOUT": "true", + "DOTNET_NOLOGO": "true", + "DOTNET_SKIP_FIRST_TIME_EXPERIENCE": "true", + "DOTNET_USE_POLLING_FILE_WATCHER": "true", + "NUGET_XMLDOC_MODE": "skip", + + "BANNERLORD_BUTR_COMPATIBILITY_SCORE_URL": "${localEnv:BANNERLORD_BUTR_COMPATIBILITY_SCORE_URL}" + }, + "mounts": [ + { + "source":"${localEnv:BANNERLORD_GAME_DIR}", + "target":"/bannerlord", + "type":"bind" + } + ], + "postStartCommand": { + "dotnet restore": "dotnet restore src/Bannerlord.BLSE.sln" + }, + "postAttachCommand": "dotnet restore src/Bannerlord.BLSE.sln", + "customizations": { + "vscode": { + "extensions": [ + "editorconfig.editorconfig", + "github.vscode-github-actions", + "ms-vscode.powershell", + "ms-azuretools.vscode-docker", + "ms-dotnettools.csdevkit" + ], + "settings": { + "terminal.integrated.defaultProfile.linux": "pwsh", + "terminal.integrated.profiles.linux": { + "path": { + "path": "/usr/local/bin/pwsh" + } + }, + "powershell.powerShellAdditionalExePaths": { + "pwsh": "/usr/local/bin/pwsh" + }, + "telemetry.telemetryLevel": "off", + "dotnetAcquisitionExtension.enableTelemetry": false + } } } -} +} \ No newline at end of file diff --git a/.github/workflows/devcontainer.yml b/.github/workflows/devcontainer.yml new file mode 100644 index 0000000..45d2d33 --- /dev/null +++ b/.github/workflows/devcontainer.yml @@ -0,0 +1,33 @@ +name: Dev Container Build and Push Image + +on: + workflow_dispatch: + push: + branches: + - master + paths: + - '.devcontainer/**/*' + - '.github/workflows/devcontainer.yml' + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - + name: Checkout + id: checkout + uses: actions/checkout@v4 + - + name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: BUTR + password: ${{ secrets.TOKEN_GPR }} + - + name: Pre-build dev container image + uses: devcontainers/ci@v0.3 + with: + imageName: ghcr.io/butr/bannerlord.blse-devcontainer + cacheFrom: ghcr.io/butr/bannerlord.blse-devcontainer + push: always diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0ec3974..ab948a1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -76,13 +76,13 @@ jobs: ########################### publish-on-nexusmods: if: github.ref == 'refs/heads/master' - needs: ["get-changelog", "build"] + needs: [get-changelog, build] uses: BUTR/workflows/.github/workflows/release-nexusmods.yml@master with: nexusmods_game_id: mountandblade2bannerlord nexusmods_mod_id: 1 mod_filename: 'Bannerlord Software Extender (BLSE)' - mod_version: ${{ needs.build-changelog.outputs.mod_version }} + mod_version: ${{ needs.get-changelog.outputs.mod_version }} mod_description: ${{ needs.build.outputs.mod_description }} artifact_name: bannerlord secrets: @@ -94,11 +94,11 @@ jobs: ########################### publish-on-github: if: github.ref == 'refs/heads/master' - needs: ["get-changelog", "build"] + needs: [get-changelog, build] uses: BUTR/workflows/.github/workflows/release-github.yml@master with: mod_id: Bannerlord.BLSE - mod_version: ${{ needs.build-changelog.outputs.mod_version }} + mod_version: ${{ needs.get-changelog.outputs.mod_version }} mod_description: ${{ needs.build.outputs.mod_description }} artifact_name: bannerlord @@ -107,7 +107,7 @@ jobs: ########################### publish-on-steam: if: github.ref == 'refs/heads/master' - needs: ["get-changelog", "build"] + needs: [get-changelog, build] runs-on: ubuntu-latest steps: - name: Download Module artifact @@ -123,7 +123,7 @@ jobs: ssfnFileName: ${{ secrets.STEAM_SSFN_FILE_NAME }} ssfnFileContents: ${{ secrets.STEAM_SSFN_FILE_CONTENTS }} appId: ${{ secrets.STEAM_APPID }} - buildDescription: ${{ needs.build-changelog.outputs.mod_version }} - rootPath: artifact + buildDescription: ${{ needs.get-changelog.outputs.mod_version }} + rootPath: build depot1Path: ./artifact releaseBranch: prerelease diff --git a/build/common.props b/build/common.props index 21ac841..c6e97ff 100644 --- a/build/common.props +++ b/build/common.props @@ -11,13 +11,13 @@ - 1.4.12 + 1.5.0 2.2.2 - 3.0.0.137 - 5.0.209 + 3.0.0.139 + 5.0.222 3.2.0.77 1.0.1.50 - 1.0.77 + 1.0.107 full diff --git a/build/common.targets b/build/common.targets index 47a6443..411f264 100644 --- a/build/common.targets +++ b/build/common.targets @@ -37,9 +37,10 @@ if (Files.Length > 0) Log.LogMessage(MessageImportance.Normal, $"EmbeddedResource Src: {sourceItemSpec}"); using (var sourceStream = File.OpenRead(sourcePath)) - using (var destinationStream = File.OpenWrite(destinationPath)) + using (var destinationStream = new FileStream(destinationPath, FileMode.OpenOrCreate, FileAccess.Write)) using (var destinationGZip = new GZipStream(destinationStream, CompressionLevel.Optimal)) { + destinationStream.SetLength(0); sourceStream.CopyTo(destinationGZip); } diff --git a/changelog.txt b/changelog.txt index b7151d0..d06c9e1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,10 @@ --------------------------------------------------------------------------------------------------- +Version: 1.5.0 +Game Versions: v1.0.0,v1.0.1,v1.0.2,v1.0.3,v1.1.0,v1.1.1,v1.1.2,v1.1.3,v1.1.4,v1.1.5,v1.1.6,v1.2.x +* BETA Release! +* Switched to new launcher manager backend +* Added Update Recommendations - based on BUTR Analytics Server (based on ButterLib Crash Reports) +--------------------------------------------------------------------------------------------------- Version: 1.4.12 Game Versions: v1.0.0,v1.0.1,v1.0.2,v1.0.3,v1.1.0,v1.1.1,v1.1.2,v1.1.3,v1.1.4,v1.1.5,v1.1.6,v1.2.9 * Adapted to v1.2.9. Thanks to jzebedee! diff --git a/src/Bannerlord.BLSE.Loaders.Launcher/Bannerlord.BLSE.Loaders.Launcher.csproj b/src/Bannerlord.BLSE.Loaders.Launcher/Bannerlord.BLSE.Loaders.Launcher.csproj index 0ca898b..906c6f4 100644 --- a/src/Bannerlord.BLSE.Loaders.Launcher/Bannerlord.BLSE.Loaders.Launcher.csproj +++ b/src/Bannerlord.BLSE.Loaders.Launcher/Bannerlord.BLSE.Loaders.Launcher.csproj @@ -4,7 +4,7 @@ winexe x64 net472 - 11.0 + 12.0 enable ../../resources/BLSE_SMALL.ico true diff --git a/src/Bannerlord.BLSE.Loaders.LauncherEx/Bannerlord.BLSE.Loaders.LauncherEx.csproj b/src/Bannerlord.BLSE.Loaders.LauncherEx/Bannerlord.BLSE.Loaders.LauncherEx.csproj index 73e65b2..bd83798 100644 --- a/src/Bannerlord.BLSE.Loaders.LauncherEx/Bannerlord.BLSE.Loaders.LauncherEx.csproj +++ b/src/Bannerlord.BLSE.Loaders.LauncherEx/Bannerlord.BLSE.Loaders.LauncherEx.csproj @@ -4,7 +4,7 @@ winexe x64 net472 - 11.0 + 12.0 enable ../../resources/BLSE_SMALL.ico true diff --git a/src/Bannerlord.BLSE.Loaders.Standalone/Bannerlord.BLSE.Loaders.Standalone.csproj b/src/Bannerlord.BLSE.Loaders.Standalone/Bannerlord.BLSE.Loaders.Standalone.csproj index 3e43bce..b797fac 100644 --- a/src/Bannerlord.BLSE.Loaders.Standalone/Bannerlord.BLSE.Loaders.Standalone.csproj +++ b/src/Bannerlord.BLSE.Loaders.Standalone/Bannerlord.BLSE.Loaders.Standalone.csproj @@ -4,7 +4,7 @@ winexe x64 net472 - 11.0 + 12.0 enable ../../resources/BLSE_SMALL.ico true diff --git a/src/Bannerlord.BLSE.Shared/Bannerlord.BLSE.Shared.csproj b/src/Bannerlord.BLSE.Shared/Bannerlord.BLSE.Shared.csproj index b30d038..75ee181 100644 --- a/src/Bannerlord.BLSE.Shared/Bannerlord.BLSE.Shared.csproj +++ b/src/Bannerlord.BLSE.Shared/Bannerlord.BLSE.Shared.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 11.0 + 12.0 enable x64 library @@ -18,13 +18,11 @@ false true - $(PkgBUTR_ILRepack)\tools\net461\ILRepack.exe - - - + + @@ -79,8 +77,10 @@ $(MSBuildThisFileDirectory)..\Bannerlord.LauncherEx\Bannerlord.LauncherEx.csproj "$([System.IO.Path]::GetFullPath('$(LauncherExProject)'))" - - + + + + diff --git a/src/Bannerlord.BLSE/BLSECommands.cs b/src/Bannerlord.BLSE/BLSECommands.cs index 427b99f..17d704b 100644 --- a/src/Bannerlord.BLSE/BLSECommands.cs +++ b/src/Bannerlord.BLSE/BLSECommands.cs @@ -4,14 +4,13 @@ using System.Linq; using System.Reflection; -namespace Bannerlord.BLSE +namespace Bannerlord.BLSE; + +public static class BLSECommands { - public static class BLSECommands + public static string GetVersion(List _) { - public static string GetVersion(List _) - { - var blseMetadata = AccessTools2.TypeByName("Bannerlord.BLSE.BLSEInterceptorAttribute")?.Assembly.GetCustomAttributes(); - return blseMetadata?.FirstOrDefault(x => x.Key == "BLSEVersion")?.Value ?? "0.0.0.0"; - } + var blseMetadata = AccessTools2.TypeByName("Bannerlord.BLSE.BLSEInterceptorAttribute")?.Assembly.GetCustomAttributes(); + return blseMetadata?.FirstOrDefault(x => x.Key == "BLSEVersion")?.Value ?? "0.0.0.0"; } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/BLSEExceptionHandlerAttribute.cs b/src/Bannerlord.BLSE/BLSEExceptionHandlerAttribute.cs index 1604721..1fcaca3 100644 --- a/src/Bannerlord.BLSE/BLSEExceptionHandlerAttribute.cs +++ b/src/Bannerlord.BLSE/BLSEExceptionHandlerAttribute.cs @@ -1,7 +1,6 @@ using System; -namespace Bannerlord.BLSE -{ - [AttributeUsage(AttributeTargets.Class)] - public sealed class BLSEExceptionHandlerAttribute : Attribute { } -} \ No newline at end of file +namespace Bannerlord.BLSE; + +[AttributeUsage(AttributeTargets.Class)] +public sealed class BLSEExceptionHandlerAttribute : Attribute { } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/BLSEInterceptorAttribute.cs b/src/Bannerlord.BLSE/BLSEInterceptorAttribute.cs index 7d82759..ae128ed 100644 --- a/src/Bannerlord.BLSE/BLSEInterceptorAttribute.cs +++ b/src/Bannerlord.BLSE/BLSEInterceptorAttribute.cs @@ -1,7 +1,6 @@ using System; -namespace Bannerlord.BLSE -{ - [AttributeUsage(AttributeTargets.Class)] - public sealed class BLSEInterceptorAttribute : Attribute { } -} \ No newline at end of file +namespace Bannerlord.BLSE; + +[AttributeUsage(AttributeTargets.Class)] +public sealed class BLSEInterceptorAttribute : Attribute { } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Bannerlord.BLSE.csproj b/src/Bannerlord.BLSE/Bannerlord.BLSE.csproj index 196a480..6739a13 100644 --- a/src/Bannerlord.BLSE/Bannerlord.BLSE.csproj +++ b/src/Bannerlord.BLSE/Bannerlord.BLSE.csproj @@ -6,7 +6,7 @@ $(Version).$(GITHUB_RUN_NUMBER) netstandard2.0 - 11.0 + 12.0 enable x64 diff --git a/src/Bannerlord.BLSE/FeatureIds.cs b/src/Bannerlord.BLSE/FeatureIds.cs index 4b583e8..caeeadc 100644 --- a/src/Bannerlord.BLSE/FeatureIds.cs +++ b/src/Bannerlord.BLSE/FeatureIds.cs @@ -1,35 +1,34 @@ using System.Collections.Generic; -namespace Bannerlord.BLSE +namespace Bannerlord.BLSE; + +public static class FeatureIds { - public static class FeatureIds - { - public static string InterceptorId => "BLSE.LoadingInterceptor"; - public static string AssemblyResolverId => "BLSE.AssemblyResolver"; - private static string InterceptorId2 => "BUTRLoader.BUTRLoadingInterceptor"; - private static string AssemblyResolverId2 => "BUTRLoader.BUTRAssemblyResolver"; - public static string ContinueSaveFileId => "BLSE.ContinueSaveFile"; - public static string CommandsId => "BLSE.Commands"; - public static string XboxId => "BLSE.Xbox"; - public static string ExceptionInterceptorId => "BLSE.ExceptionInterceptor"; + public static string InterceptorId => "BLSE.LoadingInterceptor"; + public static string AssemblyResolverId => "BLSE.AssemblyResolver"; + private static string InterceptorId2 => "BUTRLoader.BUTRLoadingInterceptor"; + private static string AssemblyResolverId2 => "BUTRLoader.BUTRAssemblyResolver"; + public static string ContinueSaveFileId => "BLSE.ContinueSaveFile"; + public static string CommandsId => "BLSE.Commands"; + public static string XboxId => "BLSE.Xbox"; + public static string ExceptionInterceptorId => "BLSE.ExceptionInterceptor"; - public static readonly HashSet Features = new() - { - InterceptorId, - InterceptorId2, - AssemblyResolverId, - AssemblyResolverId2, - ContinueSaveFileId, - CommandsId, - ExceptionInterceptorId, - }; - public static readonly HashSet LauncherFeatures = new() - { - InterceptorId, - InterceptorId2, - AssemblyResolverId, - AssemblyResolverId2, - ExceptionInterceptorId, - }; - } + public static readonly HashSet Features = new() + { + InterceptorId, + InterceptorId2, + AssemblyResolverId, + AssemblyResolverId2, + ContinueSaveFileId, + CommandsId, + ExceptionInterceptorId, + }; + public static readonly HashSet LauncherFeatures = new() + { + InterceptorId, + InterceptorId2, + AssemblyResolverId, + AssemblyResolverId2, + ExceptionInterceptorId, + }; } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/AssemblyResolver/AssemblyResolverFeature.cs b/src/Bannerlord.BLSE/Features/AssemblyResolver/AssemblyResolverFeature.cs index bac4795..edcb16a 100644 --- a/src/Bannerlord.BLSE/Features/AssemblyResolver/AssemblyResolverFeature.cs +++ b/src/Bannerlord.BLSE/Features/AssemblyResolver/AssemblyResolverFeature.cs @@ -11,68 +11,67 @@ using TaleWorlds.Library; -namespace Bannerlord.BLSE.Features.AssemblyResolver +namespace Bannerlord.BLSE.Features.AssemblyResolver; + +public static class AssemblyResolverFeature { - public static class AssemblyResolverFeature - { - private static ResolveEventHandler AssemblyLoaderOnAssemblyResolve = - AccessTools2.GetDelegate(typeof(AssemblyLoader), "OnAssemblyResolve")!; + private static ResolveEventHandler AssemblyLoaderOnAssemblyResolve = + AccessTools2.GetDelegate(typeof(AssemblyLoader), "OnAssemblyResolve")!; - public static string Id = FeatureIds.AssemblyResolverId; + public static string Id = FeatureIds.AssemblyResolverId; - public static void Enable(Harmony harmony) - { - AssemblyLoader.Initialize(); - AppDomain.CurrentDomain.AssemblyResolve -= AssemblyLoaderOnAssemblyResolve; - AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve; - } + public static void Enable(Harmony harmony) + { + AssemblyLoader.Initialize(); + AppDomain.CurrentDomain.AssemblyResolve -= AssemblyLoaderOnAssemblyResolve; + AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve; + } - private static Assembly? OnAssemblyResolve(object? sender, ResolveEventArgs args) - { - if (args.Name is null) return null; + private static Assembly? OnAssemblyResolve(object? sender, ResolveEventArgs args) + { + if (args.Name is null) return null; - var name = new AssemblyName(args.Name); - if (name.Name is null) return null; + var name = new AssemblyName(args.Name); + if (name.Name is null) return null; - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + if (assembly.FullName == name.FullName) { - if (assembly.FullName == name.FullName) - { - return assembly; - } + return assembly; } + } - try - { - var configName = Path.GetFileName(Directory.GetCurrentDirectory()); - - var isInGame = GameUtils.GetModulesNames() is not null; + try + { + var configName = Path.GetFileName(Directory.GetCurrentDirectory()); - var assemblies = (isInGame ? ModuleInfoHelper.GetLoadedModules() : ModuleInfoHelper.GetModules()).Select(x => - { - var directory = Path.Combine(x.Path, "bin", configName); - return Directory.Exists(directory) ? Directory.GetFiles(directory, "*.dll") : Array.Empty(); - }).ToArray(); + var isInGame = GameUtils.GetModulesNames() is not null; - var assembly = assemblies.SelectMany(x => x).FirstOrDefault(x => Path.GetFileNameWithoutExtension(x) == name.Name); + var assemblies = (isInGame ? ModuleInfoHelper.GetLoadedModules() : ModuleInfoHelper.GetModules()).Select(x => + { + var directory = Path.Combine(x.Path, "bin", configName); + return Directory.Exists(directory) ? Directory.GetFiles(directory, "*.dll") : Array.Empty(); + }).ToArray(); - if (assembly is not null) - { - return Assembly.LoadFrom(assembly); - } + var assembly = assemblies.SelectMany(x => x).FirstOrDefault(x => Path.GetFileNameWithoutExtension(x) == name.Name); - assembly = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll").FirstOrDefault(x => Path.GetFileNameWithoutExtension(x) == name.Name); - if (assembly is not null) - { - return Assembly.LoadFrom(assembly); - } - } - catch (Exception) + if (assembly is not null) { - return null; + return Assembly.LoadFrom(assembly); } + assembly = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll").FirstOrDefault(x => Path.GetFileNameWithoutExtension(x) == name.Name); + if (assembly is not null) + { + return Assembly.LoadFrom(assembly); + } + } + catch (Exception) + { return null; } + + return null; } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/Commands/CommandsFeature.cs b/src/Bannerlord.BLSE/Features/Commands/CommandsFeature.cs index 33b6910..68debd4 100644 --- a/src/Bannerlord.BLSE/Features/Commands/CommandsFeature.cs +++ b/src/Bannerlord.BLSE/Features/Commands/CommandsFeature.cs @@ -5,20 +5,19 @@ using System; using System.Collections.Generic; -namespace Bannerlord.BLSE.Features.Commands +namespace Bannerlord.BLSE.Features.Commands; + +public static class CommandsFeature { - public static class CommandsFeature - { - public static string Id = FeatureIds.CommandsId; + public static string Id = FeatureIds.CommandsId; - public static readonly Dictionary, string>> Functions = new() - { - { "blse.version", BLSECommands.GetVersion } - }; + public static readonly Dictionary, string>> Functions = new() + { + { "blse.version", BLSECommands.GetVersion } + }; - public static void Enable(Harmony harmony) - { - CommandLineFunctionalityPatch.Enable(harmony); - } + public static void Enable(Harmony harmony) + { + CommandLineFunctionalityPatch.Enable(harmony); } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/Commands/Patches/CommandLineFunctionalityPatch.cs b/src/Bannerlord.BLSE/Features/Commands/Patches/CommandLineFunctionalityPatch.cs index 4b50e6d..e27a221 100644 --- a/src/Bannerlord.BLSE/Features/Commands/Patches/CommandLineFunctionalityPatch.cs +++ b/src/Bannerlord.BLSE/Features/Commands/Patches/CommandLineFunctionalityPatch.cs @@ -9,55 +9,54 @@ using TaleWorlds.Library; -namespace Bannerlord.BLSE.Features.Commands.Patches +namespace Bannerlord.BLSE.Features.Commands.Patches; + +internal static class CommandLineFunctionalityPatch { - internal static class CommandLineFunctionalityPatch + private readonly ref struct CommandLineFunctionHandle { - private readonly ref struct CommandLineFunctionHandle - { - private delegate object CommandLineFunctionCtorDelegate(Func, string> commandlinefunc); - private static readonly CommandLineFunctionCtorDelegate? CommandLineFunctionCtor = - AccessTools2.GetConstructorDelegate("TaleWorlds.Library.CommandLineFunctionality+CommandLineFunction", new[] { typeof(Func, string>) }); + private delegate object CommandLineFunctionCtorDelegate(Func, string> commandlinefunc); + private static readonly CommandLineFunctionCtorDelegate? CommandLineFunctionCtor = + AccessTools2.GetConstructorDelegate("TaleWorlds.Library.CommandLineFunctionality+CommandLineFunction", new[] { typeof(Func, string>) }); - public static CommandLineFunctionHandle Create(Func, string> commandlinefunc) - { - var commandLineFunction = CommandLineFunctionCtor?.Invoke(commandlinefunc); - return commandLineFunction is not null ? new(commandLineFunction) : default; - } + public static CommandLineFunctionHandle Create(Func, string> commandlinefunc) + { + var commandLineFunction = CommandLineFunctionCtor?.Invoke(commandlinefunc); + return commandLineFunction is not null ? new(commandLineFunction) : default; + } - public object Object { get; } + public object Object { get; } - private CommandLineFunctionHandle(object obj) => Object = obj; - } + private CommandLineFunctionHandle(object obj) => Object = obj; + } - private static Harmony? _harmony; + private static Harmony? _harmony; - public static bool Enable(Harmony harmony) - { - _harmony = harmony; + public static bool Enable(Harmony harmony) + { + _harmony = harmony; - return harmony.TryPatch( - AccessTools2.DeclaredMethod(typeof(CommandLineFunctionality), nameof(CommandLineFunctionality.CollectCommandLineFunctions)), - postfix: AccessTools2.DeclaredMethod(typeof(CommandLineFunctionalityPatch), nameof(CollectCommandLineFunctionsPostfix))); - } + return harmony.TryPatch( + AccessTools2.DeclaredMethod(typeof(CommandLineFunctionality), nameof(CommandLineFunctionality.CollectCommandLineFunctions)), + postfix: AccessTools2.DeclaredMethod(typeof(CommandLineFunctionalityPatch), nameof(CollectCommandLineFunctionsPostfix))); + } - private static void CollectCommandLineFunctionsPostfix(IDictionary ___AllFunctions, ref List __result) + private static void CollectCommandLineFunctionsPostfix(IDictionary ___AllFunctions, ref List __result) + { + try { - try + foreach (var (name, function) in CommandsFeature.Functions) { - foreach (var (name, function) in CommandsFeature.Functions) - { - if (CommandLineFunctionHandle.Create(function) is not { Object: { } cmdFuncObject }) continue; - ___AllFunctions.Add(name, cmdFuncObject); - __result.Add(name); - } - } - finally - { - _harmony?.Unpatch( - AccessTools2.DeclaredMethod(typeof(CommandLineFunctionality), nameof(CommandLineFunctionality.CollectCommandLineFunctions)), - AccessTools2.DeclaredMethod(typeof(CommandLineFunctionalityPatch), nameof(CollectCommandLineFunctionsPostfix))); + if (CommandLineFunctionHandle.Create(function) is not { Object: { } cmdFuncObject }) continue; + ___AllFunctions.Add(name, cmdFuncObject); + __result.Add(name); } } + finally + { + _harmony?.Unpatch( + AccessTools2.DeclaredMethod(typeof(CommandLineFunctionality), nameof(CommandLineFunctionality.CollectCommandLineFunctions)), + AccessTools2.DeclaredMethod(typeof(CommandLineFunctionalityPatch), nameof(CollectCommandLineFunctionsPostfix))); + } } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/ContinueSaveFile/ContinueSaveFileFeature.cs b/src/Bannerlord.BLSE/Features/ContinueSaveFile/ContinueSaveFileFeature.cs index c3ff8f9..2b7daca 100644 --- a/src/Bannerlord.BLSE/Features/ContinueSaveFile/ContinueSaveFileFeature.cs +++ b/src/Bannerlord.BLSE/Features/ContinueSaveFile/ContinueSaveFileFeature.cs @@ -6,39 +6,38 @@ using TaleWorlds.MountAndBlade; -namespace Bannerlord.BLSE.Features.ContinueSaveFile +namespace Bannerlord.BLSE.Features.ContinueSaveFile; + +public static class ContinueSaveFileFeature { - public static class ContinueSaveFileFeature - { - public static string Id = FeatureIds.ContinueSaveFileId; + public static string Id = FeatureIds.ContinueSaveFileId; - private static string? _currentSaveFile; - private static Harmony? _harmony; + private static string? _currentSaveFile; + private static Harmony? _harmony; - public static void Enable(Harmony harmony) - { - _harmony = harmony; - ModulePatch.OnSaveGameArgParsed += (_, saveFile) => _currentSaveFile = saveFile; - ModulePatch.Enable(harmony); + public static void Enable(Harmony harmony) + { + _harmony = harmony; + ModulePatch.OnSaveGameArgParsed += (_, saveFile) => _currentSaveFile = saveFile; + ModulePatch.Enable(harmony); - AppDomain.CurrentDomain.AssemblyLoad += CurrentDomainOnAssemblyLoad; - } + AppDomain.CurrentDomain.AssemblyLoad += CurrentDomainOnAssemblyLoad; + } - private static void CurrentDomainOnAssemblyLoad(object? sender, AssemblyLoadEventArgs args) - { - if (_harmony is null) return; - - if (args.LoadedAssembly.GetName().Name == "SandBox") - { - SandBoxSubModulePatch.GetSaveGameArg = GetSaveFile; - SandBoxSubModulePatch.Enable(_harmony); - InformationManagerPatch.Enable(_harmony); - } - } + private static void CurrentDomainOnAssemblyLoad(object? sender, AssemblyLoadEventArgs args) + { + if (_harmony is null) return; - private static string? GetSaveFile(GameStartupInfo startupInfo) + if (args.LoadedAssembly.GetName().Name == "SandBox") { - return _currentSaveFile; + SandBoxSubModulePatch.GetSaveGameArg = GetSaveFile; + SandBoxSubModulePatch.Enable(_harmony); + InformationManagerPatch.Enable(_harmony); } } + + private static string? GetSaveFile(GameStartupInfo startupInfo) + { + return _currentSaveFile; + } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/InformationManagerConfirmInquiryHandler.cs b/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/InformationManagerConfirmInquiryHandler.cs index 110019e..3d7a27f 100644 --- a/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/InformationManagerConfirmInquiryHandler.cs +++ b/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/InformationManagerConfirmInquiryHandler.cs @@ -1,10 +1,9 @@ using System; -namespace Bannerlord.BLSE.Features.ContinueSaveFile.Patches +namespace Bannerlord.BLSE.Features.ContinueSaveFile.Patches; + +internal class InformationManagerConfirmInquiryHandler : IDisposable { - internal class InformationManagerConfirmInquiryHandler : IDisposable - { - public InformationManagerConfirmInquiryHandler() => InformationManagerPatch.SkipChange = true; - public void Dispose() => InformationManagerPatch.SkipChange = false; - } + public InformationManagerConfirmInquiryHandler() => InformationManagerPatch.SkipChange = true; + public void Dispose() => InformationManagerPatch.SkipChange = false; } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/InformationManagerPatch.cs b/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/InformationManagerPatch.cs index ee56089..dfb58fb 100644 --- a/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/InformationManagerPatch.cs +++ b/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/InformationManagerPatch.cs @@ -3,27 +3,26 @@ using TaleWorlds.Library; -namespace Bannerlord.BLSE.Features.ContinueSaveFile.Patches +namespace Bannerlord.BLSE.Features.ContinueSaveFile.Patches; + +internal static class InformationManagerPatch { - internal static class InformationManagerPatch - { - internal static bool SkipChange = false; + internal static bool SkipChange = false; - public static bool Enable(Harmony harmony) - { - return harmony.TryPatch( - original: AccessTools2.Method(typeof(InformationManager), "ShowInquiry"), - prefix: AccessTools2.Method(typeof(InformationManagerPatch), nameof(Prefix))); - } + public static bool Enable(Harmony harmony) + { + return harmony.TryPatch( + original: AccessTools2.Method(typeof(InformationManager), "ShowInquiry"), + prefix: AccessTools2.Method(typeof(InformationManagerPatch), nameof(Prefix))); + } - private static bool Prefix(InquiryData data) + private static bool Prefix(InquiryData data) + { + if (SkipChange) { - if (SkipChange) - { - data.AffirmativeAction?.Invoke(); - return false; - } - return true; + data.AffirmativeAction?.Invoke(); + return false; } + return true; } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/ModulePatch.cs b/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/ModulePatch.cs index 1f1c997..e7d3dec 100644 --- a/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/ModulePatch.cs +++ b/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/ModulePatch.cs @@ -9,38 +9,37 @@ using TaleWorlds.Engine; using TaleWorlds.MountAndBlade; -namespace Bannerlord.BLSE.Features.ContinueSaveFile.Patches +namespace Bannerlord.BLSE.Features.ContinueSaveFile.Patches; + +internal static class ModulePatch { - internal static class ModulePatch - { - public static event Action? OnSaveGameArgParsed; + public static event Action? OnSaveGameArgParsed; - private static Harmony? _harmony; + private static Harmony? _harmony; - public static bool Enable(Harmony harmony) - { - _harmony = harmony; + public static bool Enable(Harmony harmony) + { + _harmony = harmony; - return harmony.TryPatch( - AccessTools2.DeclaredMethod(typeof(Module), "ProcessApplicationArguments"), - postfix: AccessTools2.DeclaredMethod(typeof(ModulePatch), nameof(ProcessApplicationArgumentsPostfix))); - } + return harmony.TryPatch( + AccessTools2.DeclaredMethod(typeof(Module), "ProcessApplicationArguments"), + postfix: AccessTools2.DeclaredMethod(typeof(ModulePatch), nameof(ProcessApplicationArgumentsPostfix))); + } - private static void ProcessApplicationArgumentsPostfix(Module __instance) + private static void ProcessApplicationArgumentsPostfix(Module __instance) + { + var cli = Utilities.GetFullCommandLineString(); + var array = CommandLineSplitter.SplitCommandLine(cli).ToArray(); + for (var i = 0; i < array.Length; i++) { - var cli = Utilities.GetFullCommandLineString(); - var array = CommandLineSplitter.SplitCommandLine(cli).ToArray(); - for (var i = 0; i < array.Length; i++) - { - if (!string.Equals(array[i], "/continuesave", StringComparison.OrdinalIgnoreCase)) continue; - if (array.Length <= i + 1) continue; - var saveGame = array[i + 1]; - OnSaveGameArgParsed?.Invoke(__instance.StartupInfo, saveGame); - } - - _harmony?.Unpatch( - AccessTools2.DeclaredMethod(typeof(Module), "ProcessApplicationArguments"), - AccessTools2.DeclaredMethod(typeof(ModulePatch), nameof(ProcessApplicationArgumentsPostfix))); + if (!string.Equals(array[i], "/continuesave", StringComparison.OrdinalIgnoreCase)) continue; + if (array.Length <= i + 1) continue; + var saveGame = array[i + 1]; + OnSaveGameArgParsed?.Invoke(__instance.StartupInfo, saveGame); } + + _harmony?.Unpatch( + AccessTools2.DeclaredMethod(typeof(Module), "ProcessApplicationArguments"), + AccessTools2.DeclaredMethod(typeof(ModulePatch), nameof(ProcessApplicationArgumentsPostfix))); } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/SandBoxSubModulePatch.cs b/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/SandBoxSubModulePatch.cs index 15e2404..94033ce 100644 --- a/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/SandBoxSubModulePatch.cs +++ b/src/Bannerlord.BLSE/Features/ContinueSaveFile/Patches/SandBoxSubModulePatch.cs @@ -10,66 +10,65 @@ using TaleWorlds.SaveSystem; using TaleWorlds.SaveSystem.Load; -namespace Bannerlord.BLSE.Features.ContinueSaveFile.Patches +namespace Bannerlord.BLSE.Features.ContinueSaveFile.Patches; + +internal static class SandBoxSubModulePatch { - internal static class SandBoxSubModulePatch - { - private delegate void TryLoadSaveDelegate(SaveGameFileInfo saveInfo, Action onStartGame, Action? onCancel = null); + private delegate void TryLoadSaveDelegate(SaveGameFileInfo saveInfo, Action onStartGame, Action? onCancel = null); - public static Func? GetSaveGameArg; + public static Func? GetSaveGameArg; - private static Harmony? _harmony; + private static Harmony? _harmony; - public static bool Enable(Harmony harmony) - { - _harmony = harmony; + public static bool Enable(Harmony harmony) + { + _harmony = harmony; - return harmony.TryPatch( - AccessTools2.DeclaredMethod("SandBox.SandBoxSubModule:OnInitialState"), - prefix: AccessTools2.DeclaredMethod(typeof(SandBoxSubModulePatch), nameof(OnInitialStatePrefix))); - } + return harmony.TryPatch( + AccessTools2.DeclaredMethod("SandBox.SandBoxSubModule:OnInitialState"), + prefix: AccessTools2.DeclaredMethod(typeof(SandBoxSubModulePatch), nameof(OnInitialStatePrefix))); + } - private static bool OnInitialStatePrefix(MBSubModuleBase __instance) + private static bool OnInitialStatePrefix(MBSubModuleBase __instance) + { + static void FailedToLoad(string message) { - static void FailedToLoad(string message) + try { - try - { - InformationManagerWrapper.ShowInquiry("Warning!", message); - } - catch (Exception) - { - MessageBoxDialog.Show(message, "Warning!"); - } + InformationManagerWrapper.ShowInquiry("Warning!", message); } - - if (AccessTools2.GetDelegate("SandBox.SandBoxSaveHelper:TryLoadSave") is not { } tryLoadSave) return true; - if (GetSaveGameArg?.Invoke(Module.CurrentModule.StartupInfo) is not { } saveFileName) return true; - if (saveFileName.EndsWith(".sav", StringComparison.OrdinalIgnoreCase)) saveFileName = saveFileName.Remove(saveFileName.Length - 4, 4); - if (MBSaveLoad.GetSaveFileWithName(saveFileName) is not { } saveFile) + catch (Exception) { - FailedToLoad($"Failed to load Save!\nFailed to find save '{saveFileName}'!"); - return true; - } - if (AccessTools2.TypeByName("SandBox.SandBoxSubModule") is not { } sandBoxSubModuleType) - { - FailedToLoad($"Failed to load Save!\nFailed to find 'SandBox' module!"); - return true; - } - if (AccessTools2.GetDelegate>(__instance, sandBoxSubModuleType, "StartGame") is not { } startGame) - { - FailedToLoad($"Failed to load Save!\nUnexpected 'SandBox' issue! 'StartGame' method not found!"); - return true; + MessageBoxDialog.Show(message, "Warning!"); } + } + + if (AccessTools2.GetDelegate("SandBox.SandBoxSaveHelper:TryLoadSave") is not { } tryLoadSave) return true; + if (GetSaveGameArg?.Invoke(Module.CurrentModule.StartupInfo) is not { } saveFileName) return true; + if (saveFileName.EndsWith(".sav", StringComparison.OrdinalIgnoreCase)) saveFileName = saveFileName.Remove(saveFileName.Length - 4, 4); + if (MBSaveLoad.GetSaveFileWithName(saveFileName) is not { } saveFile) + { + FailedToLoad($"Failed to load Save!\nFailed to find save '{saveFileName}'!"); + return true; + } + if (AccessTools2.TypeByName("SandBox.SandBoxSubModule") is not { } sandBoxSubModuleType) + { + FailedToLoad($"Failed to load Save!\nFailed to find 'SandBox' module!"); + return true; + } + if (AccessTools2.GetDelegate>(__instance, sandBoxSubModuleType, "StartGame") is not { } startGame) + { + FailedToLoad($"Failed to load Save!\nUnexpected 'SandBox' issue! 'StartGame' method not found!"); + return true; + } - using (var _ = new InformationManagerConfirmInquiryHandler()) - tryLoadSave(saveFile, startGame); + using (var _ = new InformationManagerConfirmInquiryHandler()) + tryLoadSave(saveFile, startGame); - _harmony?.Unpatch( - AccessTools2.DeclaredMethod("SandBox.SandBoxSubModule:OnInitialState"), - AccessTools2.DeclaredMethod(typeof(SandBoxSubModulePatch), nameof(OnInitialStatePrefix))); + _harmony?.Unpatch( + AccessTools2.DeclaredMethod("SandBox.SandBoxSubModule:OnInitialState"), + AccessTools2.DeclaredMethod(typeof(SandBoxSubModulePatch), nameof(OnInitialStatePrefix))); - return false; - } + return false; } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/ExceptionInterceptor/ExceptionInterceptorFeature.cs b/src/Bannerlord.BLSE/Features/ExceptionInterceptor/ExceptionInterceptorFeature.cs index c8c5266..a1d5f48 100644 --- a/src/Bannerlord.BLSE/Features/ExceptionInterceptor/ExceptionInterceptorFeature.cs +++ b/src/Bannerlord.BLSE/Features/ExceptionInterceptor/ExceptionInterceptorFeature.cs @@ -8,71 +8,70 @@ using System.Runtime.ExceptionServices; using System.Security; -namespace Bannerlord.BLSE.Features.ExceptionInterceptor +namespace Bannerlord.BLSE.Features.ExceptionInterceptor; + +public static class ExceptionInterceptorFeature { - public static class ExceptionInterceptorFeature - { - public static string Id = FeatureIds.ExceptionInterceptorId; + public static string Id = FeatureIds.ExceptionInterceptorId; - private delegate void OnExceptionDelegate(Exception exception); + private delegate void OnExceptionDelegate(Exception exception); - private static readonly Harmony ExceptionHandler = new("bannerlord.blse.exceptionhandler"); - private static readonly MethodInfo FinalizerMethod = AccessTools2.Method(typeof(ExceptionInterceptorFeature), nameof(Finalizer))!; + private static readonly Harmony ExceptionHandler = new("bannerlord.blse.exceptionhandler"); + private static readonly MethodInfo FinalizerMethod = AccessTools2.Method(typeof(ExceptionInterceptorFeature), nameof(Finalizer))!; - public static event Action? OnException; + public static event Action? OnException; - public static void Enable() - { - AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException; - OnException += HandleException; - } + public static void Enable() + { + AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException; + OnException += HandleException; + } - public static void EnableAutoGens() - { - AppDomain.CurrentDomain.AssemblyLoad += CurrentDomainOnAssemblyLoad; - FinalizerGlobal.Enable(ExceptionHandler, FinalizerMethod); - } + public static void EnableAutoGens() + { + AppDomain.CurrentDomain.AssemblyLoad += CurrentDomainOnAssemblyLoad; + FinalizerGlobal.Enable(ExceptionHandler, FinalizerMethod); + } - public static void Disable() - { - AppDomain.CurrentDomain.UnhandledException -= CurrentDomainOnUnhandledException; - OnException -= HandleException; - AppDomain.CurrentDomain.AssemblyLoad -= CurrentDomainOnAssemblyLoad; - ExceptionHandler.UnpatchAll(ExceptionHandler.Id); - } + public static void Disable() + { + AppDomain.CurrentDomain.UnhandledException -= CurrentDomainOnUnhandledException; + OnException -= HandleException; + AppDomain.CurrentDomain.AssemblyLoad -= CurrentDomainOnAssemblyLoad; + ExceptionHandler.UnpatchAll(ExceptionHandler.Id); + } - private static void CurrentDomainOnAssemblyLoad(object sender, AssemblyLoadEventArgs args) - { - var assembly = args.LoadedAssembly; - FinalizerGlobal.OnNewAssembly(ExceptionHandler, FinalizerMethod, assembly); - } + private static void CurrentDomainOnAssemblyLoad(object sender, AssemblyLoadEventArgs args) + { + var assembly = args.LoadedAssembly; + FinalizerGlobal.OnNewAssembly(ExceptionHandler, FinalizerMethod, assembly); + } - private static void Finalizer(Exception? __exception) - { - if (__exception is not null) - HandleException(__exception); - } + private static void Finalizer(Exception? __exception) + { + if (__exception is not null) + HandleException(__exception); + } - [HandleProcessCorruptedStateExceptions, SecurityCritical] - private static void CurrentDomainOnUnhandledException(object? _, UnhandledExceptionEventArgs e) - { - if (e.ExceptionObject is Exception exception) - OnException?.Invoke(exception); - } + [HandleProcessCorruptedStateExceptions, SecurityCritical] + private static void CurrentDomainOnUnhandledException(object? _, UnhandledExceptionEventArgs e) + { + if (e.ExceptionObject is Exception exception) + OnException?.Invoke(exception); + } - private static void HandleException(Exception exception) + private static void HandleException(Exception exception) + { + try { - try + foreach (var type in TypeFinder.GetInterceptorTypes(typeof(BLSEExceptionHandlerAttribute))) { - foreach (var type in TypeFinder.GetInterceptorTypes(typeof(BLSEExceptionHandlerAttribute))) + if (AccessTools2.GetDelegate(type, "OnException") is { } method) { - if (AccessTools2.GetDelegate(type, "OnException") is { } method) - { - method(exception); - } + method(exception); } } - catch (Exception) { /* ignore */ } } + catch (Exception) { /* ignore */ } } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/ExceptionInterceptor/FinalizerGlobal.cs b/src/Bannerlord.BLSE/Features/ExceptionInterceptor/FinalizerGlobal.cs index c1d2bdd..52f6c4d 100644 --- a/src/Bannerlord.BLSE/Features/ExceptionInterceptor/FinalizerGlobal.cs +++ b/src/Bannerlord.BLSE/Features/ExceptionInterceptor/FinalizerGlobal.cs @@ -5,52 +5,51 @@ using System.Linq; using System.Reflection; -namespace Bannerlord.BLSE.Features.ExceptionInterceptor +namespace Bannerlord.BLSE.Features.ExceptionInterceptor; + +internal static class FinalizerGlobal { - internal static class FinalizerGlobal + private static readonly HashSet BlacklistedMethodStarts = new() + { + "GameNetwork_", + "ManagedOptions_", + "MBEditor_", + "MBMultiplayerData_", + "CrashInformationCollector_", + "NativeParallelDriver_", + "NativeObject_", + "ManagedObject_", + "DotNetObject_", + "ManagedExtensions_", + }; + private static readonly HashSet BlacklistedMethods = new() { - private static readonly HashSet BlacklistedMethodStarts = new() - { - "GameNetwork_", - "ManagedOptions_", - "MBEditor_", - "MBMultiplayerData_", - "CrashInformationCollector_", - "NativeParallelDriver_", - "NativeObject_", - "ManagedObject_", - "DotNetObject_", - "ManagedExtensions_", - }; - private static readonly HashSet BlacklistedMethods = new() - { - "Managed_SetStringArrayValueAtIndex", - "Managed_SetCurrentStringReturnValueAsUnicode", - "Managed_SetCurrentStringReturnValue", - "Managed_PassCustomCallbackMethodPointers", - "Managed_GetStringArrayValueAtIndex", - "Managed_GetStringArrayLength", - }; + "Managed_SetStringArrayValueAtIndex", + "Managed_SetCurrentStringReturnValueAsUnicode", + "Managed_SetCurrentStringReturnValue", + "Managed_PassCustomCallbackMethodPointers", + "Managed_GetStringArrayValueAtIndex", + "Managed_GetStringArrayLength", + }; - public static void Enable(Harmony harmony, MethodInfo finalizerMethod) - { - var callbacksGeneratedTypes = AccessTools2.AllAssemblies().SelectMany(x => x.GetTypes().Where(y => y.Name.EndsWith("CallbacksGenerated"))); - var callbackGeneratedMethods = callbacksGeneratedTypes.SelectMany(AccessTools.GetDeclaredMethods) - .Where(x => !BlacklistedMethods.Contains(x.Name)) - .Where(x => !BlacklistedMethodStarts.Any(y => x.Name.StartsWith(y))); - foreach (var method in callbackGeneratedMethods.Where(x => x.GetCustomAttributesData().Any(y => y.AttributeType.Name == "MonoPInvokeCallbackAttribute"))) - harmony.Patch(method, finalizer: new HarmonyMethod(finalizerMethod)); - } + public static void Enable(Harmony harmony, MethodInfo finalizerMethod) + { + var callbacksGeneratedTypes = AccessTools2.AllAssemblies().SelectMany(x => x.GetTypes().Where(y => y.Name.EndsWith("CallbacksGenerated"))); + var callbackGeneratedMethods = callbacksGeneratedTypes.SelectMany(AccessTools.GetDeclaredMethods) + .Where(x => !BlacklistedMethods.Contains(x.Name)) + .Where(x => !BlacklistedMethodStarts.Any(y => x.Name.StartsWith(y))); + foreach (var method in callbackGeneratedMethods.Where(x => x.GetCustomAttributesData().Any(y => y.AttributeType.Name == "MonoPInvokeCallbackAttribute"))) + harmony.Patch(method, finalizer: new HarmonyMethod(finalizerMethod)); + } - public static void OnNewAssembly(Harmony harmony, MethodInfo finalizerMethod, Assembly assembly) - { - var callbacksGeneratedTypes = assembly.GetTypes().Where(y => y.Name.EndsWith("CallbacksGenerated")); - var callbackGeneratedMethods = callbacksGeneratedTypes.SelectMany(AccessTools.GetDeclaredMethods) - .Where(x => !BlacklistedMethods.Contains(x.Name)) - .Where(x => !BlacklistedMethodStarts.Any(y => x.Name.StartsWith(y))); - foreach (var method in callbackGeneratedMethods.Where(x => x.GetCustomAttributesData().Any(y => y.AttributeType.Name == "MonoPInvokeCallbackAttribute"))) - harmony.Patch(method, finalizer: new HarmonyMethod(finalizerMethod)); - } + public static void OnNewAssembly(Harmony harmony, MethodInfo finalizerMethod, Assembly assembly) + { + var callbacksGeneratedTypes = assembly.GetTypes().Where(y => y.Name.EndsWith("CallbacksGenerated")); + var callbackGeneratedMethods = callbacksGeneratedTypes.SelectMany(AccessTools.GetDeclaredMethods) + .Where(x => !BlacklistedMethods.Contains(x.Name)) + .Where(x => !BlacklistedMethodStarts.Any(y => x.Name.StartsWith(y))); + foreach (var method in callbackGeneratedMethods.Where(x => x.GetCustomAttributesData().Any(y => y.AttributeType.Name == "MonoPInvokeCallbackAttribute"))) + harmony.Patch(method, finalizer: new HarmonyMethod(finalizerMethod)); } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/ExceptionInterceptor/FinalizerSimple.cs b/src/Bannerlord.BLSE/Features/ExceptionInterceptor/FinalizerSimple.cs index 0d7a4af..13bbe77 100644 --- a/src/Bannerlord.BLSE/Features/ExceptionInterceptor/FinalizerSimple.cs +++ b/src/Bannerlord.BLSE/Features/ExceptionInterceptor/FinalizerSimple.cs @@ -3,39 +3,38 @@ using System.Reflection; -namespace Bannerlord.BLSE.Features.ExceptionInterceptor +namespace Bannerlord.BLSE.Features.ExceptionInterceptor; + +// TaleWorlds.DotNet.Managed:ApplicationTick -> Replicated +// TaleWorlds.Engine.ScriptComponentBehaviour:OnTick -> Called by TaleWorlds.Engine.ManagedScriptHolder:TickComponents +// TaleWorlds.MountAndBlade.Module:OnApplicationTick -> Replicated +// TaleWorlds.MountAndBlade.View.Missions.MissionView:OnMissionScreenTick -> Called by TaleWorlds.MountAndBlade.View.Screen.MissionScreen:OnFrameTick +// TaleWorlds.ScreenSystem.ScreenManager:Tick -> Replicated +// TaleWorlds.MountAndBlade.Mission:Tick -> Replicated +// TaleWorlds.MountAndBlade.MissionBehaviour:OnMissionTick -> Called by TaleWorlds.MountAndBlade.Mission:Tick +// TaleWorlds.MountAndBlade.MBSubModuleBase:OnSubModuleLoad -> Replicated +internal static class FinalizerSimple { - // TaleWorlds.DotNet.Managed:ApplicationTick -> Replicated - // TaleWorlds.Engine.ScriptComponentBehaviour:OnTick -> Called by TaleWorlds.Engine.ManagedScriptHolder:TickComponents - // TaleWorlds.MountAndBlade.Module:OnApplicationTick -> Replicated - // TaleWorlds.MountAndBlade.View.Missions.MissionView:OnMissionScreenTick -> Called by TaleWorlds.MountAndBlade.View.Screen.MissionScreen:OnFrameTick - // TaleWorlds.ScreenSystem.ScreenManager:Tick -> Replicated - // TaleWorlds.MountAndBlade.Mission:Tick -> Replicated - // TaleWorlds.MountAndBlade.MissionBehaviour:OnMissionTick -> Called by TaleWorlds.MountAndBlade.Mission:Tick - // TaleWorlds.MountAndBlade.MBSubModuleBase:OnSubModuleLoad -> Replicated - internal static class FinalizerSimple - { - private static readonly MethodInfo? ModuleInitializeMethod = AccessTools2.Method("TaleWorlds.MountAndBlade.Module:Initialize"); + private static readonly MethodInfo? ModuleInitializeMethod = AccessTools2.Method("TaleWorlds.MountAndBlade.Module:Initialize"); - private static readonly MethodInfo? ManagedApplicationTickMethod = AccessTools2.Method("TaleWorlds.DotNet.Managed:ApplicationTick"); + private static readonly MethodInfo? ManagedApplicationTickMethod = AccessTools2.Method("TaleWorlds.DotNet.Managed:ApplicationTick"); - private static readonly MethodInfo? ScreenManagerPreTickMethod = AccessTools2.Method("TaleWorlds.Engine.EngineScreenManager:PreTick"); - private static readonly MethodInfo? ScreenManagerTickMethod = AccessTools2.Method("TaleWorlds.Engine.EngineScreenManager:Tick"); - private static readonly MethodInfo? ScreenManagerLateTickMethod = AccessTools2.Method("TaleWorlds.Engine.EngineScreenManager:LateTick"); + private static readonly MethodInfo? ScreenManagerPreTickMethod = AccessTools2.Method("TaleWorlds.Engine.EngineScreenManager:PreTick"); + private static readonly MethodInfo? ScreenManagerTickMethod = AccessTools2.Method("TaleWorlds.Engine.EngineScreenManager:Tick"); + private static readonly MethodInfo? ScreenManagerLateTickMethod = AccessTools2.Method("TaleWorlds.Engine.EngineScreenManager:LateTick"); - private static readonly MethodInfo? ManagedScriptHolderTickComponentsMethod = AccessTools2.Method("TaleWorlds.Engine.ManagedScriptHolder:TickComponents"); + private static readonly MethodInfo? ManagedScriptHolderTickComponentsMethod = AccessTools2.Method("TaleWorlds.Engine.ManagedScriptHolder:TickComponents"); - public static void Enable(Harmony harmony, MethodInfo finalizerMethod) - { - harmony.Patch(ModuleInitializeMethod, finalizer: new HarmonyMethod(finalizerMethod)); + public static void Enable(Harmony harmony, MethodInfo finalizerMethod) + { + harmony.Patch(ModuleInitializeMethod, finalizer: new HarmonyMethod(finalizerMethod)); - harmony.Patch(ManagedApplicationTickMethod, finalizer: new HarmonyMethod(finalizerMethod)); + harmony.Patch(ManagedApplicationTickMethod, finalizer: new HarmonyMethod(finalizerMethod)); - harmony.Patch(ScreenManagerPreTickMethod, finalizer: new HarmonyMethod(finalizerMethod)); - harmony.Patch(ScreenManagerTickMethod, finalizer: new HarmonyMethod(finalizerMethod)); - harmony.Patch(ScreenManagerLateTickMethod, finalizer: new HarmonyMethod(finalizerMethod)); + harmony.Patch(ScreenManagerPreTickMethod, finalizer: new HarmonyMethod(finalizerMethod)); + harmony.Patch(ScreenManagerTickMethod, finalizer: new HarmonyMethod(finalizerMethod)); + harmony.Patch(ScreenManagerLateTickMethod, finalizer: new HarmonyMethod(finalizerMethod)); - harmony.Patch(ManagedScriptHolderTickComponentsMethod, finalizer: new HarmonyMethod(finalizerMethod)); - } + harmony.Patch(ManagedScriptHolderTickComponentsMethod, finalizer: new HarmonyMethod(finalizerMethod)); } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/Interceptor/InterceptorFeature.cs b/src/Bannerlord.BLSE/Features/Interceptor/InterceptorFeature.cs index 1473a74..445d0dc 100644 --- a/src/Bannerlord.BLSE/Features/Interceptor/InterceptorFeature.cs +++ b/src/Bannerlord.BLSE/Features/Interceptor/InterceptorFeature.cs @@ -4,41 +4,40 @@ using HarmonyLib; using HarmonyLib.BUTR.Extensions; -namespace Bannerlord.BLSE.Features.Interceptor +namespace Bannerlord.BLSE.Features.Interceptor; + +public static class InterceptorFeature { - public static class InterceptorFeature - { - public static string Id = FeatureIds.InterceptorId; + public static string Id = FeatureIds.InterceptorId; - private delegate void OnInitializeSubModulesPrefixDelegate(); - private delegate void OnLoadSubModulesPostfixDelegate(); + private delegate void OnInitializeSubModulesPrefixDelegate(); + private delegate void OnLoadSubModulesPostfixDelegate(); - public static void Enable(Harmony harmony) - { - ModulePatch.OnInitializeSubModulesPrefix += OnInitializeSubModulesPrefix; - ModulePatch.OnLoadSubModulesPostfix += OnLoadSubModulesPostfix; - ModulePatch.Enable(harmony); - } + public static void Enable(Harmony harmony) + { + ModulePatch.OnInitializeSubModulesPrefix += OnInitializeSubModulesPrefix; + ModulePatch.OnLoadSubModulesPostfix += OnLoadSubModulesPostfix; + ModulePatch.Enable(harmony); + } - private static void OnInitializeSubModulesPrefix() + private static void OnInitializeSubModulesPrefix() + { + foreach (var type in TypeFinder.GetInterceptorTypes(typeof(BLSEInterceptorAttribute))) { - foreach (var type in TypeFinder.GetInterceptorTypes(typeof(BLSEInterceptorAttribute))) + if (AccessTools2.GetDelegate(type, "OnInitializeSubModulesPrefix", logErrorInTrace: false) is { } method) { - if (AccessTools2.GetDelegate(type, "OnInitializeSubModulesPrefix", logErrorInTrace: false) is { } method) - { - method(); - } + method(); } } + } - private static void OnLoadSubModulesPostfix() + private static void OnLoadSubModulesPostfix() + { + foreach (var type in TypeFinder.GetInterceptorTypes(typeof(BLSEInterceptorAttribute))) { - foreach (var type in TypeFinder.GetInterceptorTypes(typeof(BLSEInterceptorAttribute))) + if (AccessTools2.GetDelegate(type, "OnLoadSubModulesPostfix", logErrorInTrace: false) is { } method) { - if (AccessTools2.GetDelegate(type, "OnLoadSubModulesPostfix", logErrorInTrace: false) is { } method) - { - method(); - } + method(); } } } diff --git a/src/Bannerlord.BLSE/Features/Interceptor/Patches/ModulePatch.cs b/src/Bannerlord.BLSE/Features/Interceptor/Patches/ModulePatch.cs index c4b5e0f..a8a4e93 100644 --- a/src/Bannerlord.BLSE/Features/Interceptor/Patches/ModulePatch.cs +++ b/src/Bannerlord.BLSE/Features/Interceptor/Patches/ModulePatch.cs @@ -6,50 +6,49 @@ using Module = TaleWorlds.MountAndBlade.Module; -namespace Bannerlord.BLSE.Features.Interceptor.Patches +namespace Bannerlord.BLSE.Features.Interceptor.Patches; + +internal static class ModulePatch { - internal static class ModulePatch + public static event Action? OnInitializeSubModulesPrefix; + public static event Action? OnLoadSubModulesPostfix; + + private static Harmony? _harmony; + + public static bool Enable(Harmony harmony) + { + _harmony = harmony; + + var res1 = harmony.TryPatch( + AccessTools2.Method(typeof(Module), "LoadSubModules"), + postfix: AccessTools2.Method(typeof(ModulePatch), nameof(LoadSubModulesPostfix))); + if (!res1) return false; + + var res2 = harmony.TryPatch( + AccessTools2.Method(typeof(Module), "InitializeSubModules"), + prefix: AccessTools2.Method(typeof(ModulePatch), nameof(InitializeSubModulesPrefix))); + if (!res2) return false; + + return true; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void InitializeSubModulesPrefix() { - public static event Action? OnInitializeSubModulesPrefix; - public static event Action? OnLoadSubModulesPostfix; - - private static Harmony? _harmony; - - public static bool Enable(Harmony harmony) - { - _harmony = harmony; - - var res1 = harmony.TryPatch( - AccessTools2.Method(typeof(Module), "LoadSubModules"), - postfix: AccessTools2.Method(typeof(ModulePatch), nameof(LoadSubModulesPostfix))); - if (!res1) return false; - - var res2 = harmony.TryPatch( - AccessTools2.Method(typeof(Module), "InitializeSubModules"), - prefix: AccessTools2.Method(typeof(ModulePatch), nameof(InitializeSubModulesPrefix))); - if (!res2) return false; - - return true; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static void InitializeSubModulesPrefix() - { - OnInitializeSubModulesPrefix?.Invoke(); - - _harmony?.Unpatch( - AccessTools2.Method(typeof(Module), "InitializeSubModules"), - AccessTools2.Method(typeof(ModulePatch), nameof(InitializeSubModulesPrefix))); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static void LoadSubModulesPostfix() - { - OnLoadSubModulesPostfix?.Invoke(); - - _harmony?.Unpatch( - AccessTools2.Method(typeof(Module), "LoadSubModules"), - AccessTools2.Method(typeof(ModulePatch), nameof(LoadSubModulesPostfix))); - } + OnInitializeSubModulesPrefix?.Invoke(); + + _harmony?.Unpatch( + AccessTools2.Method(typeof(Module), "InitializeSubModules"), + AccessTools2.Method(typeof(ModulePatch), nameof(InitializeSubModulesPrefix))); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void LoadSubModulesPostfix() + { + OnLoadSubModulesPostfix?.Invoke(); + + _harmony?.Unpatch( + AccessTools2.Method(typeof(Module), "LoadSubModules"), + AccessTools2.Method(typeof(ModulePatch), nameof(LoadSubModulesPostfix))); } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/Xbox/Patches/ModulePatch.cs b/src/Bannerlord.BLSE/Features/Xbox/Patches/ModulePatch.cs index 916f79b..c7a5558 100644 --- a/src/Bannerlord.BLSE/Features/Xbox/Patches/ModulePatch.cs +++ b/src/Bannerlord.BLSE/Features/Xbox/Patches/ModulePatch.cs @@ -9,50 +9,49 @@ using Module = TaleWorlds.MountAndBlade.Module; -namespace Bannerlord.BLSE.Features.Xbox.Patches +namespace Bannerlord.BLSE.Features.Xbox.Patches; + +internal static class ModulePatch { - internal static class ModulePatch + public static bool Enable(Harmony harmony) + { + var asm = Assembly.LoadFrom("TaleWorlds.MountAndBlade.Platform.GDK.dll"); + Trace.Assert(asm is not null); + + var res1 = harmony.TryPatch( + AccessTools2.Constructor(typeof(Module)), + prefix: AccessTools2.Method(typeof(ModulePatch), nameof(ShowedLoginScreenPrefix))); + if (!res1) return false; + + var res2 = harmony.TryPatch( + AccessTools2.DeclaredMethod("TaleWorlds.MountAndBlade.Platform.GDK.PlatformGDKSubModule:OnSubModuleLoad"), + prefix: AccessTools2.Method(typeof(ModulePatch), nameof(OnSubModuleLoadPrefix))); + if (!res2) return false; + + var res3 = harmony.TryPatch( + AccessTools2.DeclaredMethod("TaleWorlds.MountAndBlade.Platform.GDK.PlatformGDKSubModule:OnApplicationTick"), + prefix: AccessTools2.Method(typeof(ModulePatch), nameof(OnApplicationTickPrefix))); + if (!res3) return false; + + return true; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ShowedLoginScreenPrefix(ref bool ___ShowedLoginScreen) + { + ___ShowedLoginScreen = true; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool OnSubModuleLoadPrefix() + { + Common.PlatformFileHelper = new PlatformFileHelperPC("Mount and Blade II Bannerlord"); + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool OnApplicationTickPrefix() { - public static bool Enable(Harmony harmony) - { - var asm = Assembly.LoadFrom("TaleWorlds.MountAndBlade.Platform.GDK.dll"); - Trace.Assert(asm is not null); - - var res1 = harmony.TryPatch( - AccessTools2.Constructor(typeof(Module)), - prefix: AccessTools2.Method(typeof(ModulePatch), nameof(ShowedLoginScreenPrefix))); - if (!res1) return false; - - var res2 = harmony.TryPatch( - AccessTools2.DeclaredMethod("TaleWorlds.MountAndBlade.Platform.GDK.PlatformGDKSubModule:OnSubModuleLoad"), - prefix: AccessTools2.Method(typeof(ModulePatch), nameof(OnSubModuleLoadPrefix))); - if (!res2) return false; - - var res3 = harmony.TryPatch( - AccessTools2.DeclaredMethod("TaleWorlds.MountAndBlade.Platform.GDK.PlatformGDKSubModule:OnApplicationTick"), - prefix: AccessTools2.Method(typeof(ModulePatch), nameof(OnApplicationTickPrefix))); - if (!res3) return false; - - return true; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static void ShowedLoginScreenPrefix(ref bool ___ShowedLoginScreen) - { - ___ShowedLoginScreen = true; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static bool OnSubModuleLoadPrefix() - { - Common.PlatformFileHelper = new PlatformFileHelperPC("Mount and Blade II Bannerlord"); - return false; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static bool OnApplicationTickPrefix() - { - return false; - } + return false; } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Features/Xbox/XboxFeature.cs b/src/Bannerlord.BLSE/Features/Xbox/XboxFeature.cs index 95e4956..b4041d9 100644 --- a/src/Bannerlord.BLSE/Features/Xbox/XboxFeature.cs +++ b/src/Bannerlord.BLSE/Features/Xbox/XboxFeature.cs @@ -4,21 +4,20 @@ using System.IO; -namespace Bannerlord.BLSE.Features.Xbox +namespace Bannerlord.BLSE.Features.Xbox; + +/// +/// We currently just bypass the sign in screen on launch. Don't think it's very safe, but it works for now. +/// +public static class XboxFeature { - /// - /// We currently just bypass the sign in screen on launch. Don't think it's very safe, but it works for now. - /// - public static class XboxFeature - { - public static string Id = FeatureIds.XboxId; + public static string Id = FeatureIds.XboxId; - public static void Enable(Harmony harmony) - { - if (Path.GetFileName(Directory.GetCurrentDirectory()) != "Gaming.Desktop.x64_Shipping_Client") - return; + public static void Enable(Harmony harmony) + { + if (Path.GetFileName(Directory.GetCurrentDirectory()) != "Gaming.Desktop.x64_Shipping_Client") + return; - ModulePatch.Enable(harmony); - } + ModulePatch.Enable(harmony); } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Utils/AssemblyWrapper.cs b/src/Bannerlord.BLSE/Utils/AssemblyWrapper.cs index 1e35436..30f899f 100644 --- a/src/Bannerlord.BLSE/Utils/AssemblyWrapper.cs +++ b/src/Bannerlord.BLSE/Utils/AssemblyWrapper.cs @@ -1,11 +1,10 @@ using System.Reflection; -namespace Bannerlord.BLSE.Utils +namespace Bannerlord.BLSE.Utils; + +internal class AssemblyWrapper : Assembly { - internal class AssemblyWrapper : Assembly - { - public override string Location { get; } + public override string Location { get; } - public AssemblyWrapper(string location) => Location = location; - } + public AssemblyWrapper(string location) => Location = location; } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Utils/CommandLineSplitter.cs b/src/Bannerlord.BLSE/Utils/CommandLineSplitter.cs index 0b3959a..e58bf81 100644 --- a/src/Bannerlord.BLSE/Utils/CommandLineSplitter.cs +++ b/src/Bannerlord.BLSE/Utils/CommandLineSplitter.cs @@ -1,111 +1,110 @@ using System.Collections.Generic; -namespace Bannerlord.BLSE.Utils +namespace Bannerlord.BLSE.Utils; + +internal static class CommandLineSplitter { - internal static class CommandLineSplitter + private enum Boundary { - private enum Boundary - { - TokenStart, - WordEnd, - QuoteStart, - QuoteEnd - } + TokenStart, + WordEnd, + QuoteStart, + QuoteEnd + } - public static IEnumerable SplitCommandLine(string commandLine) - { - var startTokenIndex = 0; + public static IEnumerable SplitCommandLine(string commandLine) + { + var startTokenIndex = 0; - var pos = 0; + var pos = 0; - var seeking = Boundary.TokenStart; - var seekingQuote = Boundary.QuoteStart; + var seeking = Boundary.TokenStart; + var seekingQuote = Boundary.QuoteStart; - while (pos < commandLine.Length) - { - var c = commandLine[pos]; + while (pos < commandLine.Length) + { + var c = commandLine[pos]; - if (char.IsWhiteSpace(c)) + if (char.IsWhiteSpace(c)) + { + if (seekingQuote == Boundary.QuoteStart) { - if (seekingQuote == Boundary.QuoteStart) + switch (seeking) { - switch (seeking) - { - case Boundary.WordEnd: - yield return CurrentToken(); - startTokenIndex = pos; - seeking = Boundary.TokenStart; - break; - - case Boundary.TokenStart: - startTokenIndex = pos; - break; - } + case Boundary.WordEnd: + yield return CurrentToken(); + startTokenIndex = pos; + seeking = Boundary.TokenStart; + break; + + case Boundary.TokenStart: + startTokenIndex = pos; + break; } } - else if (c == '\"') + } + else if (c == '\"') + { + if (seeking == Boundary.TokenStart) { - if (seeking == Boundary.TokenStart) + switch (seekingQuote) { - switch (seekingQuote) - { - case Boundary.QuoteEnd: - yield return CurrentToken(); - startTokenIndex = pos; - seekingQuote = Boundary.QuoteStart; - break; - - case Boundary.QuoteStart: - startTokenIndex = pos + 1; - seekingQuote = Boundary.QuoteEnd; - break; - } - } - else - { - switch (seekingQuote) - { - case Boundary.QuoteEnd: - seekingQuote = Boundary.QuoteStart; - break; - - case Boundary.QuoteStart: - seekingQuote = Boundary.QuoteEnd; - break; - } + case Boundary.QuoteEnd: + yield return CurrentToken(); + startTokenIndex = pos; + seekingQuote = Boundary.QuoteStart; + break; + + case Boundary.QuoteStart: + startTokenIndex = pos + 1; + seekingQuote = Boundary.QuoteEnd; + break; } } - else if (seeking == Boundary.TokenStart && seekingQuote == Boundary.QuoteStart) - { - seeking = Boundary.WordEnd; - startTokenIndex = pos; - } - - Advance(); - - if (IsAtEndOfInput()) + else { - switch (seeking) + switch (seekingQuote) { - case Boundary.TokenStart: + case Boundary.QuoteEnd: + seekingQuote = Boundary.QuoteStart; break; - default: - yield return CurrentToken(); + + case Boundary.QuoteStart: + seekingQuote = Boundary.QuoteEnd; break; } } } + else if (seeking == Boundary.TokenStart && seekingQuote == Boundary.QuoteStart) + { + seeking = Boundary.WordEnd; + startTokenIndex = pos; + } - void Advance() => pos++; + Advance(); - string CurrentToken() + if (IsAtEndOfInput()) { - return commandLine.Substring(startTokenIndex, IndexOfEndOfToken()).Replace("\"", ""); + switch (seeking) + { + case Boundary.TokenStart: + break; + default: + yield return CurrentToken(); + break; + } } + } - int IndexOfEndOfToken() => pos - startTokenIndex; + void Advance() => pos++; - bool IsAtEndOfInput() => pos == commandLine.Length; + string CurrentToken() + { + return commandLine.Substring(startTokenIndex, IndexOfEndOfToken()).Replace("\"", ""); } + + int IndexOfEndOfToken() => pos - startTokenIndex; + + bool IsAtEndOfInput() => pos == commandLine.Length; } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Utils/GameUtils.cs b/src/Bannerlord.BLSE/Utils/GameUtils.cs index 641d273..f5f8723 100644 --- a/src/Bannerlord.BLSE/Utils/GameUtils.cs +++ b/src/Bannerlord.BLSE/Utils/GameUtils.cs @@ -3,26 +3,25 @@ using System; -namespace Bannerlord.BLSE.Utils +namespace Bannerlord.BLSE.Utils; + +internal static class GameUtils { - internal static class GameUtils - { - private static readonly Lazy EngineApplicationInterfaceType = - new(() => AccessTools2.TypeByName("TaleWorlds.Engine.EngineApplicationInterface")); + private static readonly Lazy EngineApplicationInterfaceType = + new(() => AccessTools2.TypeByName("TaleWorlds.Engine.EngineApplicationInterface")); - private static readonly Lazy?> IUtilField = - new(() => AccessTools2.StaticFieldRefAccess(EngineApplicationInterfaceType.Value!, "IUtil")); + private static readonly Lazy?> IUtilField = + new(() => AccessTools2.StaticFieldRefAccess(EngineApplicationInterfaceType.Value!, "IUtil")); - private delegate string GetModulesCodeDelegate(object instance); + private delegate string GetModulesCodeDelegate(object instance); - public static string[]? GetModulesNames() - { - var iUtil = IUtilField.Value?.Invoke(); - // Don't use Trace, as this is used in critical code like AssemblyResolver. - // The less we trigger custom code, the better - // A custom Trace listener will break the resolver if it will trigger a recursive assembly resolution - var getModulesCode = AccessTools2.GetDelegate(iUtil, "GetModulesCode", logErrorInTrace: false); - return iUtil is not null && getModulesCode is not null ? getModulesCode(iUtil)?.Split('*') : null; - } + public static string[]? GetModulesNames() + { + var iUtil = IUtilField.Value?.Invoke(); + // Don't use Trace, as this is used in critical code like AssemblyResolver. + // The less we trigger custom code, the better + // A custom Trace listener will break the resolver if it will trigger a recursive assembly resolution + var getModulesCode = AccessTools2.GetDelegate(iUtil, "GetModulesCode", logErrorInTrace: false); + return iUtil is not null && getModulesCode is not null ? getModulesCode(iUtil)?.Split('*') : null; } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE/Utils/TypeFinder.cs b/src/Bannerlord.BLSE/Utils/TypeFinder.cs index 3ffdfcd..6dddbaa 100644 --- a/src/Bannerlord.BLSE/Utils/TypeFinder.cs +++ b/src/Bannerlord.BLSE/Utils/TypeFinder.cs @@ -8,50 +8,49 @@ using TaleWorlds.Library; using TaleWorlds.MountAndBlade; -namespace Bannerlord.BLSE.Utils +namespace Bannerlord.BLSE.Utils; + +internal static class TypeFinder { - internal static class TypeFinder + public static IEnumerable GetInterceptorTypes(Type attributeType) { - public static IEnumerable GetInterceptorTypes(Type attributeType) - { - bool CheckType(Type type) => type.GetCustomAttributes() - .Any(att => string.Equals(att.GetType().FullName, attributeType.FullName, StringComparison.Ordinal)); + bool CheckType(Type type) => type.GetCustomAttributes() + .Any(att => string.Equals(att.GetType().FullName, attributeType.FullName, StringComparison.Ordinal)); - var dlls = new HashSet(GetLoadedModulePaths()); - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic && dlls.Contains(x.Location))) + var dlls = new HashSet(GetLoadedModulePaths()); + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic && dlls.Contains(x.Location))) + { + IEnumerable enumerable; + try { - IEnumerable enumerable; - try - { - enumerable = assembly.GetTypes().Where(CheckType).ToArray(); // Force type resolution - } - catch (TypeLoadException) - { - enumerable = Enumerable.Empty(); // ignore the incompatibility, not our problem - } - catch (ReflectionTypeLoadException) - { - enumerable = Enumerable.Empty(); // ignore the incompatibility, not our problem - } - foreach (var type in enumerable) - { - yield return type; - } + enumerable = assembly.GetTypes().Where(CheckType).ToArray(); // Force type resolution + } + catch (TypeLoadException) + { + enumerable = Enumerable.Empty(); // ignore the incompatibility, not our problem + } + catch (ReflectionTypeLoadException) + { + enumerable = Enumerable.Empty(); // ignore the incompatibility, not our problem + } + foreach (var type in enumerable) + { + yield return type; } } + } - private static IEnumerable GetLoadedModulePaths() - { - var configName = Common.ConfigName; + private static IEnumerable GetLoadedModulePaths() + { + var configName = Common.ConfigName; - foreach (var moduleInfo in ModuleInfoHelper.GetLoadedModules()) + foreach (var moduleInfo in ModuleInfoHelper.GetLoadedModules()) + { + foreach (var subModule in moduleInfo.SubModules) { - foreach (var subModule in moduleInfo.SubModules) + if (ModuleInfoHelper.CheckIfSubModuleCanBeLoaded(subModule, ApplicationPlatform.CurrentPlatform, ApplicationPlatform.CurrentRuntimeLibrary, DedicatedServerType.None, false)) { - if (ModuleInfoHelper.CheckIfSubModuleCanBeLoaded(subModule, ApplicationPlatform.CurrentPlatform, ApplicationPlatform.CurrentRuntimeLibrary, DedicatedServerType.None, false)) - { - yield return System.IO.Path.GetFullPath(System.IO.Path.Combine(moduleInfo.Path, "bin", configName, subModule.DLLName)); - } + yield return System.IO.Path.GetFullPath(System.IO.Path.Combine(moduleInfo.Path, "bin", configName, subModule.DLLName)); } } } diff --git a/src/Bannerlord.BLSE/Utils/TypeWrapper.cs b/src/Bannerlord.BLSE/Utils/TypeWrapper.cs index 28b0c3e..0c47764 100644 --- a/src/Bannerlord.BLSE/Utils/TypeWrapper.cs +++ b/src/Bannerlord.BLSE/Utils/TypeWrapper.cs @@ -2,50 +2,49 @@ using System.Globalization; using System.Reflection; -namespace Bannerlord.BLSE.Utils +namespace Bannerlord.BLSE.Utils; + +internal class TypeWrapper : Type { - internal class TypeWrapper : Type - { - public override string Name { get; } = default!; - public override Guid GUID { get; } = default!; - public override Module Module { get; } = default!; - public override Assembly Assembly { get; } = default!; - public override string FullName { get; } = default!; - public override string Namespace { get; } = default!; - public override string AssemblyQualifiedName { get; } = default!; - public override Type BaseType { get; } = default!; - public override Type UnderlyingSystemType { get; } = default!; + public override string Name { get; } = default!; + public override Guid GUID { get; } = default!; + public override Module Module { get; } = default!; + public override Assembly Assembly { get; } = default!; + public override string FullName { get; } = default!; + public override string Namespace { get; } = default!; + public override string AssemblyQualifiedName { get; } = default!; + public override Type BaseType { get; } = default!; + public override Type UnderlyingSystemType { get; } = default!; - public TypeWrapper(string location) => Assembly = new AssemblyWrapper(location); + public TypeWrapper(string location) => Assembly = new AssemblyWrapper(location); - public override object[] GetCustomAttributes(bool inherit) => new object[] { }; - public override bool IsDefined(Type attributeType, bool inherit) => false; - public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => new ConstructorInfo[] { }; - public override Type? GetInterface(string name, bool ignoreCase) => null; - public override Type[] GetInterfaces() => new Type[] { }; - public override EventInfo? GetEvent(string name, BindingFlags bindingAttr) => null; - public override EventInfo[] GetEvents(BindingFlags bindingAttr) => new EventInfo[] { }; - public override Type[] GetNestedTypes(BindingFlags bindingAttr) => new Type[] { }; - public override Type? GetNestedType(string name, BindingFlags bindingAttr) => null; - public override Type? GetElementType() => null; - protected override bool HasElementTypeImpl() => false; - protected override PropertyInfo? GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder, Type? returnType, Type[]? types, ParameterModifier[]? modifiers) => null; - public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => new PropertyInfo[] { }; - protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) => null; - public override MethodInfo[] GetMethods(BindingFlags bindingAttr) => new MethodInfo[] { }; - public override FieldInfo? GetField(string name, BindingFlags bindingAttr) => null; - public override FieldInfo[] GetFields(BindingFlags bindingAttr) => new FieldInfo[] { }; - public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => new MemberInfo[] { }; - protected override TypeAttributes GetAttributeFlagsImpl() => TypeAttributes.NotPublic; - protected override bool IsArrayImpl() => false; - protected override bool IsByRefImpl() => false; - protected override bool IsPointerImpl() => false; - protected override bool IsPrimitiveImpl() => false; - protected override bool IsCOMObjectImpl() => false; - public override object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, object?[]? args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? namedParameters) => null; - protected override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[] types, ParameterModifier[]? modifiers) => null; + public override object[] GetCustomAttributes(bool inherit) => new object[] { }; + public override bool IsDefined(Type attributeType, bool inherit) => false; + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => new ConstructorInfo[] { }; + public override Type? GetInterface(string name, bool ignoreCase) => null; + public override Type[] GetInterfaces() => new Type[] { }; + public override EventInfo? GetEvent(string name, BindingFlags bindingAttr) => null; + public override EventInfo[] GetEvents(BindingFlags bindingAttr) => new EventInfo[] { }; + public override Type[] GetNestedTypes(BindingFlags bindingAttr) => new Type[] { }; + public override Type? GetNestedType(string name, BindingFlags bindingAttr) => null; + public override Type? GetElementType() => null; + protected override bool HasElementTypeImpl() => false; + protected override PropertyInfo? GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder, Type? returnType, Type[]? types, ParameterModifier[]? modifiers) => null; + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => new PropertyInfo[] { }; + protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) => null; + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) => new MethodInfo[] { }; + public override FieldInfo? GetField(string name, BindingFlags bindingAttr) => null; + public override FieldInfo[] GetFields(BindingFlags bindingAttr) => new FieldInfo[] { }; + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => new MemberInfo[] { }; + protected override TypeAttributes GetAttributeFlagsImpl() => TypeAttributes.NotPublic; + protected override bool IsArrayImpl() => false; + protected override bool IsByRefImpl() => false; + protected override bool IsPointerImpl() => false; + protected override bool IsPrimitiveImpl() => false; + protected override bool IsCOMObjectImpl() => false; + public override object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, object?[]? args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? namedParameters) => null; + protected override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[] types, ParameterModifier[]? modifiers) => null; - public override object[] GetCustomAttributes(Type attributeType, bool inherit) => new object[] { }; - } + public override object[] GetCustomAttributes(Type attributeType, bool inherit) => new object[] { }; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Adapters/DialogProviderImpl.cs b/src/Bannerlord.LauncherEx/Adapters/DialogProviderImpl.cs new file mode 100644 index 0000000..4cee407 --- /dev/null +++ b/src/Bannerlord.LauncherEx/Adapters/DialogProviderImpl.cs @@ -0,0 +1,70 @@ +using Bannerlord.LauncherEx.Helpers.Input; +using Bannerlord.LauncherManager.External.UI; +using Bannerlord.LauncherManager.Localization; +using Bannerlord.LauncherManager.Models; + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Bannerlord.LauncherEx.Adapters; + +internal sealed class DialogProviderImpl : IDialogProvider +{ + public static readonly DialogProviderImpl Instance = new(); + + public void SendDialog(DialogType type, string title, string message, IReadOnlyList filters, Action onResult) + { + switch (type) + { + case DialogType.Warning: + { + var split = message.Split(new[] { "--CONTENT-SPLIT--" }, StringSplitOptions.RemoveEmptyEntries); + var result = MessageBoxDialog.Show( + string.Join("\n", split), + new BUTRTextObject(title).ToString(), + MessageBoxButtons.OkCancel, + MessageBoxIcon.Warning, + 0, + 0 + ); + onResult(result == MessageBoxResult.Ok ? "true" : "false"); + return; + } + case DialogType.FileOpen: + { + var filter = string.Join("|", filters.Select(x => $"{x.Name} {string.Join((string) ", ", x.Extensions)}|{string.Join((string) ", ", x.Extensions)}")); + var dialog = new OpenFileDialog + { + Title = title, + Filter = filter, + + CheckFileExists = true, + CheckPathExists = true, + ReadOnlyChecked = true, + Multiselect = false, + ValidateNames = true, + }; + onResult(dialog.ShowDialog() ? dialog.FileName ?? string.Empty : string.Empty); + return; + } + case DialogType.FileSave: + { + var fileName = message; + var filter = string.Join("|", filters.Select(x => $"{x.Name} {string.Join(", ", x.Extensions)}|{string.Join(", ", x.Extensions)}")); + var dialog = new SaveFileDialog + { + Title = title, + Filter = filter, + FileName = fileName, + + CheckPathExists = false, + + ValidateNames = true, + }; + onResult(dialog.ShowDialog() ? dialog.FileName : string.Empty); + return; + } + } + } +} \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Adapters/FileSystemProviderImpl.cs b/src/Bannerlord.LauncherEx/Adapters/FileSystemProviderImpl.cs new file mode 100644 index 0000000..95841c5 --- /dev/null +++ b/src/Bannerlord.LauncherEx/Adapters/FileSystemProviderImpl.cs @@ -0,0 +1,60 @@ +using Bannerlord.LauncherManager.External; + +using System; +using System.IO; + +namespace Bannerlord.LauncherEx.Adapters; + +internal sealed class FileSystemProviderImpl : IFileSystemProvider +{ + public static readonly FileSystemProviderImpl Instance = new(); + + public byte[]? ReadFileContent(string filePath, int offset, int length) + { + if (!File.Exists(filePath)) return null; + + try + { + if (offset == 0 && length == -1) + { + return File.ReadAllBytes(filePath); + } + else if (offset >= 0 && length > 0) + { + using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); + var data = new byte[length]; + fs.Seek(offset, SeekOrigin.Begin); + _ = fs.Read(data, 0, length); + return data; + } + else + { + return null; + } + } + catch (Exception) + { + //_logger.LogError(ex, "Bannerlord IO Read Operation failed! {Path}", filePath); + return null; + } + } + + public void WriteFileContent(string filePath, byte[]? data) + { + try + { + if (data is null) + File.Delete(filePath); + else + File.WriteAllBytes(filePath, data); + } + catch (Exception) + { + //_logger.LogError(ex, "Bannerlord IO Write Operation failed! {Path}", filePath); + } + } + + public string[]? ReadDirectoryFileList(string directoryPath) => Directory.Exists(directoryPath) ? Directory.GetFiles(directoryPath) : null; + + public string[]? ReadDirectoryList(string directoryPath) => Directory.Exists(directoryPath) ? Directory.GetDirectories(directoryPath) : null; +} \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Adapters/GameInfoProviderImpl.cs b/src/Bannerlord.LauncherEx/Adapters/GameInfoProviderImpl.cs new file mode 100644 index 0000000..874d8dd --- /dev/null +++ b/src/Bannerlord.LauncherEx/Adapters/GameInfoProviderImpl.cs @@ -0,0 +1,15 @@ +using Bannerlord.LauncherManager.External; + +using System; +using System.IO; + +namespace Bannerlord.LauncherEx.Adapters; + +internal sealed class GameInfoProviderImpl : IGameInfoProvider +{ + public static readonly GameInfoProviderImpl Instance = new(); + + private readonly string _installPath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory!, "../", "../")); + + public string GetInstallPath() => _installPath; +} \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Adapters/NotificationProviderImpl.cs b/src/Bannerlord.LauncherEx/Adapters/NotificationProviderImpl.cs new file mode 100644 index 0000000..8cc0811 --- /dev/null +++ b/src/Bannerlord.LauncherEx/Adapters/NotificationProviderImpl.cs @@ -0,0 +1,51 @@ +using Bannerlord.LauncherEx.Helpers; +using Bannerlord.LauncherEx.Helpers.Input; +using Bannerlord.LauncherManager.External.UI; +using Bannerlord.LauncherManager.Localization; +using Bannerlord.LauncherManager.Models; + +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace Bannerlord.LauncherEx.Adapters; + +internal sealed class NotificationProviderImpl : INotificationProvider +{ + public static readonly NotificationProviderImpl Instance = new(); + + private readonly ConcurrentDictionary _ids = new(); + + public void SendNotification(string id, NotificationType type, string message, uint displayMs) + { + if (string.IsNullOrEmpty(id)) id = Guid.NewGuid().ToString(); + + // Prevents message spam + if (_ids.TryAdd(id, null)) return; + using var cts = new CancellationTokenSource(); + _ = Task.Delay(TimeSpan.FromMilliseconds(displayMs), cts.Token).ContinueWith(x => _ids.TryRemove(id, out _), CancellationToken.None); + + var translatedMessage = new BUTRTextObject(message).ToString(); + switch (type) + { + case NotificationType.Hint: + { + HintManager.ShowHint(translatedMessage); + cts.Cancel(); + break; + } + case NotificationType.Info: + { + // TODO: + HintManager.ShowHint(translatedMessage); + cts.Cancel(); + break; + } + default: + MessageBoxDialog.Show(translatedMessage, "Information", MessageBoxButtons.Ok); + cts.Cancel(); + break; + } + } +} \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.Callbacks.cs b/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.Callbacks.cs new file mode 100644 index 0000000..50df708 --- /dev/null +++ b/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.Callbacks.cs @@ -0,0 +1,22 @@ +using Bannerlord.LauncherManager.Models; + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Bannerlord.LauncherEx; + +partial class BUTRLauncherManagerHandler +{ + private LauncherState GetStateCallback() => _getState?.Invoke() ?? LauncherState.Empty; + + private IModuleViewModel[] GetAllModuleViewModelsCallback() => _getAllModuleViewModels?.Invoke()?.ToArray() ?? Array.Empty(); + private IModuleViewModel[] GetModuleViewModelsCallback() => _getModuleViewModels?.Invoke()?.ToArray() ?? Array.Empty(); + private void SetModuleViewModelsCallback(IReadOnlyList orderedViewModels) => _setModuleViewModels?.Invoke(orderedViewModels); + + private void SetGameParametersCallback(string executable, IReadOnlyList parameters) + { + _executable = executable; + _executableParameters = string.Join(" ", parameters); + } +} \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.Save.cs b/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.Save.cs index 701cb99..56a70fe 100644 --- a/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.Save.cs +++ b/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.Save.cs @@ -1,6 +1,7 @@ using Bannerlord.LauncherEx.Extensions; using Bannerlord.LauncherManager.Models; +using System; using System.IO; using System.Linq; @@ -8,40 +9,39 @@ using TaleWorlds.Library; using TaleWorlds.SaveSystem; -namespace Bannerlord.LauncherEx +namespace Bannerlord.LauncherEx; + +partial class BUTRLauncherManagerHandler { - partial class BUTRLauncherManagerHandler + public override SaveMetadata[] GetSaveFiles() => MBSaveLoad.GetSaveFiles().Where(x => x.MetaData is not null).Select(x => { - public override SaveMetadata[] GetSaveFiles() => MBSaveLoad.GetSaveFiles().Where(x => x.MetaData is not null).Select(x => - { - var dict = new SaveMetadata(x.Name); - foreach (var key in x.MetaData.Keys) - dict.Add(key, x.MetaData[key]); - return dict; - }).ToArray(); + var dict = new SaveMetadata(x.Name); + foreach (var key in x.MetaData.Keys) + dict.Add(key, x.MetaData[key]); + return dict; + }).ToArray(); - public override SaveMetadata GetSaveMetadata(string fileName, byte[] data) - { - using var stream = new MemoryStream(data); - var metadata = MetaData.Deserialize(stream); - var dict = new SaveMetadata(fileName); - foreach (var key in metadata.Keys) - dict.Add(key, metadata[key]); - return dict; - } + public override SaveMetadata GetSaveMetadata(string fileName, ReadOnlySpan data) + { + using var stream = new MemoryStream(data.ToArray()); + var metadata = MetaData.Deserialize(stream); + var dict = new SaveMetadata(fileName); + foreach (var key in metadata.Keys) + dict.Add(key, metadata[key]); + return dict; + } - private static string? GetSaveFilePath(SaveGameFileInfo saveGameFileInfo) - { - var savesDirectory = new PlatformDirectoryPath(PlatformFileType.User, "Game Saves\\"); - if (PlatformFileHelperPCExtended.GetDirectoryFullPath(savesDirectory) is not { } savesDirectoryPath) return null; - return Path.Combine(savesDirectoryPath, $"{saveGameFileInfo.Name}.sav"); - } + private static string? GetSaveFilePath(SaveGameFileInfo saveGameFileInfo) + { + var savesDirectory = new PlatformDirectoryPath(PlatformFileType.User, "Game Saves\\"); + if (PlatformFileHelperPCExtended.GetDirectoryFullPath(savesDirectory) is not { } savesDirectoryPath) return null; + return Path.Combine(savesDirectoryPath, $"{saveGameFileInfo.Name}.sav"); + } - public override string? GetSaveFilePath(string saveFile) - { - if (MBSaveLoad.GetSaveFileWithName(saveFile) is not { } si || GetSaveFilePath(si) is not { } saveFilePath || !File.Exists(saveFilePath)) - return null; - return saveFilePath; - } + public override string? GetSaveFilePath(string saveFile) + { + if (MBSaveLoad.GetSaveFileWithName(saveFile) is not { } si || GetSaveFilePath(si) is not { } saveFilePath || !File.Exists(saveFilePath)) + return null; + return saveFilePath; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.Utils.cs b/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.Utils.cs index 45c2bab..f2cd4c4 100644 --- a/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.Utils.cs +++ b/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.Utils.cs @@ -2,43 +2,43 @@ using Bannerlord.LauncherManager.Models; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; -namespace Bannerlord.LauncherEx -{ - partial class BUTRLauncherManagerHandler - { - public void SetGameParametersLoadOrder(IEnumerable modules) => SaveLoadOrder(GetFromViewModel(modules)); +namespace Bannerlord.LauncherEx; +partial class BUTRLauncherManagerHandler +{ + public void SetGameParametersLoadOrder(IEnumerable modules) => SaveLoadOrder(GetFromViewModel(modules)); - public override string GetGameVersion() => ApplicationVersionHelper.GameVersionStr(); - public override int GetChangeset() => typeof(TaleWorlds.Library.ApplicationVersion).GetField("DefaultChangeSet")?.GetValue(null) as int? ?? 0; + public override string GetGameVersion() => ApplicationVersionHelper.GameVersionStr(); - protected override IEnumerable ReloadModules() => ModuleInfoHelper.GetModules().Select(x => new ModuleInfoExtendedWithPath(x, x.Path)); + public override int GetChangeset() => typeof(TaleWorlds.Library.ApplicationVersion).GetField("DefaultChangeSet")?.GetValue(null) as int? ?? 0; - // More of a reminder how the callbacks should be handled if needed in C# - public Task ShowWarning(string title, string contentPrimary, string contentSecondary) - { - var tcs = new TaskCompletionSource(); - base.ShowWarning(title, contentPrimary, contentSecondary, tcs.SetResult); - return tcs.Task; - } + // More of a reminder how the callbacks should be handled if needed in C# + public Task ShowWarning(string title, string contentPrimary, string contentSecondary) + { + var tcs = new TaskCompletionSource(); + base.ShowWarning(title, contentPrimary, contentSecondary, tcs.SetResult); + return tcs.Task; + } - public Task ShowFileOpen(string title, IReadOnlyList filters) - { - var tcs = new TaskCompletionSource(); - base.ShowFileOpen(title, filters, tcs.SetResult); - return tcs.Task; - } + public Task ShowFileOpen(string title, IReadOnlyList filters) + { + var tcs = new TaskCompletionSource(); + base.ShowFileOpen(title, filters, tcs.SetResult); + return tcs.Task; + } - public Task ShowFileSave(string title, string fileName, IReadOnlyList filters) - { - var tcs = new TaskCompletionSource(); - base.ShowFileSave(title, fileName, filters, tcs.SetResult); - return tcs.Task; - } + public Task ShowFileSave(string title, string fileName, IReadOnlyList filters) + { + var tcs = new TaskCompletionSource(); + base.ShowFileSave(title, fileName, filters, tcs.SetResult); + return tcs.Task; } + + public new LoadOrder LoadLoadOrder() => base.LoadLoadOrder(); + + public new IReadOnlyList GetAllModules() => base.GetAllModules(); } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.cs b/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.cs index 3747b1a..89307e8 100644 --- a/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.cs +++ b/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.cs @@ -1,8 +1,8 @@ using Bannerlord.BUTR.Shared.Extensions; -using Bannerlord.LauncherEx.Helpers; -using Bannerlord.LauncherEx.Helpers.Input; +using Bannerlord.LauncherEx.Adapters; using Bannerlord.LauncherManager; -using Bannerlord.LauncherManager.Localization; +using Bannerlord.LauncherManager.External; +using Bannerlord.LauncherManager.External.UI; using Bannerlord.LauncherManager.Models; using Bannerlord.ModuleManager; @@ -10,234 +10,122 @@ using HarmonyLib.BUTR.Extensions; using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using TaleWorlds.MountAndBlade.Launcher.Library; using TaleWorlds.MountAndBlade.Launcher.Library.UserDatas; -namespace Bannerlord.LauncherEx -{ - internal partial class BUTRLauncherManagerHandler : LauncherManagerHandler - { - private static readonly Harmony _harmony = new("Bannerlord.LauncherEx.launchermanager"); - public static BUTRLauncherManagerHandler Default = default!; +namespace Bannerlord.LauncherEx; - public static void Initialize(UserDataManager userDataManager) => Default = new BUTRLauncherManagerHandler(userDataManager); - - public new Dictionary ExtendedModuleInfoCache => base.ExtendedModuleInfoCache; +internal partial class BUTRLauncherManagerHandler : LauncherManagerHandler +{ + private static readonly Harmony _harmony = new("Bannerlord.LauncherEx.launchermanager"); + public static BUTRLauncherManagerHandler Default = default!; + public static void Initialize(UserDataManager userDataManager) => Default = new BUTRLauncherManagerHandler(userDataManager); - private string _executable = Constants.BannerlordExecutable; - private string _executableParameters = string.Empty; + public new Dictionary ExtendedModuleInfoCache => base.ExtendedModuleInfoCache; - private Func? _getState; - private Func>? _getAllModuleViewModels; - private Func>? _getModuleViewModels; - private Action>? _setModuleViewModels; - private readonly UserDataManager _userDataManager; - private readonly string _installPath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory!, "../", "../")); + private string _executable = Constants.BannerlordExecutable; + private string _executableParameters = string.Empty; - private BUTRLauncherManagerHandler(UserDataManager userDataManager) - { - _userDataManager = userDataManager; + private Func? _getState; + private Func>? _getAllModuleViewModels; + private Func>? _getModuleViewModels; + private Action>? _setModuleViewModels; - _harmony.Patch( - AccessTools2.DeclaredPropertyGetter(typeof(LauncherUI), "AdditionalArgs"), - postfix: new HarmonyMethod(AccessTools2.DeclaredMethod(typeof(BUTRLauncherManagerHandler), nameof(AdditionalArgsPostfix)), priority: 10000)); + private readonly UserDataManager _userDataManager; - var ids = new ConcurrentDictionary(); - RegisterCallbacks( + private BUTRLauncherManagerHandler(UserDataManager userDataManager) + { + _userDataManager = userDataManager; + + _harmony.Patch( + AccessTools2.DeclaredPropertyGetter(typeof(LauncherUI), "AdditionalArgs"), + postfix: new HarmonyMethod(AccessTools2.DeclaredMethod(typeof(BUTRLauncherManagerHandler), nameof(AdditionalArgsPostfix)), priority: 10000)); + + Initialize( + dialogProvider: DialogProviderImpl.Instance, + notificationProvider: NotificationProviderImpl.Instance, + fileSystemProvider: FileSystemProviderImpl.Instance, + gameInfoProvider: GameInfoProviderImpl.Instance, + loadOrderPersistenceProvider: new CallbackLoadOrderPersistenceProvider( loadLoadOrder: LoadTWLoadOrder, - saveLoadOrder: loadOrder => - { - if (_getState is null) return; - - var state = _getState(); - var userGameTypeData = state.IsSingleplayer ? _userDataManager.UserData.SingleplayerData : _userDataManager.UserData.MultiplayerData; - userGameTypeData.ModDatas.Clear(); - foreach (var (id, entry) in loadOrder) - { - userGameTypeData.ModDatas.Add(new UserModData - { - Id = id, - IsSelected = entry.IsSelected, - }); - } - _userDataManager.UserData.GameType = state.IsSingleplayer ? GameType.Singleplayer : GameType.Multiplayer; - _userDataManager.SaveUserData(); - }, - sendNotification: (id, type, message, ms) => - { - if (string.IsNullOrEmpty(id)) id = Guid.NewGuid().ToString(); - - // Prevents message spam - if (ids.TryAdd(id, null)) return; - using var cts = new CancellationTokenSource(); - _ = Task.Delay(TimeSpan.FromMilliseconds(ms), cts.Token).ContinueWith(x => ids.TryRemove(id, out _)); - - var translatedMessage = new BUTRTextObject(message).ToString(); - switch (type) - { - case NotificationType.Hint: - { - HintManager.ShowHint(translatedMessage); - cts.Cancel(); - break; - } - case NotificationType.Info: - { - // TODO: - HintManager.ShowHint(translatedMessage); - cts.Cancel(); - break; - } - default: - MessageBoxDialog.Show(translatedMessage, "Information", MessageBoxButtons.Ok); - cts.Cancel(); - break; - } - }, - sendDialog: (type, title, message, filters, onResult) => - { - switch (type) - { - case DialogType.Warning: - { - var split = message.Split(new[] { "--CONTENT-SPLIT--" }, StringSplitOptions.RemoveEmptyEntries); - var result = MessageBoxDialog.Show( - string.Join("\n", split), - new BUTRTextObject(title).ToString(), - MessageBoxButtons.OkCancel, - MessageBoxIcon.Warning, - 0, - 0 - ); - onResult(result == MessageBoxResult.Ok ? "true" : "false"); - return; - } - case DialogType.FileOpen: - { - var filter = string.Join("|", filters.Select(x => $"{x.Name} ({string.Join(", ", x.Extensions)}|{string.Join(", ", x.Extensions)}")); - var dialog = new OpenFileDialog - { - Title = title, - Filter = filter, - - CheckFileExists = true, - CheckPathExists = true, - ReadOnlyChecked = true, - Multiselect = false, - ValidateNames = true, - }; - onResult(dialog.ShowDialog() ? dialog.FileName ?? string.Empty : string.Empty); - return; - } - case DialogType.FileSave: - { - var fileName = message; - var filter = string.Join("|", filters.Select(x => $"{x.Name} ({string.Join(", ", x.Extensions)}|{string.Join(", ", x.Extensions)}")); - var dialog = new SaveFileDialog - { - Title = title, - Filter = filter, - FileName = fileName, - - CheckPathExists = false, - - ValidateNames = true, - }; - onResult(dialog.ShowDialog() ? dialog.FileName : string.Empty); - return; - } - } - }, - setGameParameters: (executable, parameters) => - { - _executable = executable; - _executableParameters = string.Join(" ", parameters); - }, - getInstallPath: () => _installPath, - readFileContent: (path, offset, length) => - { - if (!File.Exists(path)) return null; - - if (offset == 0 && length == -1) - { - return File.ReadAllBytes(path); - } - else if (offset >= 0 && length > 0) - { - using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); - var data = new byte[length]; - fs.Seek(offset, SeekOrigin.Begin); - fs.Read(data, 0, length); - return data; - } - else - { - return null; - } - }, - writeFileContent: File.WriteAllBytes, - readDirectoryFileList: Directory.GetFiles, - readDirectoryList: Directory.GetDirectories, - getAllModuleViewModels: () => _getAllModuleViewModels?.Invoke()?.ToArray() ?? Array.Empty(), - getModuleViewModels: () => _getModuleViewModels?.Invoke()?.ToArray() ?? Array.Empty(), - setModuleViewModels: (orderedViewModels) => _setModuleViewModels?.Invoke(orderedViewModels), + saveLoadOrder: SaveTWLoadOrder + ), + launcherStateProvider: new CallbackLauncherStateProvider( + setGameParameters: SetGameParametersCallback, getOptions: GetTWOptions, - getState: () => _getState?.Invoke() ?? LauncherState.Empty - ); - SetGameStore(LauncherPlatform.PlatformType switch - { - LauncherPlatformType.Steam => GameStore.Steam, - LauncherPlatformType.Epic => GameStore.Epic, - LauncherPlatformType.Gog => GameStore.GOG, - LauncherPlatformType.Gdk => GameStore.Xbox, - _ => GameStore.Unknown - }); - } - private static void AdditionalArgsPostfix(ref string __result) + getState: GetStateCallback + ), + loadOrderStateProvider: new CallbackLoadOrderStateProvider( + getAllModuleViewModels: GetAllModuleViewModelsCallback, + getModuleViewModels: GetModuleViewModelsCallback, + setModuleViewModels: SetModuleViewModelsCallback + )); + + SetGameStore(LauncherPlatform.PlatformType switch { - __result = Default._executableParameters; - } + LauncherPlatformType.Steam => GameStore.Steam, + LauncherPlatformType.Epic => GameStore.Epic, + LauncherPlatformType.Gog => GameStore.GOG, + LauncherPlatformType.Gdk => GameStore.Xbox, + _ => GameStore.Unknown + }); + } + private static void AdditionalArgsPostfix(ref string __result) + { + __result = Default._executableParameters; + } - public void RegisterStateProvider(Func getState) - { - _getState = getState; - } + public void RegisterStateProvider(Func getState) + { + _getState = getState; + } - public void RegisterModuleViewModelProvider(Func> getAllModuleViewModels, Func> getModuleViewModels, Action> setModuleViewModels) - { - _getAllModuleViewModels = getAllModuleViewModels; - _getModuleViewModels = getModuleViewModels; - _setModuleViewModels = setModuleViewModels; - } + public void RegisterModuleViewModelProvider(Func> getAllModuleViewModels, Func> getModuleViewModels, Action> setModuleViewModels) + { + _getAllModuleViewModels = getAllModuleViewModels; + _getModuleViewModels = getModuleViewModels; + _setModuleViewModels = setModuleViewModels; + } - public LoadOrder LoadTWLoadOrder() - { - var state = _getState?.Invoke() ?? LauncherState.Empty; + private LoadOrder LoadTWLoadOrder() + { + var state = _getState?.Invoke() ?? LauncherState.Empty; - var userGameTypeData = state.IsSingleplayer ? _userDataManager.UserData.SingleplayerData : _userDataManager.UserData.MultiplayerData; - return new LoadOrder(userGameTypeData.ModDatas.Select((x, i) => new LoadOrderEntry(x.Id, string.Empty, x.IsSelected, i))); - } + var userGameTypeData = state.IsSingleplayer ? _userDataManager.UserData.SingleplayerData : _userDataManager.UserData.MultiplayerData; + return new LoadOrder(userGameTypeData.ModDatas.Select((x, i) => new LoadOrderEntry(x.Id, string.Empty, x.IsSelected, false, i))); + } + + private void SaveTWLoadOrder(LoadOrder loadOrder) + { + if (_getState is null) return; - public LauncherOptions GetTWOptions() => new() + var state = _getState(); + var userGameTypeData = state.IsSingleplayer ? _userDataManager.UserData.SingleplayerData : _userDataManager.UserData.MultiplayerData; + userGameTypeData.ModDatas.Clear(); + foreach (var (id, entry) in loadOrder) { - BetaSorting = LauncherSettings.BetaSorting, - FixCommonIssues = LauncherSettings.FixCommonIssues, - UnblockFiles = true, // TODO: Remove. Always unblock - Language = Manager.GetActiveLanguage(), - }; - - public new bool TryOrderByLoadOrderTW(IEnumerable loadOrder, Func isModuleSelected, [NotNullWhen(false)] out IReadOnlyList? issues, - out IReadOnlyList orderedModules, bool overwriteWhenFailure = false) - => base.TryOrderByLoadOrderTW(loadOrder, isModuleSelected, out issues, out orderedModules, overwriteWhenFailure); + userGameTypeData.ModDatas.Add(new UserModData { Id = id, IsSelected = entry.IsSelected, }); + } + + _userDataManager.UserData.GameType = state.IsSingleplayer ? GameType.Singleplayer : GameType.Multiplayer; + _userDataManager.SaveUserData(); } + + public LauncherOptions GetTWOptions() => new() + { + BetaSorting = LauncherSettings.BetaSorting, + FixCommonIssues = LauncherSettings.FixCommonIssues, + UnblockFiles = true, // TODO: Remove. Always unblock + Language = Manager.GetActiveLanguage(), + }; + + public new bool TryOrderByLoadOrderTW(IEnumerable loadOrder, Func isModuleSelected, [NotNullWhen(false)] out IReadOnlyList? issues, + out IReadOnlyList orderedModules, bool overwriteWhenFailure = false) + => base.TryOrderByLoadOrderTW(loadOrder, isModuleSelected, out issues, out orderedModules, overwriteWhenFailure); } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Bannerlord.LauncherEx.csproj b/src/Bannerlord.LauncherEx/Bannerlord.LauncherEx.csproj index 14be25b..8095845 100644 --- a/src/Bannerlord.LauncherEx/Bannerlord.LauncherEx.csproj +++ b/src/Bannerlord.LauncherEx/Bannerlord.LauncherEx.csproj @@ -2,11 +2,13 @@ netstandard2.0 - 11.0 + 12.0 enable x64 - full - 1.24.3 + 1.25.0 + $(BANNERLORD_BUTR_COMPATIBILITY_SCORE_URL) + portable + System.Diagnostics.CodeAnalysis.UnscopedRefAttribute true @@ -18,16 +20,18 @@ true true false - $(PkgBUTR_ILRepack)\tools\net461\ILRepack.exe - - $(DefineConstants);$(GameVersionConstant) - + Bannerlord.LauncherEx Bannerlord.LauncherEx @@ -46,9 +50,8 @@ - - - + + @@ -61,7 +64,8 @@ - + + @@ -79,12 +83,8 @@ - - - - - - + + $(ILRepackExcludeAssemblies);$(ProjectDir)$(OutputPath)System.Drawing.dll; $(ILRepackExcludeAssemblies);$(ProjectDir)$(OutputPath)System.Drawing.Common.dll; @@ -92,4 +92,14 @@ + + + + <_Parameter1>BUTRCompatibilityScoreUrl + <_Parameter2>$(BUTRCompatibilityScoreUrl) + + + + + \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Extensions/CollectionsExtensions.cs b/src/Bannerlord.LauncherEx/Extensions/CollectionsExtensions.cs deleted file mode 100644 index 9e7ff48..0000000 --- a/src/Bannerlord.LauncherEx/Extensions/CollectionsExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; - -// ReSharper disable once CheckNamespace -namespace Bannerlord.ModuleManager; - -internal static class CollectionsExtensions -{ - public static int IndexOf(this IReadOnlyList self, T elementToFind) - { - var i = 0; - foreach (T element in self) - { - if (Equals(element, elementToFind)) - return i; - i++; - } - return -1; - } -} \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Extensions/LauncherModsVMExtensions.cs b/src/Bannerlord.LauncherEx/Extensions/LauncherModsVMExtensions.cs deleted file mode 100644 index e2af3b8..0000000 --- a/src/Bannerlord.LauncherEx/Extensions/LauncherModsVMExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Bannerlord.LauncherEx.Mixins; -using Bannerlord.LauncherEx.ViewModels; - -using TaleWorlds.Library; -using TaleWorlds.MountAndBlade.Launcher.Library; - -namespace Bannerlord.LauncherEx.Extensions -{ - internal static class LauncherModsVMExtensions - { - public static MBBindingList? GetModules(this LauncherModsVM viewModel) => - viewModel.GetPropertyValue(nameof(LauncherModsVMMixin.Modules2)) as MBBindingList; - } -} \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Extensions/OpenGLTextureExtensions.cs b/src/Bannerlord.LauncherEx/Extensions/OpenGLTextureExtensions.cs index 285d1c0..f595711 100644 --- a/src/Bannerlord.LauncherEx/Extensions/OpenGLTextureExtensions.cs +++ b/src/Bannerlord.LauncherEx/Extensions/OpenGLTextureExtensions.cs @@ -12,81 +12,80 @@ using TaleWorlds.TwoDimension.Standalone; -namespace Bannerlord.LauncherEx.Extensions +namespace Bannerlord.LauncherEx.Extensions; + +internal static class OpenGLTextureExtensions { - internal static class OpenGLTextureExtensions + private enum PixelFormat : uint { - private enum PixelFormat : uint - { - ColorIndex = 6400U, - StencilIndex, - DepthComponent, - Red, - Green, - Blue, - Alpha, - RGB, - RGBA, - Luminance, - LuminanceAlpha, - BGR = 32992U, - BGRA - } + ColorIndex = 6400U, + StencilIndex, + DepthComponent, + Red, + Green, + Blue, + Alpha, + RGB, + RGBA, + Luminance, + LuminanceAlpha, + BGR = 32992U, + BGRA + } - private delegate void MakeActiveDelegate(OpenGLTexture texture); - private static readonly MakeActiveDelegate? _makeActiveDelegate = AccessTools2.GetDelegate(typeof(OpenGLTexture), "MakeActive"); + private delegate void MakeActiveDelegate(OpenGLTexture texture); + private static readonly MakeActiveDelegate? _makeActiveDelegate = AccessTools2.GetDelegate(typeof(OpenGLTexture), "MakeActive"); - [DllImport("Opengl32.dll", EntryPoint = "glTexImage2D")] - private static extern void TexImage2D(uint target, int level, uint pixelInternalformat, int width, int height, int border, PixelFormat format, uint type, byte[] pixels); + [DllImport("Opengl32.dll", EntryPoint = "glTexImage2D")] + private static extern void TexImage2D(uint target, int level, uint pixelInternalformat, int width, int height, int border, PixelFormat format, uint type, byte[] pixels); - [DllImport("Opengl32.dll", EntryPoint = "glTexImage2D")] - private static extern void TexImage2D2(uint target, int level, uint pixelInternalformat, int width, int height, int border, PixelFormat format, uint type, IntPtr pixels); + [DllImport("Opengl32.dll", EntryPoint = "glTexImage2D")] + private static extern void TexImage2D2(uint target, int level, uint pixelInternalformat, int width, int height, int border, PixelFormat format, uint type, IntPtr pixels); - public static void MakeActive(this OpenGLTexture texture) => _makeActiveDelegate?.Invoke(texture); + public static void MakeActive(this OpenGLTexture texture) => _makeActiveDelegate?.Invoke(texture); - public static bool LoadFromStream(this OpenGLTexture texture, string name, Stream stream) + public static bool LoadFromStream(this OpenGLTexture texture, string name, Stream stream) + { + if (_makeActiveDelegate is null) + return false; + + // TODO: For some reason, we need now to pass req_comp as 4, not 0 as previously + var image = new ImageReader().Read(stream, 4); + texture.Initialize(name, image.Width, image.Height); + texture.MakeActive(); + var (error, pixelFormat, pixelInternalformat) = image.Comp switch + { + 1 => (false, PixelFormat.Red, 0x8229U), + 3 => (false, PixelFormat.RGB, 0x8051U), + 4 => (false, PixelFormat.RGBA, 0x8058U), + _ => (true, (PixelFormat) 0, 0U), + }; + if (!error) { - if (_makeActiveDelegate is null) - return false; - - // TODO: For some reason, we need now to pass req_comp as 4, not 0 as previously - var image = new ImageReader().Read(stream, 4); - texture.Initialize(name, image.Width, image.Height); - texture.MakeActive(); - var (error, pixelFormat, pixelInternalformat) = image.Comp switch - { - 1 => (false, PixelFormat.Red, 0x8229U), - 3 => (false, PixelFormat.RGB, 0x8051U), - 4 => (false, PixelFormat.RGBA, 0x8058U), - _ => (true, (PixelFormat) 0, 0U), - }; - if (!error) - { - TexImage2D(0x00000DE1, 0, pixelInternalformat, image.Width, image.Height, 0, pixelFormat, 0x00001401, image.Data); - } - - return true; + TexImage2D(0x00000DE1, 0, pixelInternalformat, image.Width, image.Height, 0, pixelFormat, 0x00001401, image.Data); } - public static bool LoadFromAssetTexture(this OpenGLTexture texture, string name, Texture source) - { - if (_makeActiveDelegate is null) - return false; + return true; + } - if (source.TexturePixels is null) - return false; + public static bool LoadFromAssetTexture(this OpenGLTexture texture, string name, Texture source) + { + if (_makeActiveDelegate is null) + return false; - var textureData = source.TexturePixels.GetData(); - using var bitmap = TextureUtil.DecodeTextureDataToBitmap(textureData.PrimaryRawImage, (int) source.Width, (int) source.Height, source.Format); - var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + if (source.TexturePixels is null) + return false; - texture.Initialize(name, (int) source.Width, (int) source.Height); - texture.MakeActive(); - TexImage2D2(0x00000DE1, 0, 0x8058U, data.Width, data.Height, 0, PixelFormat.BGRA, 0x00001401, data.Scan0); + var textureData = source.TexturePixels.GetData(); + using var bitmap = TextureUtil.DecodeTextureDataToBitmap(textureData.PrimaryRawImage, (int) source.Width, (int) source.Height, source.Format); + var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - bitmap.UnlockBits(data); + texture.Initialize(name, (int) source.Width, (int) source.Height); + texture.MakeActive(); + TexImage2D2(0x00000DE1, 0, 0x8058U, data.Width, data.Height, 0, PixelFormat.BGRA, 0x00001401, data.Scan0); - return true; - } + bitmap.UnlockBits(data); + + return true; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Extensions/PlatformFileHelperPCExtended.cs b/src/Bannerlord.LauncherEx/Extensions/PlatformFileHelperPCExtended.cs index 42806cd..e2fb7d9 100644 --- a/src/Bannerlord.LauncherEx/Extensions/PlatformFileHelperPCExtended.cs +++ b/src/Bannerlord.LauncherEx/Extensions/PlatformFileHelperPCExtended.cs @@ -2,22 +2,21 @@ using TaleWorlds.Library; -namespace Bannerlord.LauncherEx.Extensions +namespace Bannerlord.LauncherEx.Extensions; + +// TODO: What to with Xbox version? +internal static class PlatformFileHelperPCExtended { - // TODO: What to with Xbox version? - internal static class PlatformFileHelperPCExtended - { - private delegate string GetDirectoryFullPathDelegate(object instance, PlatformDirectoryPath directoryPath); - private static GetDirectoryFullPathDelegate? GetDirectoryFullPathMethod = - AccessTools2.GetDelegate("TaleWorlds.Library.PlatformFileHelperPC:GetDirectoryFullPath"); + private delegate string GetDirectoryFullPathDelegate(object instance, PlatformDirectoryPath directoryPath); + private static GetDirectoryFullPathDelegate? GetDirectoryFullPathMethod = + AccessTools2.GetDelegate("TaleWorlds.Library.PlatformFileHelperPC:GetDirectoryFullPath"); - private delegate object GetPlatformFileHelperDelegate(); - private static GetPlatformFileHelperDelegate? GetPlatformFileHelper = - AccessTools2.GetPropertyGetterDelegate("TaleWorlds.Library.Common:PlatformFileHelper"); + private delegate object GetPlatformFileHelperDelegate(); + private static GetPlatformFileHelperDelegate? GetPlatformFileHelper = + AccessTools2.GetPropertyGetterDelegate("TaleWorlds.Library.Common:PlatformFileHelper"); - public static string? GetDirectoryFullPath(PlatformDirectoryPath directoryPath) => - GetPlatformFileHelper is not null && GetDirectoryFullPathMethod is not null && GetPlatformFileHelper() is { } obj - ? GetDirectoryFullPathMethod(obj, directoryPath) - : null; - } + public static string? GetDirectoryFullPath(PlatformDirectoryPath directoryPath) => + GetPlatformFileHelper is not null && GetDirectoryFullPathMethod is not null && GetPlatformFileHelper() is { } obj + ? GetDirectoryFullPathMethod(obj, directoryPath) + : null; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Extensions/ProcessExtensions.cs b/src/Bannerlord.LauncherEx/Extensions/ProcessExtensions.cs index e96c81a..ceb18e8 100644 --- a/src/Bannerlord.LauncherEx/Extensions/ProcessExtensions.cs +++ b/src/Bannerlord.LauncherEx/Extensions/ProcessExtensions.cs @@ -9,50 +9,49 @@ using Windows.Win32.Foundation; using Windows.Win32.System.Diagnostics.ToolHelp; -namespace Bannerlord.LauncherEx.Extensions +namespace Bannerlord.LauncherEx.Extensions; + +internal static class ProcessExtensions { - internal static class ProcessExtensions - { - public static int ParentProcessId(this Process process) => ParentProcessId(process.Id); - public static Process? ParentProcess(this Process process) => ParentProcessId(process.Id) is var pId && pId is not -1 ? Process.GetProcessById(pId) : null; + public static int ParentProcessId(this Process process) => ParentProcessId(process.Id); + public static Process? ParentProcess(this Process process) => ParentProcessId(process.Id) is var pId && pId is not -1 ? Process.GetProcessById(pId) : null; - private static int ParentProcessId(int id) + private static int ParentProcessId(int id) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + var pe32 = new PROCESSENTRY32 { - var pe32 = new PROCESSENTRY32 - { - dwSize = (uint) Marshal.SizeOf(typeof(PROCESSENTRY32)) - }; - using var hSnapshot = new SafeSnapshotHandle(PInvoke.CreateToolhelp32Snapshot(CREATE_TOOLHELP_SNAPSHOT_FLAGS.TH32CS_SNAPPROCESS, (uint) id)); - if (hSnapshot.IsInvalid) return -1; - - if (!PInvoke.Process32First(hSnapshot, ref pe32)) - return -1; - - do - { - if (pe32.th32ProcessID == (uint) id) - return (int) pe32.th32ParentProcessID; - } while (PInvoke.Process32Next(hSnapshot, ref pe32)); - } - - return -1; - } + dwSize = (uint) Marshal.SizeOf(typeof(PROCESSENTRY32)) + }; + using var hSnapshot = new SafeSnapshotHandle(PInvoke.CreateToolhelp32Snapshot(CREATE_TOOLHELP_SNAPSHOT_FLAGS.TH32CS_SNAPPROCESS, (uint) id)); + if (hSnapshot.IsInvalid) return -1; - [SuppressUnmanagedCodeSecurity] - private sealed class SafeSnapshotHandle : SafeHandleMinusOneIsInvalid - { - private readonly HANDLE _handle; + if (!PInvoke.Process32First(hSnapshot, ref pe32)) + return -1; - [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] - internal SafeSnapshotHandle(HANDLE handle) : base(true) + do { - _handle = handle; - SetHandle(_handle); - } + if (pe32.th32ProcessID == (uint) id) + return (int) pe32.th32ParentProcessID; + } while (PInvoke.Process32Next(hSnapshot, ref pe32)); + } - protected override bool ReleaseHandle() => PInvoke.CloseHandle(_handle); + return -1; + } + + [SuppressUnmanagedCodeSecurity] + private sealed class SafeSnapshotHandle : SafeHandleMinusOneIsInvalid + { + private readonly HANDLE _handle; + + [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] + internal SafeSnapshotHandle(HANDLE handle) : base(true) + { + _handle = handle; + SetHandle(_handle); } + + protected override bool ReleaseHandle() => PInvoke.CloseHandle(_handle); } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Extensions/ViewModelExtensions.cs b/src/Bannerlord.LauncherEx/Extensions/ViewModelExtensions.cs index 445dd87..8d02c4f 100644 --- a/src/Bannerlord.LauncherEx/Extensions/ViewModelExtensions.cs +++ b/src/Bannerlord.LauncherEx/Extensions/ViewModelExtensions.cs @@ -10,75 +10,74 @@ using TaleWorlds.Library; -namespace Bannerlord.LauncherEx.Extensions -{ - internal static class ViewModelExtensions - { - private static readonly AccessTools.FieldRef? PropertiesAndMethods = - AccessTools2.FieldRefAccess("TaleWorlds.Library.ViewModel:_propertiesAndMethods"); - - private delegate Dictionary GetPropertiesDelegate(object instance); - private static readonly GetPropertiesDelegate? GetProperties = - AccessTools2.GetDeclaredPropertyGetterDelegate("TaleWorlds.Library.ViewModel+DataSourceTypeBindingPropertiesCollection:Properties"); +namespace Bannerlord.LauncherEx.Extensions; - private delegate Dictionary GetMethodsDelegate(object instance); - private static readonly GetMethodsDelegate? GetMethods = - AccessTools2.GetDeclaredPropertyGetterDelegate("TaleWorlds.Library.ViewModel+DataSourceTypeBindingPropertiesCollection:Methods"); +internal static class ViewModelExtensions +{ + private static readonly AccessTools.FieldRef? PropertiesAndMethods = + AccessTools2.FieldRefAccess("TaleWorlds.Library.ViewModel:_propertiesAndMethods"); - private static readonly AccessTools.FieldRef? CachedViewModelProperties = - AccessTools2.StaticFieldRefAccess("TaleWorlds.Library.ViewModel:_cachedViewModelProperties"); + private delegate Dictionary GetPropertiesDelegate(object instance); + private static readonly GetPropertiesDelegate? GetProperties = + AccessTools2.GetDeclaredPropertyGetterDelegate("TaleWorlds.Library.ViewModel+DataSourceTypeBindingPropertiesCollection:Properties"); - public delegate object DataSourceTypeBindingPropertiesCollectionCtorDelegate(Dictionary properties, Dictionary methods); - public static readonly DataSourceTypeBindingPropertiesCollectionCtorDelegate? DataSourceTypeBindingPropertiesCollectionCtor = - AccessTools2.GetDeclaredConstructorDelegate( - "TaleWorlds.Library.ViewModel+DataSourceTypeBindingPropertiesCollection", new[] { typeof(Dictionary), typeof(Dictionary) }); + private delegate Dictionary GetMethodsDelegate(object instance); + private static readonly GetMethodsDelegate? GetMethods = + AccessTools2.GetDeclaredPropertyGetterDelegate("TaleWorlds.Library.ViewModel+DataSourceTypeBindingPropertiesCollection:Methods"); - public static void AddProperty(this ViewModel viewModel, string name, PropertyInfo propertyInfo) - { - if (!GetOrCreateIndividualStorage(viewModel, out var propDict, out _)) - return; + private static readonly AccessTools.FieldRef? CachedViewModelProperties = + AccessTools2.StaticFieldRefAccess("TaleWorlds.Library.ViewModel:_cachedViewModelProperties"); - propDict[name] = propertyInfo; - } + public delegate object DataSourceTypeBindingPropertiesCollectionCtorDelegate(Dictionary properties, Dictionary methods); + public static readonly DataSourceTypeBindingPropertiesCollectionCtorDelegate? DataSourceTypeBindingPropertiesCollectionCtor = + AccessTools2.GetDeclaredConstructorDelegate( + "TaleWorlds.Library.ViewModel+DataSourceTypeBindingPropertiesCollection", new[] { typeof(Dictionary), typeof(Dictionary) }); - public static void AddMethod(this ViewModel viewModel, string name, MethodInfo methodInfo) - { - if (!GetOrCreateIndividualStorage(viewModel, out _, out var methodDict)) - return; + public static void AddProperty(this ViewModel viewModel, string name, PropertyInfo propertyInfo) + { + if (!GetOrCreateIndividualStorage(viewModel, out var propDict, out _)) + return; - methodDict[name] = methodInfo; - } + propDict[name] = propertyInfo; + } - private static bool GetOrCreateIndividualStorage(ViewModel viewModel, [NotNullWhen(true)] out Dictionary? propDict, [NotNullWhen(true)] out Dictionary? methodDict) - { - propDict = null; - methodDict = null; + public static void AddMethod(this ViewModel viewModel, string name, MethodInfo methodInfo) + { + if (!GetOrCreateIndividualStorage(viewModel, out _, out var methodDict)) + return; - if (PropertiesAndMethods is null || CachedViewModelProperties is null || DataSourceTypeBindingPropertiesCollectionCtor is null || GetProperties is null || GetMethods is null) - return false; + methodDict[name] = methodInfo; + } - if (PropertiesAndMethods(viewModel) is not { } storage || CachedViewModelProperties() is not { } staticStorageDict) - return false; + private static bool GetOrCreateIndividualStorage(ViewModel viewModel, [NotNullWhen(true)] out Dictionary? propDict, [NotNullWhen(true)] out Dictionary? methodDict) + { + propDict = null; + methodDict = null; - if ((propDict = GetProperties(storage)) is null || (methodDict = GetMethods(storage)) is null) - return false; + if (PropertiesAndMethods is null || CachedViewModelProperties is null || DataSourceTypeBindingPropertiesCollectionCtor is null || GetProperties is null || GetMethods is null) + return false; - var type = viewModel.GetType(); - if (!staticStorageDict.Contains(type)) // There is not static storage to copy from, fast exit - return true; + if (PropertiesAndMethods(viewModel) is not { } storage || CachedViewModelProperties() is not { } staticStorageDict) + return false; - if (staticStorageDict[type] is not { } staticStorage) - return false; + if ((propDict = GetProperties(storage)) is null || (methodDict = GetMethods(storage)) is null) + return false; - // TW caches the properties, since we modify each VM individually, we need to copy them - if (ReferenceEquals(storage, staticStorage)) - PropertiesAndMethods(viewModel) = DataSourceTypeBindingPropertiesCollectionCtor(propDict = new(propDict), methodDict = new(methodDict)); + var type = viewModel.GetType(); + if (!staticStorageDict.Contains(type)) // There is not static storage to copy from, fast exit return true; - } - public static TViewModelMixin? GetMixin(this TViewModel viewModel) - where TViewModelMixin : ViewModelMixin - where TViewModel : ViewModel - => viewModel.GetPropertyValue(typeof(TViewModelMixin).Name) as TViewModelMixin; + if (staticStorageDict[type] is not { } staticStorage) + return false; + + // TW caches the properties, since we modify each VM individually, we need to copy them + if (ReferenceEquals(storage, staticStorage)) + PropertiesAndMethods(viewModel) = DataSourceTypeBindingPropertiesCollectionCtor(propDict = new(propDict), methodDict = new(methodDict)); + return true; } + + public static TViewModelMixin? GetMixin(this TViewModel viewModel) + where TViewModelMixin : ViewModelMixin + where TViewModel : ViewModel + => viewModel.GetPropertyValue(typeof(TViewModelMixin).Name) as TViewModelMixin; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Extensions/WidgetExtensions.cs b/src/Bannerlord.LauncherEx/Extensions/WidgetExtensions.cs index 0baf97c..15a396e 100644 --- a/src/Bannerlord.LauncherEx/Extensions/WidgetExtensions.cs +++ b/src/Bannerlord.LauncherEx/Extensions/WidgetExtensions.cs @@ -11,95 +11,94 @@ using TaleWorlds.GauntletUI.BaseTypes; using TaleWorlds.Library; -namespace Bannerlord.LauncherEx.Extensions +namespace Bannerlord.LauncherEx.Extensions; + +internal static class WidgetExtensions { - internal static class WidgetExtensions - { - private delegate void OnPropertyChangedDelegate1(PropertyOwnerObject instance, T value, [CallerMemberName] string? propertyName = null); - private static readonly ConcurrentDictionary OnPropertyChanged1 = - new(); + private delegate void OnPropertyChangedDelegate1(PropertyOwnerObject instance, T value, [CallerMemberName] string? propertyName = null); + private static readonly ConcurrentDictionary OnPropertyChanged1 = + new(); - private delegate void OnPropertyChangedDelegate2(PropertyOwnerObject instance, bool value, [CallerMemberName] string? propertyName = null); - private static readonly OnPropertyChangedDelegate2? OnPropertyChanged2 = - AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(bool), typeof(string) }); + private delegate void OnPropertyChangedDelegate2(PropertyOwnerObject instance, bool value, [CallerMemberName] string? propertyName = null); + private static readonly OnPropertyChangedDelegate2? OnPropertyChanged2 = + AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(bool), typeof(string) }); - private delegate void OnPropertyChangedDelegate3(PropertyOwnerObject instance, int value, [CallerMemberName] string? propertyName = null); - private static readonly OnPropertyChangedDelegate3? OnPropertyChanged3 = - AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(int), typeof(string) }); + private delegate void OnPropertyChangedDelegate3(PropertyOwnerObject instance, int value, [CallerMemberName] string? propertyName = null); + private static readonly OnPropertyChangedDelegate3? OnPropertyChanged3 = + AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(int), typeof(string) }); - private delegate void OnPropertyChangedDelegate4(PropertyOwnerObject instance, float value, [CallerMemberName] string? propertyName = null); - private static readonly OnPropertyChangedDelegate4? OnPropertyChanged4 = - AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(float), typeof(string) }); + private delegate void OnPropertyChangedDelegate4(PropertyOwnerObject instance, float value, [CallerMemberName] string? propertyName = null); + private static readonly OnPropertyChangedDelegate4? OnPropertyChanged4 = + AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(float), typeof(string) }); - private delegate void OnPropertyChangedDelegate5(PropertyOwnerObject instance, uint value, [CallerMemberName] string? propertyName = null); - private static readonly OnPropertyChangedDelegate5? OnPropertyChanged5 = - AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(uint), typeof(string) }); + private delegate void OnPropertyChangedDelegate5(PropertyOwnerObject instance, uint value, [CallerMemberName] string? propertyName = null); + private static readonly OnPropertyChangedDelegate5? OnPropertyChanged5 = + AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(uint), typeof(string) }); - private delegate void OnPropertyChangedDelegate6(PropertyOwnerObject instance, Color value, [CallerMemberName] string? propertyName = null); - private static readonly OnPropertyChangedDelegate6? OnPropertyChanged6 = - AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(Color), typeof(string) }); + private delegate void OnPropertyChangedDelegate6(PropertyOwnerObject instance, Color value, [CallerMemberName] string? propertyName = null); + private static readonly OnPropertyChangedDelegate6? OnPropertyChanged6 = + AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(Color), typeof(string) }); - private delegate void OnPropertyChangedDelegate7(PropertyOwnerObject instance, double value, [CallerMemberName] string? propertyName = null); - private static readonly OnPropertyChangedDelegate7? OnPropertyChanged7 = - AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(double), typeof(string) }); + private delegate void OnPropertyChangedDelegate7(PropertyOwnerObject instance, double value, [CallerMemberName] string? propertyName = null); + private static readonly OnPropertyChangedDelegate7? OnPropertyChanged7 = + AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(double), typeof(string) }); - private delegate void OnPropertyChangedDelegate8(PropertyOwnerObject instance, Vec2 value, [CallerMemberName] string? propertyName = null); - private static readonly OnPropertyChangedDelegate8? OnPropertyChanged8 = - AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(Vec2), typeof(string) }); + private delegate void OnPropertyChangedDelegate8(PropertyOwnerObject instance, Vec2 value, [CallerMemberName] string? propertyName = null); + private static readonly OnPropertyChangedDelegate8? OnPropertyChanged8 = + AccessTools2.GetDelegate(typeof(PropertyOwnerObject), "OnPropertyChanged", new[] { typeof(Vec2), typeof(string) }); - private delegate void OnPropertyChangedDelegate(PropertyOwnerObject instance, T value, [CallerMemberName] string? propertyName = null); - private static readonly OnPropertyChangedDelegate? OnPropertyChangedMethod = - AccessTools2.GetDelegate>(typeof(PropertyOwnerObject), "OnPropertyChanged"); + private delegate void OnPropertyChangedDelegate(PropertyOwnerObject instance, T value, [CallerMemberName] string? propertyName = null); + private static readonly OnPropertyChangedDelegate? OnPropertyChangedMethod = + AccessTools2.GetDelegate>(typeof(PropertyOwnerObject), "OnPropertyChanged"); - private delegate void SetIsPressedDelegate(Widget instance, bool value); - private static readonly SetIsPressedDelegate? SetIsPressedMethod = - AccessTools2.GetPropertySetterDelegate(typeof(Widget), "IsPressed"); + private delegate void SetIsPressedDelegate(Widget instance, bool value); + private static readonly SetIsPressedDelegate? SetIsPressedMethod = + AccessTools2.GetPropertySetterDelegate(typeof(Widget), "IsPressed"); - public static bool IsPointInsideMeasuredArea(this Widget widget) - { - var method = AccessTools2.Method(typeof(Widget), "IsPointInsideMeasuredArea"); - var property = AccessTools2.Property(typeof(EventManager), "MousePosition"); + public static bool IsPointInsideMeasuredArea(this Widget widget) + { + var method = AccessTools2.Method(typeof(Widget), "IsPointInsideMeasuredArea"); + var property = AccessTools2.Property(typeof(EventManager), "MousePosition"); + + if (method is null || property is null) + return false; + + if (method.Invoke(widget, new[] { property.GetValue(widget.EventManager) }) is not bool result) + return false; - if (method is null || property is null) - return false; + return result; + } - if (method.Invoke(widget, new[] { property.GetValue(widget.EventManager) }) is not bool result) - return false; + public static void SetIsPressed(this Widget widget, bool value) => SetIsPressedMethod?.Invoke(widget, value); - return result; + public static bool SetField(this Widget widget, ref T field, T value, [CallerMemberName] string? propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) + { + return false; } + field = value; - public static void SetIsPressed(this Widget widget, bool value) => SetIsPressedMethod?.Invoke(widget, value); + switch (value) + { + case bool val when OnPropertyChanged2 is not null: OnPropertyChanged2(widget, val, propertyName); return true; + case int val when OnPropertyChanged3 is not null: OnPropertyChanged3(widget, val, propertyName); return true; + case float val when OnPropertyChanged4 is not null: OnPropertyChanged4(widget, val, propertyName); return true; + case uint val when OnPropertyChanged5 is not null: OnPropertyChanged5(widget, val, propertyName); return true; + case Color val when OnPropertyChanged6 is not null: OnPropertyChanged6(widget, val, propertyName); return true; + case double val when OnPropertyChanged7 is not null: OnPropertyChanged7(widget, val, propertyName); return true; + case Vec2 val when OnPropertyChanged8 is not null: OnPropertyChanged8(widget, val, propertyName); return true; + } - public static bool SetField(this Widget widget, ref T field, T value, [CallerMemberName] string? propertyName = null) + static Delegate ValueFactory(Type _) { - if (EqualityComparer.Default.Equals(field, value)) - { - return false; - } - field = value; - - switch (value) - { - case bool val when OnPropertyChanged2 is not null: OnPropertyChanged2(widget, val, propertyName); return true; - case int val when OnPropertyChanged3 is not null: OnPropertyChanged3(widget, val, propertyName); return true; - case float val when OnPropertyChanged4 is not null: OnPropertyChanged4(widget, val, propertyName); return true; - case uint val when OnPropertyChanged5 is not null: OnPropertyChanged5(widget, val, propertyName); return true; - case Color val when OnPropertyChanged6 is not null: OnPropertyChanged6(widget, val, propertyName); return true; - case double val when OnPropertyChanged7 is not null: OnPropertyChanged7(widget, val, propertyName); return true; - case Vec2 val when OnPropertyChanged8 is not null: OnPropertyChanged8(widget, val, propertyName); return true; - } - - static Delegate ValueFactory(Type _) - { - var method = AccessTools.GetDeclaredMethods(typeof(PropertyOwnerObject)).FirstOrDefault(x => x.IsGenericMethod && x.Name == "OnPropertyChanged")?.MakeGenericMethod(typeof(T))!; - return AccessTools2.GetDelegate>(method)!; - } - - if (OnPropertyChanged1.GetOrAdd(typeof(T), ValueFactory) is OnPropertyChangedDelegate1 del) - del(widget, value, propertyName); - return true; + var method = AccessTools.GetDeclaredMethods(typeof(PropertyOwnerObject)).FirstOrDefault(x => x.IsGenericMethod && x.Name == "OnPropertyChanged")?.MakeGenericMethod(typeof(T))!; + return AccessTools2.GetDelegate>(method)!; } + + if (OnPropertyChanged1.GetOrAdd(typeof(T), ValueFactory) is OnPropertyChangedDelegate1 del) + del(widget, value, propertyName); + return true; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/AssemblyWrapper.cs b/src/Bannerlord.LauncherEx/Helpers/AssemblyWrapper.cs index cb52b1c..7d2b080 100644 --- a/src/Bannerlord.LauncherEx/Helpers/AssemblyWrapper.cs +++ b/src/Bannerlord.LauncherEx/Helpers/AssemblyWrapper.cs @@ -1,11 +1,10 @@ using System.Reflection; -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +internal class AssemblyWrapper : Assembly { - internal class AssemblyWrapper : Assembly - { - public override string Location { get; } + public override string Location { get; } - public AssemblyWrapper(string location) => Location = location; - } + public AssemblyWrapper(string location) => Location = location; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/ConfigReader.cs b/src/Bannerlord.LauncherEx/Helpers/ConfigReader.cs index 8ba2cdf..8251958 100644 --- a/src/Bannerlord.LauncherEx/Helpers/ConfigReader.cs +++ b/src/Bannerlord.LauncherEx/Helpers/ConfigReader.cs @@ -4,60 +4,59 @@ using System.Linq; using System.Text; -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +internal static class ConfigReader { - internal static class ConfigReader - { - private static readonly string BOMMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()); - - public static readonly string GameConfigPath = - Path.Combine($@"{Environment.GetFolderPath(Environment.SpecialFolder.Personal)}", "Mount and Blade II Bannerlord", "Configs", "BannerlordConfig.txt"); - public static readonly string EngineConfigPath = - Path.Combine($@"{Environment.GetFolderPath(Environment.SpecialFolder.Personal)}", "Mount and Blade II Bannerlord", "Configs", "engine_config.txt"); + private static readonly string BOMMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()); + + public static readonly string GameConfigPath = + Path.Combine($@"{Environment.GetFolderPath(Environment.SpecialFolder.Personal)}", "Mount and Blade II Bannerlord", "Configs", "BannerlordConfig.txt"); + public static readonly string EngineConfigPath = + Path.Combine($@"{Environment.GetFolderPath(Environment.SpecialFolder.Personal)}", "Mount and Blade II Bannerlord", "Configs", "engine_config.txt"); - public static Dictionary GetGameOptions(Func readFileContent) + public static Dictionary GetGameOptions(Func readFileContent) + { + var dict = new Dictionary(); + if (readFileContent(GameConfigPath) is not { } data) return dict; + try { - var dict = new Dictionary(); - if (readFileContent(GameConfigPath) is not { } data) return dict; - try - { - var content = Encoding.UTF8.GetString(data); - if (content.StartsWith(BOMMarkUtf8, StringComparison.Ordinal)) - content = content.Remove(0, BOMMarkUtf8.Length); + var content = Encoding.UTF8.GetString(data); + if (content.StartsWith(BOMMarkUtf8, StringComparison.Ordinal)) + content = content.Remove(0, BOMMarkUtf8.Length); - foreach (var keyValue in content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries)) - { - var split = keyValue.Split(new[] { "=" }, StringSplitOptions.RemoveEmptyEntries); - if (split.Length != 2) continue; - var key = split[0].Trim(); - var value = split[1].Trim(); - dict[key] = value; - } + foreach (var keyValue in content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries)) + { + var split = keyValue.Split(new[] { "=" }, StringSplitOptions.RemoveEmptyEntries); + if (split.Length != 2) continue; + var key = split[0].Trim(); + var value = split[1].Trim(); + dict[key] = value; } - catch (Exception) { /* ignore */ } - return dict; } - public static Dictionary GetEngineOptions(Func readFileContent) + catch (Exception) { /* ignore */ } + return dict; + } + public static Dictionary GetEngineOptions(Func readFileContent) + { + var dict = new Dictionary(); + if (readFileContent(EngineConfigPath) is not { } data) return dict; + try { - var dict = new Dictionary(); - if (readFileContent(EngineConfigPath) is not { } data) return dict; - try - { - var content = Encoding.UTF8.GetString(data); - if (content.StartsWith(BOMMarkUtf8, StringComparison.Ordinal)) - content = content.Remove(0, BOMMarkUtf8.Length); + var content = Encoding.UTF8.GetString(data); + if (content.StartsWith(BOMMarkUtf8, StringComparison.Ordinal)) + content = content.Remove(0, BOMMarkUtf8.Length); - foreach (var keyValue in content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries)) - { - var split = keyValue.Split(new[] { "=" }, StringSplitOptions.RemoveEmptyEntries); - if (split.Length != 2) continue; - var key = split[0].Trim(); - var value = split[1].Trim(); - dict[key] = value; - } + foreach (var keyValue in content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries)) + { + var split = keyValue.Split(new[] { "=" }, StringSplitOptions.RemoveEmptyEntries); + if (split.Length != 2) continue; + var key = split[0].Trim(); + var value = split[1].Trim(); + dict[key] = value; } - catch (Exception) { /* ignore */ } - return dict; } + catch (Exception) { /* ignore */ } + return dict; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/HintManager.cs b/src/Bannerlord.LauncherEx/Helpers/HintManager.cs index 2951414..dea86f2 100644 --- a/src/Bannerlord.LauncherEx/Helpers/HintManager.cs +++ b/src/Bannerlord.LauncherEx/Helpers/HintManager.cs @@ -2,13 +2,12 @@ using TaleWorlds.MountAndBlade.Launcher.Library; -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +internal static class HintManager { - internal static class HintManager - { - public static void ShowHint(BUTRTextObject message) => ShowHint(message.ToString()); - public static void ShowHint(string message) => LauncherUI.AddHintInformation(message); + public static void ShowHint(BUTRTextObject message) => ShowHint(message.ToString()); + public static void ShowHint(string message) => LauncherUI.AddHintInformation(message); - public static void HideHint() => LauncherUI.HideHintInformation(); - } + public static void HideHint() => LauncherUI.HideHintInformation(); } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/Input/BUTRInputManager.cs b/src/Bannerlord.LauncherEx/Helpers/Input/BUTRInputManager.cs index ec9bfd7..c244cc1 100644 --- a/src/Bannerlord.LauncherEx/Helpers/Input/BUTRInputManager.cs +++ b/src/Bannerlord.LauncherEx/Helpers/Input/BUTRInputManager.cs @@ -6,168 +6,152 @@ using TaleWorlds.InputSystem; using TaleWorlds.Library; -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +// Can't believe I need to add a custom keyboard handler for the launcher +internal class BUTRInputManager : IInputManager, IDisposable { - // Can't believe I need to add a custom keyboard handler for the launcher - internal class BUTRInputManager : IInputManager, IDisposable - { - public readonly IInputManager InputManager; - public readonly int[] ReleasedChars = new int[10]; + public readonly IInputManager InputManager; + public readonly int[] ReleasedChars = new int[10]; - private KeyboardState _currentState; - private KeyboardState _previousState; + private KeyboardState _currentState; + private KeyboardState _previousState; - public BUTRInputManager(IInputManager inputManager) => InputManager = inputManager; + public BUTRInputManager(IInputManager inputManager) => InputManager = inputManager; - public void Update() - { - _currentState.Dispose(); - - _previousState = _currentState; - _currentState = Keyboard.GetState(); - - var previousPressedKeys = _previousState.GetPressedKeys(); - var currentPressedKeys = _currentState.GetPressedKeys(); - - var i = 0; - foreach (var str in previousPressedKeys.Except(currentPressedKeys).Select(x => _currentState.AsString(x))) - { - if (string.IsNullOrEmpty(str)) continue; - - ReleasedChars[i] = str[0]; - i++; - } - for (; i < ReleasedChars.Length; i++) - { - ReleasedChars[i] = default; - } - } + public void Update() + { + _currentState.Dispose(); - public bool IsMouseActive() => InputManager.IsMouseActive(); - public float GetMousePositionX() => InputManager.GetMousePositionX(); - public float GetMousePositionY() => InputManager.GetMousePositionY(); - public float GetMouseScrollValue() => InputManager.GetMouseScrollValue(); - public float GetMouseMoveY() => InputManager.GetMouseMoveY(); - public float GetMouseSensitivity() => InputManager.GetMouseSensitivity(); - public float GetMouseDeltaZ() => InputManager.GetMouseDeltaZ(); + _previousState = _currentState; + _currentState = Keyboard.GetState(); - public void SetClipboardText(string text) + var previousPressedKeys = _previousState.GetPressedKeys(); + var currentPressedKeys = _currentState.GetPressedKeys(); + + var i = 0; + foreach (var str in previousPressedKeys.Except(currentPressedKeys).Select(x => _currentState.AsString(x))) { - var thread = new Thread(() => WindowsClipboard.SetText(text)); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - thread.Join(); - //_inputManagerImplementation.SetClipboardText(text); + if (string.IsNullOrEmpty(str)) continue; + + ReleasedChars[i] = str[0]; + i++; } - public string GetClipboardText() + for (; i < ReleasedChars.Length; i++) { - var text = string.Empty; - var thread = new Thread(() => text = WindowsClipboard.GetText()); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - thread.Join(); - return text; - //return _inputManagerImplementation.GetClipboardText(); + ReleasedChars[i] = default; } + } - public Vec2 GetKeyState(InputKey key) => - IsAction(key, rawKey => new Vec2(_currentState.IsKeyDown(rawKey) ? 1f : 0f, _previousState.IsKeyDown(rawKey) ? 1f : 0f), _key => InputManager.GetKeyState(_key)); - public bool IsKeyPressed(InputKey key) => - IsAction(key, rawKey => _currentState.IsKeyDown(rawKey) && _previousState.IsKeyUp(rawKey), _key => InputManager.IsKeyPressed(_key)); - public bool IsKeyDown(InputKey key) => - IsAction(key, rawKey => _currentState.IsKeyDown(rawKey) || _previousState.IsKeyDown(rawKey), _key => InputManager.IsKeyDown(_key)); - public bool IsKeyReleased(InputKey key) => - IsAction(key, rawKey => _currentState.IsKeyUp(rawKey) && _previousState.IsKeyDown(rawKey), _key => InputManager.IsKeyReleased(_key)); - public bool IsKeyDownImmediate(InputKey key) => - IsAction(key, rawKey => _currentState.IsKeyDown(rawKey), _key => InputManager.IsKeyDownImmediate(_key)); - - public Vec2 GetResolution() => InputManager.GetResolution(); - public Vec2 GetDesktopResolution() => InputManager.GetDesktopResolution(); - - public void SetCursorPosition(int x, int y) => InputManager.SetCursorPosition(x, y); - public void SetCursorFriction(float frictionValue) => InputManager.SetCursorFriction(frictionValue); - - public void PressKey(InputKey key) => InputManager.PressKey(key); - public void ClearKeys() => InputManager.ClearKeys(); - public int GetVirtualKeyCode(InputKey key) => InputManager.GetVirtualKeyCode(key); - public float GetMouseMoveX() => InputManager.GetMouseMoveX(); - public void UpdateKeyData(byte[] keyData) => InputManager.UpdateKeyData(keyData); - - public bool IsControllerConnected() => InputManager.IsControllerConnected(); -#if v100 || v101 || v102 || v103 || v110 || v111 || v112 || v113 || v114 || v115 - public InputKey GetControllerClickKey() => InputManager.GetControllerClickKey(); -#endif + public bool IsMouseActive() => InputManager.IsMouseActive(); + public float GetMousePositionX() => InputManager.GetMousePositionX(); + public float GetMousePositionY() => InputManager.GetMousePositionY(); + public float GetMouseScrollValue() => InputManager.GetMouseScrollValue(); + public float GetMouseMoveY() => InputManager.GetMouseMoveY(); + public float GetMouseSensitivity() => InputManager.GetMouseSensitivity(); + public float GetMouseDeltaZ() => InputManager.GetMouseDeltaZ(); -#if v110 || v111 - public void SetRumbleEffect(float[] lowFrequencyLevels, float[] lowFrequencyDurations, int numLowFrequencyElements, float[] highFrequencyLevels, - float[] highFrequencyDurations, int numHighFrequencyElements) => - InputManager.SetRumbleEffect(lowFrequencyLevels, lowFrequencyDurations, numLowFrequencyElements, highFrequencyLevels, highFrequencyDurations, numHighFrequencyElements); - public void SetTriggerFeedback(byte leftTriggerPosition, byte leftTriggerStrength, byte rightTriggerPosition, byte rightTriggerStrength) => - InputManager.SetTriggerFeedback(leftTriggerPosition, leftTriggerStrength, rightTriggerPosition, rightTriggerStrength); - public void SetTriggerWeaponEffect(byte leftStartPosition, byte leftEnd_position, byte leftStrength, byte rightStartPosition, byte rightEndPosition, byte rightStrength) => - InputManager.SetTriggerWeaponEffect(leftStartPosition, leftEnd_position, leftStrength, rightStartPosition, rightEndPosition, rightStrength); - public void SetTriggerVibration(float[] leftTriggerAmplitudes, float[] leftTriggerFrequencies, float[] leftTriggerDurations, int numLeftTriggerElements, - float[] rightTriggerAmplitudes, float[] rightTriggerFrequencies, float[] rightTriggerDurations, int numRightTriggerElements) => - InputManager.SetTriggerVibration(leftTriggerAmplitudes, leftTriggerFrequencies, leftTriggerDurations, numLeftTriggerElements, rightTriggerAmplitudes, - rightTriggerFrequencies, rightTriggerDurations, numRightTriggerElements); - public void SetLightbarColor(float red, float green, float blue) => InputManager.SetLightbarColor(red, green, blue); -#endif + public void SetClipboardText(string text) + { + var thread = new Thread(() => WindowsClipboard.SetText(text)); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); + //_inputManagerImplementation.SetClipboardText(text); + } + public string GetClipboardText() + { + var text = string.Empty; + var thread = new Thread(() => text = WindowsClipboard.GetText()); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); + return text; + //return _inputManagerImplementation.GetClipboardText(); + } - private static TReturn IsAction(InputKey key, Func action, Func fallback) - { - var rawKey = key switch - { - InputKey.BackSpace => Keys.Back, - InputKey.Enter => Keys.Enter, - _ => Enum.TryParse(key.ToString(), out var keyVal) ? keyVal : Keys.None - }; - if (key is >= InputKey.ControllerLStickUp and <= InputKey.ControllerRTrigger or InputKey.ControllerLStick or InputKey.ControllerRStick) - { - return fallback(key); - } - if (key is InputKey.LeftMouseButton or InputKey.RightMouseButton or InputKey.MiddleMouseButton) - { - return fallback(key); - } - if (rawKey == Keys.None) - { - Trace.TraceError($"Wrong key {key}"); - return fallback(key); - } - - return action(rawKey); - } + public Vec2 GetKeyState(InputKey key) => + IsAction(key, rawKey => new Vec2(_currentState.IsKeyDown(rawKey) ? 1f : 0f, _previousState.IsKeyDown(rawKey) ? 1f : 0f), _key => InputManager.GetKeyState(_key)); + public bool IsKeyPressed(InputKey key) => + IsAction(key, rawKey => _currentState.IsKeyDown(rawKey) && _previousState.IsKeyUp(rawKey), _key => InputManager.IsKeyPressed(_key)); + public bool IsKeyDown(InputKey key) => + IsAction(key, rawKey => _currentState.IsKeyDown(rawKey) || _previousState.IsKeyDown(rawKey), _key => InputManager.IsKeyDown(_key)); + public bool IsKeyReleased(InputKey key) => + IsAction(key, rawKey => _currentState.IsKeyUp(rawKey) && _previousState.IsKeyDown(rawKey), _key => InputManager.IsKeyReleased(_key)); + public bool IsKeyDownImmediate(InputKey key) => + IsAction(key, rawKey => _currentState.IsKeyDown(rawKey), _key => InputManager.IsKeyDownImmediate(_key)); -#if v120 - public TaleWorlds.InputSystem.Input.ControllerTypes GetControllerType() => InputManager.GetControllerType(); + public Vec2 GetResolution() => InputManager.GetResolution(); + public Vec2 GetDesktopResolution() => InputManager.GetDesktopResolution(); - public float GetGyroX() => InputManager.GetGyroX(); - public float GetGyroY() => InputManager.GetGyroY(); - public float GetGyroZ() => InputManager.GetGyroZ(); + public void SetCursorPosition(int x, int y) => InputManager.SetCursorPosition(x, y); + public void SetCursorFriction(float frictionValue) => InputManager.SetCursorFriction(frictionValue); - public InputKey[] GetClickKeys() => InputManager.GetClickKeys(); + public void PressKey(InputKey key) => InputManager.PressKey(key); + public void ClearKeys() => InputManager.ClearKeys(); + public int GetVirtualKeyCode(InputKey key) => InputManager.GetVirtualKeyCode(key); + public float GetMouseMoveX() => InputManager.GetMouseMoveX(); + public void UpdateKeyData(byte[] keyData) => InputManager.UpdateKeyData(keyData); - public void SetRumbleEffect(float[] lowFrequencyLevels, float[] lowFrequencyDurations, int numLowFrequencyElements, float[] highFrequencyLevels, float[] highFrequencyDurations, int numHighFrequencyElements) => - InputManager.SetRumbleEffect(lowFrequencyLevels, lowFrequencyDurations, numLowFrequencyElements, highFrequencyLevels, highFrequencyDurations, numHighFrequencyElements); + public bool IsControllerConnected() => InputManager.IsControllerConnected(); - public void SetTriggerFeedback(byte leftTriggerPosition, byte leftTriggerStrength, byte rightTriggerPosition, byte rightTriggerStrength) => - InputManager.SetTriggerFeedback(leftTriggerPosition, leftTriggerStrength, rightTriggerPosition, rightTriggerStrength); +#if v100 || v110 + public InputKey GetControllerClickKey() => InputManager.GetControllerClickKey(); +#endif - public void SetTriggerWeaponEffect(byte leftStartPosition, byte leftEnd_position, byte leftStrength, byte rightStartPosition, byte rightEndPosition, byte rightStrength) => - InputManager.SetTriggerWeaponEffect(leftStartPosition, leftEnd_position, leftStrength, rightStartPosition, rightEndPosition, rightStrength); +#if v110 || v120 + public void SetRumbleEffect(float[] lowFrequencyLevels, float[] lowFrequencyDurations, int numLowFrequencyElements, float[] highFrequencyLevels, float[] highFrequencyDurations, int numHighFrequencyElements) => + InputManager.SetRumbleEffect(lowFrequencyLevels, lowFrequencyDurations, numLowFrequencyElements, highFrequencyLevels, highFrequencyDurations, numHighFrequencyElements); + public void SetTriggerFeedback(byte leftTriggerPosition, byte leftTriggerStrength, byte rightTriggerPosition, byte rightTriggerStrength) => + InputManager.SetTriggerFeedback(leftTriggerPosition, leftTriggerStrength, rightTriggerPosition, rightTriggerStrength); + public void SetTriggerWeaponEffect(byte leftStartPosition, byte leftEnd_position, byte leftStrength, byte rightStartPosition, byte rightEndPosition, byte rightStrength) => + InputManager.SetTriggerWeaponEffect(leftStartPosition, leftEnd_position, leftStrength, rightStartPosition, rightEndPosition, rightStrength); + public void SetTriggerVibration(float[] leftTriggerAmplitudes, float[] leftTriggerFrequencies, float[] leftTriggerDurations, int numLeftTriggerElements, float[] rightTriggerAmplitudes, float[] rightTriggerFrequencies, float[] rightTriggerDurations, int numRightTriggerElements) => + InputManager.SetTriggerVibration(leftTriggerAmplitudes, leftTriggerFrequencies, leftTriggerDurations, numLeftTriggerElements, rightTriggerAmplitudes, rightTriggerFrequencies, rightTriggerDurations, numRightTriggerElements); + public void SetLightbarColor(float red, float green, float blue) => InputManager.SetLightbarColor(red, green, blue); +#endif - public void SetTriggerVibration(float[] leftTriggerAmplitudes, float[] leftTriggerFrequencies, float[] leftTriggerDurations, int numLeftTriggerElements, float[] rightTriggerAmplitudes, float[] rightTriggerFrequencies, float[] rightTriggerDurations, int numRightTriggerElements) => - InputManager.SetTriggerVibration(leftTriggerAmplitudes, leftTriggerFrequencies, leftTriggerDurations, numLeftTriggerElements, rightTriggerAmplitudes, rightTriggerFrequencies, rightTriggerDurations, numRightTriggerElements); +#if v120 + public TaleWorlds.InputSystem.Input.ControllerTypes GetControllerType() => InputManager.GetControllerType(); - public void SetLightbarColor(float red, float green, float blue) => InputManager.SetLightbarColor(red, green, blue); + public float GetGyroX() => InputManager.GetGyroX(); + public float GetGyroY() => InputManager.GetGyroY(); + public float GetGyroZ() => InputManager.GetGyroZ(); - public bool IsAnyTouchActive() => InputManager.IsAnyTouchActive(); + public InputKey[] GetClickKeys() => InputManager.GetClickKeys(); + + public bool IsAnyTouchActive() => InputManager.IsAnyTouchActive(); #endif - public void Dispose() + private static TReturn IsAction(InputKey key, Func action, Func fallback) + { + var rawKey = key switch { - _currentState.Dispose(); - _previousState.Dispose(); + InputKey.BackSpace => Keys.Back, + InputKey.Enter => Keys.Enter, + _ => Enum.TryParse(key.ToString(), out var keyVal) ? keyVal : Keys.None + }; + if (key is >= InputKey.ControllerLStickUp and <= InputKey.ControllerRTrigger or InputKey.ControllerLStick or InputKey.ControllerRStick) + { + return fallback(key); } + if (key is InputKey.LeftMouseButton or InputKey.RightMouseButton or InputKey.MiddleMouseButton) + { + return fallback(key); + } + if (rawKey == Keys.None) + { + Trace.TraceError($"Wrong key {key}"); + return fallback(key); + } + + return action(rawKey); + } + + + public void Dispose() + { + _currentState.Dispose(); + _previousState.Dispose(); } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/Input/KeyState.cs b/src/Bannerlord.LauncherEx/Helpers/Input/KeyState.cs index c33f25f..3645901 100644 --- a/src/Bannerlord.LauncherEx/Helpers/Input/KeyState.cs +++ b/src/Bannerlord.LauncherEx/Helpers/Input/KeyState.cs @@ -1,4 +1,3 @@ -namespace Bannerlord.LauncherEx.Helpers -{ - internal enum KeyState { Up, Down, } -} \ No newline at end of file +namespace Bannerlord.LauncherEx.Helpers; + +internal enum KeyState { Up, Down, } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/Input/Keyboard.cs b/src/Bannerlord.LauncherEx/Helpers/Input/Keyboard.cs index abe7b14..5247efb 100644 --- a/src/Bannerlord.LauncherEx/Helpers/Input/Keyboard.cs +++ b/src/Bannerlord.LauncherEx/Helpers/Input/Keyboard.cs @@ -3,16 +3,15 @@ using Windows.Win32; -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +internal static class Keyboard { - internal static class Keyboard + public static KeyboardState GetState() { - public static KeyboardState GetState() - { - var keyState = MemoryPool.Shared.Rent(256); - return !PInvoke.GetKeyboardState(keyState.Memory.Span) - ? KeyboardState.Empty - : new KeyboardState(keyState, Console.CapsLock, Console.NumberLock); - } + var keyState = MemoryPool.Shared.Rent(256); + return !PInvoke.GetKeyboardState(keyState.Memory.Span) + ? KeyboardState.Empty + : new KeyboardState(keyState, Console.CapsLock, Console.NumberLock); } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/Input/KeyboardState.cs b/src/Bannerlord.LauncherEx/Helpers/Input/KeyboardState.cs index 4bef231..29a105d 100644 --- a/src/Bannerlord.LauncherEx/Helpers/Input/KeyboardState.cs +++ b/src/Bannerlord.LauncherEx/Helpers/Input/KeyboardState.cs @@ -6,170 +6,169 @@ using Windows.Win32; using Windows.Win32.Foundation; -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +internal readonly struct KeyboardState { - internal readonly struct KeyboardState - { - public static KeyboardState Empty = new(null); + public static KeyboardState Empty = new(null); - private static readonly byte[] _definedKeyCodes = - ((Keys[]) Enum.GetValues(typeof(Keys))).Cast().Where(keyCode => keyCode is >= 1 and <= 255).Select(keyCode => (byte) keyCode).ToArray(); + private static readonly byte[] _definedKeyCodes = + ((Keys[]) Enum.GetValues(typeof(Keys))).Cast().Where(keyCode => keyCode is >= 1 and <= 255).Select(keyCode => (byte) keyCode).ToArray(); - private const byte CapsLockModifier = 1; - private const byte NumLockModifier = 2; + private const byte CapsLockModifier = 1; + private const byte NumLockModifier = 2; - private readonly IMemoryOwner? _keyState; - // Array of 256 bits: - private readonly uint _keys0, _keys1, _keys2, _keys3, _keys4, _keys5, _keys6, _keys7; - private readonly byte _modifiers; + private readonly IMemoryOwner? _keyState; + // Array of 256 bits: + private readonly uint _keys0, _keys1, _keys2, _keys3, _keys4, _keys5, _keys6, _keys7; + private readonly byte _modifiers; - public bool CapsLock => (_modifiers & CapsLockModifier) > 0; - public bool NumLock => (_modifiers & NumLockModifier) > 0; - public KeyState this[Keys key] => InternalGetKey(key) ? KeyState.Down : KeyState.Up; + public bool CapsLock => (_modifiers & CapsLockModifier) > 0; + public bool NumLock => (_modifiers & NumLockModifier) > 0; + public KeyState this[Keys key] => InternalGetKey(key) ? KeyState.Down : KeyState.Up; - public KeyboardState(IMemoryOwner? keyState, bool capsLock = false, bool numLock = false) : this() + public KeyboardState(IMemoryOwner? keyState, bool capsLock = false, bool numLock = false) : this() + { + _keyState = keyState; + _keys0 = 0; + _keys1 = 0; + _keys2 = 0; + _keys3 = 0; + _keys4 = 0; + _keys5 = 0; + _keys6 = 0; + _keys7 = 0; + _modifiers = (byte) (0 | (capsLock ? CapsLockModifier : 0) | (numLock ? NumLockModifier : 0)); + + if (_keyState is null) return; + var keys = new HashSet(); + var span = _keyState.Memory.Span; + for (var i = 0; i < _definedKeyCodes.Length; i++) { - _keyState = keyState; - _keys0 = 0; - _keys1 = 0; - _keys2 = 0; - _keys3 = 0; - _keys4 = 0; - _keys5 = 0; - _keys6 = 0; - _keys7 = 0; - _modifiers = (byte) (0 | (capsLock ? CapsLockModifier : 0) | (numLock ? NumLockModifier : 0)); - - if (_keyState is null) return; - var keys = new HashSet(); - var span = _keyState.Memory.Span; - for (var i = 0; i < _definedKeyCodes.Length; i++) + if ((span[_definedKeyCodes[i]] & 0x80) == 0) continue; + var key = (Keys) _definedKeyCodes[i]; + if (keys.Contains(key)) continue; + keys.Add(key); + + var mask = (uint) 1 << ((int) key & 0x1f); + switch ((int) key >> 5) { - if ((span[_definedKeyCodes[i]] & 0x80) == 0) continue; - var key = (Keys) _definedKeyCodes[i]; - if (keys.Contains(key)) continue; - keys.Add(key); - - var mask = (uint) 1 << ((int) key & 0x1f); - switch ((int) key >> 5) - { - case 0: _keys0 |= mask; break; - case 1: _keys1 |= mask; break; - case 2: _keys2 |= mask; break; - case 3: _keys3 |= mask; break; - case 4: _keys4 |= mask; break; - case 5: _keys5 |= mask; break; - case 6: _keys6 |= mask; break; - case 7: _keys7 |= mask; break; - } + case 0: _keys0 |= mask; break; + case 1: _keys1 |= mask; break; + case 2: _keys2 |= mask; break; + case 3: _keys3 |= mask; break; + case 4: _keys4 |= mask; break; + case 5: _keys5 |= mask; break; + case 6: _keys6 |= mask; break; + case 7: _keys7 |= mask; break; } } + } - public bool IsKeyDown(Keys key) => InternalGetKey(key); - public bool IsKeyUp(Keys key) => !InternalGetKey(key); + public bool IsKeyDown(Keys key) => InternalGetKey(key); + public bool IsKeyUp(Keys key) => !InternalGetKey(key); - public int GetPressedKeyCount() - { - var count = CountBits(_keys0) + CountBits(_keys1) + CountBits(_keys2) + CountBits(_keys3) - + CountBits(_keys4) + CountBits(_keys5) + CountBits(_keys6) + CountBits(_keys7); - return (int) count; - } - public Keys[] GetPressedKeys() - { - var count = GetPressedKeyCount(); - if (count == 0) return Array.Empty(); - var keys = new Keys[count]; - - var index = 0; - if (_keys0 != 0) index = AddKeysToArray(_keys0, 0 * 32, keys, index); - if (_keys1 != 0) index = AddKeysToArray(_keys1, 1 * 32, keys, index); - if (_keys2 != 0) index = AddKeysToArray(_keys2, 2 * 32, keys, index); - if (_keys3 != 0) index = AddKeysToArray(_keys3, 3 * 32, keys, index); - if (_keys4 != 0) index = AddKeysToArray(_keys4, 4 * 32, keys, index); - if (_keys5 != 0) index = AddKeysToArray(_keys5, 5 * 32, keys, index); - if (_keys6 != 0) index = AddKeysToArray(_keys6, 6 * 32, keys, index); - if (_keys7 != 0) index = AddKeysToArray(_keys7, 7 * 32, keys, index); - - return keys; - } + public int GetPressedKeyCount() + { + var count = CountBits(_keys0) + CountBits(_keys1) + CountBits(_keys2) + CountBits(_keys3) + + CountBits(_keys4) + CountBits(_keys5) + CountBits(_keys6) + CountBits(_keys7); + return (int) count; + } + public Keys[] GetPressedKeys() + { + var count = GetPressedKeyCount(); + if (count == 0) return Array.Empty(); + var keys = new Keys[count]; + + var index = 0; + if (_keys0 != 0) index = AddKeysToArray(_keys0, 0 * 32, keys, index); + if (_keys1 != 0) index = AddKeysToArray(_keys1, 1 * 32, keys, index); + if (_keys2 != 0) index = AddKeysToArray(_keys2, 2 * 32, keys, index); + if (_keys3 != 0) index = AddKeysToArray(_keys3, 3 * 32, keys, index); + if (_keys4 != 0) index = AddKeysToArray(_keys4, 4 * 32, keys, index); + if (_keys5 != 0) index = AddKeysToArray(_keys5, 5 * 32, keys, index); + if (_keys6 != 0) index = AddKeysToArray(_keys6, 6 * 32, keys, index); + if (_keys7 != 0) index = AddKeysToArray(_keys7, 7 * 32, keys, index); + + return keys; + } - private bool InternalGetKey(Keys key) + private bool InternalGetKey(Keys key) + { + var mask = (uint) 1 << ((int) key & 0x1f); + uint element = ((int) key >> 5) switch { - var mask = (uint) 1 << ((int) key & 0x1f); - uint element = ((int) key >> 5) switch - { - 0 => _keys0, - 1 => _keys1, - 2 => _keys2, - 3 => _keys3, - 4 => _keys4, - 5 => _keys5, - 6 => _keys6, - 7 => _keys7, - _ => 0 - }; - return (element & mask) != 0; - } + 0 => _keys0, + 1 => _keys1, + 2 => _keys2, + 3 => _keys3, + 4 => _keys4, + 5 => _keys5, + 6 => _keys6, + 7 => _keys7, + _ => 0 + }; + return (element & mask) != 0; + } - public override int GetHashCode() => (int) (_keys0 ^ _keys1 ^ _keys2 ^ _keys3 ^ _keys4 ^ _keys5 ^ _keys6 ^ _keys7); + public override int GetHashCode() => (int) (_keys0 ^ _keys1 ^ _keys2 ^ _keys3 ^ _keys4 ^ _keys5 ^ _keys6 ^ _keys7); - public static bool operator ==(KeyboardState a, KeyboardState b) => a._keys0 == b._keys0 && - a._keys1 == b._keys1 && - a._keys2 == b._keys2 && - a._keys3 == b._keys3 && - a._keys4 == b._keys4 && - a._keys5 == b._keys5 && - a._keys6 == b._keys6 && - a._keys7 == b._keys7; + public static bool operator ==(KeyboardState a, KeyboardState b) => a._keys0 == b._keys0 && + a._keys1 == b._keys1 && + a._keys2 == b._keys2 && + a._keys3 == b._keys3 && + a._keys4 == b._keys4 && + a._keys5 == b._keys5 && + a._keys6 == b._keys6 && + a._keys7 == b._keys7; - public static bool operator !=(KeyboardState a, KeyboardState b) => !(a == b); + public static bool operator !=(KeyboardState a, KeyboardState b) => !(a == b); - public override bool Equals(object? obj) => obj is KeyboardState state && this == state; + public override bool Equals(object? obj) => obj is KeyboardState state && this == state; - public unsafe string AsString(Keys key) - { - if (_keyState is null) - return string.Empty; + public unsafe string AsString(Keys key) + { + if (_keyState is null) + return string.Empty; - var vkCode = (uint) key; + var vkCode = (uint) key; - var currentHWnd = PInvoke.GetForegroundWindow(); - var currentWindowThreadID = PInvoke.GetWindowThreadProcessId(currentHWnd); + var currentHWnd = PInvoke.GetForegroundWindow(); + var currentWindowThreadID = PInvoke.GetWindowThreadProcessId(currentHWnd); - var hkl = PInvoke.GetKeyboardLayout(currentWindowThreadID); - var lScanCode = PInvoke.MapVirtualKeyEx(vkCode, 0, hkl); + var hkl = PInvoke.GetKeyboardLayout(currentWindowThreadID); + var lScanCode = PInvoke.MapVirtualKeyEx(vkCode, 0, hkl); - var span = stackalloc char[5]; - var relevantKeyCountInBuffer = PInvoke.ToUnicodeEx(vkCode, lScanCode, _keyState.Memory.Span, new PWSTR(span), 5, 0, hkl); - return relevantKeyCountInBuffer switch - { - -1 or 0 => string.Empty, - 1 => span[0].ToString(), - 2 or _ => new string(span, 0, 2), - }; - } - - private static uint CountBits(uint v) + var span = stackalloc char[5]; + var relevantKeyCountInBuffer = PInvoke.ToUnicodeEx(vkCode, lScanCode, _keyState.Memory.Span, new PWSTR(span), 5, 0, hkl); + return relevantKeyCountInBuffer switch { - // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel - v = v - ((v >> 1) & 0x55555555); // reuse input as temporary - v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp - return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count - } + -1 or 0 => string.Empty, + 1 => span[0].ToString(), + 2 or _ => new string(span, 0, 2), + }; + } - private static int AddKeysToArray(uint keys, int offset, Keys[] pressedKeys, int index) - { - for (var i = 0; i < 32; i++) - { - if ((keys & (1 << i)) != 0) - pressedKeys[index++] = (Keys) (offset + i); - } - return index; - } + private static uint CountBits(uint v) + { + // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + v = v - ((v >> 1) & 0x55555555); // reuse input as temporary + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp + return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count + } - public void Dispose() + private static int AddKeysToArray(uint keys, int offset, Keys[] pressedKeys, int index) + { + for (var i = 0; i < 32; i++) { - _keyState?.Dispose(); + if ((keys & (1 << i)) != 0) + pressedKeys[index++] = (Keys) (offset + i); } + return index; + } + + public void Dispose() + { + _keyState?.Dispose(); } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/Input/Keys.cs b/src/Bannerlord.LauncherEx/Helpers/Input/Keys.cs index 4260c33..fab8e4b 100644 --- a/src/Bannerlord.LauncherEx/Helpers/Input/Keys.cs +++ b/src/Bannerlord.LauncherEx/Helpers/Input/Keys.cs @@ -1,646 +1,645 @@ -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +internal enum Keys { - internal enum Keys - { - /// - /// Reserved. - /// - None = 0, - /// - /// BACKSPACE key. - /// - Back = 8, - /// - /// TAB key. - /// - Tab = 9, - /// - /// ENTER key. - /// - Enter = 13, - /// - /// CAPS LOCK key. - /// - CapsLock = 20, - /// - /// ESC key. - /// - Escape = 27, - /// - /// SPACEBAR key. - /// - Space = 32, - /// - /// PAGE UP key. - /// - PageUp = 33, - /// - /// PAGE DOWN key. - /// - PageDown = 34, - /// - /// END key. - /// - End = 35, - /// - /// HOME key. - /// - Home = 36, - /// - /// LEFT ARROW key. - /// - Left = 37, - /// - /// UP ARROW key. - /// - Up = 38, - /// - /// RIGHT ARROW key. - /// - Right = 39, - /// - /// DOWN ARROW key. - /// - Down = 40, - /// - /// SELECT key. - /// - Select = 41, - /// - /// PRINT key. - /// - Print = 42, - /// - /// EXECUTE key. - /// - Execute = 43, - /// - /// PRINT SCREEN key. - /// - PrintScreen = 44, - /// - /// INS key. - /// - Insert = 45, - /// - /// DEL key. - /// - Delete = 46, - /// - /// HELP key. - /// - Help = 47, - /// - /// Used for miscellaneous characters; it can vary by keyboard. - /// - D0 = 48, - /// - /// Used for miscellaneous characters; it can vary by keyboard. - /// - D1 = 49, - /// - /// Used for miscellaneous characters; it can vary by keyboard. - /// - D2 = 50, - /// - /// Used for miscellaneous characters; it can vary by keyboard. - /// - D3 = 51, - /// - /// Used for miscellaneous characters; it can vary by keyboard. - /// - D4 = 52, - /// - /// Used for miscellaneous characters; it can vary by keyboard. - /// - D5 = 53, - /// - /// Used for miscellaneous characters; it can vary by keyboard. - /// - D6 = 54, - /// - /// Used for miscellaneous characters; it can vary by keyboard. - /// - D7 = 55, - /// - /// Used for miscellaneous characters; it can vary by keyboard. - /// - D8 = 56, - /// - /// Used for miscellaneous characters; it can vary by keyboard. - /// - D9 = 57, - /// - /// A key. - /// - A = 65, - /// - /// B key. - /// - B = 66, - /// - /// C key. - /// - C = 67, - /// - /// D key. - /// - D = 68, - /// - /// E key. - /// - E = 69, - /// - /// F key. - /// - F = 70, - /// - /// G key. - /// - G = 71, - /// - /// H key. - /// - H = 72, - /// - /// I key. - /// - I = 73, - /// - /// J key. - /// - J = 74, - /// - /// K key. - /// - K = 75, - /// - /// L key. - /// - L = 76, - /// - /// M key. - /// - M = 77, - /// - /// N key. - /// - N = 78, - /// - /// O key. - /// - O = 79, - /// - /// P key. - /// - P = 80, - /// - /// Q key. - /// - Q = 81, - /// - /// R key. - /// - R = 82, - /// - /// S key. - /// - S = 83, - /// - /// T key. - /// - T = 84, - /// - /// U key. - /// - U = 85, - /// - /// V key. - /// - V = 86, - /// - /// W key. - /// - W = 87, - /// - /// X key. - /// - X = 88, - /// - /// Y key. - /// - Y = 89, - /// - /// Z key. - /// - Z = 90, - /// - /// Left Windows key. - /// - LeftWindows = 91, - /// - /// Right Windows key. - /// - RightWindows = 92, - /// - /// Applications key. - /// - Apps = 93, - /// - /// Computer Sleep key. - /// - Sleep = 95, - /// - /// Numeric keypad 0 key. - /// - NumPad0 = 96, - /// - /// Numeric keypad 1 key. - /// - NumPad1 = 97, - /// - /// Numeric keypad 2 key. - /// - NumPad2 = 98, - /// - /// Numeric keypad 3 key. - /// - NumPad3 = 99, - /// - /// Numeric keypad 4 key. - /// - NumPad4 = 100, - /// - /// Numeric keypad 5 key. - /// - NumPad5 = 101, - /// - /// Numeric keypad 6 key. - /// - NumPad6 = 102, - /// - /// Numeric keypad 7 key. - /// - NumPad7 = 103, - /// - /// Numeric keypad 8 key. - /// - NumPad8 = 104, - /// - /// Numeric keypad 9 key. - /// - NumPad9 = 105, - /// - /// Multiply key. - /// - Multiply = 106, - /// - /// Add key. - /// - Add = 107, - /// - /// Separator key. - /// - Separator = 108, - /// - /// Subtract key. - /// - Subtract = 109, - /// - /// Decimal key. - /// - Decimal = 110, - /// - /// Divide key. - /// - Divide = 111, - /// - /// F1 key. - /// - F1 = 112, - /// - /// F2 key. - /// - F2 = 113, - /// - /// F3 key. - /// - F3 = 114, - /// - /// F4 key. - /// - F4 = 115, - /// - /// F5 key. - /// - F5 = 116, - /// - /// F6 key. - /// - F6 = 117, - /// - /// F7 key. - /// - F7 = 118, - /// - /// F8 key. - /// - F8 = 119, - /// - /// F9 key. - /// - F9 = 120, - /// - /// F10 key. - /// - F10 = 121, - /// - /// F11 key. - /// - F11 = 122, - /// - /// F12 key. - /// - F12 = 123, - /// - /// F13 key. - /// - F13 = 124, - /// - /// F14 key. - /// - F14 = 125, - /// - /// F15 key. - /// - F15 = 126, - /// - /// F16 key. - /// - F16 = 127, - /// - /// F17 key. - /// - F17 = 128, - /// - /// F18 key. - /// - F18 = 129, - /// - /// F19 key. - /// - F19 = 130, - /// - /// F20 key. - /// - F20 = 131, - /// - /// F21 key. - /// - F21 = 132, - /// - /// F22 key. - /// - F22 = 133, - /// - /// F23 key. - /// - F23 = 134, - /// - /// F24 key. - /// - F24 = 135, - /// - /// NUM LOCK key. - /// - NumLock = 144, - /// - /// SCROLL LOCK key. - /// - Scroll = 145, - /// - /// Left SHIFT key. - /// - LeftShift = 160, - /// - /// Right SHIFT key. - /// - RightShift = 161, - /// - /// Left CONTROL key. - /// - LeftControl = 162, - /// - /// Right CONTROL key. - /// - RightControl = 163, - /// - /// Left ALT key. - /// - LeftAlt = 164, - /// - /// Right ALT key. - /// - RightAlt = 165, - /// - /// Browser Back key. - /// - BrowserBack = 166, - /// - /// Browser Forward key. - /// - BrowserForward = 167, - /// - /// Browser Refresh key. - /// - BrowserRefresh = 168, - /// - /// Browser Stop key. - /// - BrowserStop = 169, - /// - /// Browser Search key. - /// - BrowserSearch = 170, - /// - /// Browser Favorites key. - /// - BrowserFavorites = 171, - /// - /// Browser Start and Home key. - /// - BrowserHome = 172, - /// - /// Volume Mute key. - /// - VolumeMute = 173, - /// - /// Volume Down key. - /// - VolumeDown = 174, - /// - /// Volume Up key. - /// - VolumeUp = 175, - /// - /// Next Track key. - /// - MediaNextTrack = 176, - /// - /// Previous Track key. - /// - MediaPreviousTrack = 177, - /// - /// Stop Media key. - /// - MediaStop = 178, - /// - /// Play/Pause Media key. - /// - MediaPlayPause = 179, - /// - /// Start Mail key. - /// - LaunchMail = 180, - /// - /// Select Media key. - /// - SelectMedia = 181, - /// - /// Start Application 1 key. - /// - LaunchApplication1 = 182, - /// - /// Start Application 2 key. - /// - LaunchApplication2 = 183, - /// - /// The OEM Semicolon key on a US standard keyboard. - /// - OemSemicolon = 186, - /// - /// For any country/region, the '+' key. - /// - OemPlus = 187, - /// - /// For any country/region, the ',' key. - /// - OemComma = 188, - /// - /// For any country/region, the '-' key. - /// - OemMinus = 189, - /// - /// For any country/region, the '.' key. - /// - OemPeriod = 190, - /// - /// The OEM question mark key on a US standard keyboard. - /// - OemQuestion = 191, - /// - /// The OEM tilde key on a US standard keyboard. - /// - OemTilde = 192, - /// - /// The OEM open bracket key on a US standard keyboard. - /// - OemOpenBrackets = 219, - /// - /// The OEM pipe key on a US standard keyboard. - /// - OemPipe = 220, - /// - /// The OEM close bracket key on a US standard keyboard. - /// - OemCloseBrackets = 221, - /// - /// The OEM singled/double quote key on a US standard keyboard. - /// - OemQuotes = 222, - /// - /// Used for miscellaneous characters; it can vary by keyboard. - /// - Oem8 = 223, - /// - /// The OEM angle bracket or backslash key on the RT 102 key keyboard. - /// - OemBackslash = 226, - /// - /// IME PROCESS key. - /// - ProcessKey = 229, - /// - /// Attn key. - /// - Attn = 246, - /// - /// CrSel key. - /// - Crsel = 247, - /// - /// ExSel key. - /// - Exsel = 248, - /// - /// Erase EOF key. - /// - EraseEof = 249, - /// - /// Play key. - /// - Play = 250, - /// - /// Zoom key. - /// - Zoom = 251, - /// - /// PA1 key. - /// - Pa1 = 253, - /// - /// CLEAR key. - /// - OemClear = 254, - /// - /// Green ChatPad key. - /// - ChatPadGreen = 0xCA, - /// - /// Orange ChatPad key. - /// - ChatPadOrange = 0xCB, - /// - /// PAUSE key. - /// - Pause = 0x13, - /// - /// IME Convert key. - /// - ImeConvert = 0x1c, - /// - /// IME NoConvert key. - /// - ImeNoConvert = 0x1d, - /// - /// Kana key on Japanese keyboards. - /// - Kana = 0x15, - /// - /// Kanji key on Japanese keyboards. - /// - Kanji = 0x19, - /// - /// OEM Auto key. - /// - OemAuto = 0xf3, - /// - /// OEM Copy key. - /// - OemCopy = 0xf2, - /// - /// OEM Enlarge Window key. - /// - OemEnlW = 0xf4 - } + /// + /// Reserved. + /// + None = 0, + /// + /// BACKSPACE key. + /// + Back = 8, + /// + /// TAB key. + /// + Tab = 9, + /// + /// ENTER key. + /// + Enter = 13, + /// + /// CAPS LOCK key. + /// + CapsLock = 20, + /// + /// ESC key. + /// + Escape = 27, + /// + /// SPACEBAR key. + /// + Space = 32, + /// + /// PAGE UP key. + /// + PageUp = 33, + /// + /// PAGE DOWN key. + /// + PageDown = 34, + /// + /// END key. + /// + End = 35, + /// + /// HOME key. + /// + Home = 36, + /// + /// LEFT ARROW key. + /// + Left = 37, + /// + /// UP ARROW key. + /// + Up = 38, + /// + /// RIGHT ARROW key. + /// + Right = 39, + /// + /// DOWN ARROW key. + /// + Down = 40, + /// + /// SELECT key. + /// + Select = 41, + /// + /// PRINT key. + /// + Print = 42, + /// + /// EXECUTE key. + /// + Execute = 43, + /// + /// PRINT SCREEN key. + /// + PrintScreen = 44, + /// + /// INS key. + /// + Insert = 45, + /// + /// DEL key. + /// + Delete = 46, + /// + /// HELP key. + /// + Help = 47, + /// + /// Used for miscellaneous characters; it can vary by keyboard. + /// + D0 = 48, + /// + /// Used for miscellaneous characters; it can vary by keyboard. + /// + D1 = 49, + /// + /// Used for miscellaneous characters; it can vary by keyboard. + /// + D2 = 50, + /// + /// Used for miscellaneous characters; it can vary by keyboard. + /// + D3 = 51, + /// + /// Used for miscellaneous characters; it can vary by keyboard. + /// + D4 = 52, + /// + /// Used for miscellaneous characters; it can vary by keyboard. + /// + D5 = 53, + /// + /// Used for miscellaneous characters; it can vary by keyboard. + /// + D6 = 54, + /// + /// Used for miscellaneous characters; it can vary by keyboard. + /// + D7 = 55, + /// + /// Used for miscellaneous characters; it can vary by keyboard. + /// + D8 = 56, + /// + /// Used for miscellaneous characters; it can vary by keyboard. + /// + D9 = 57, + /// + /// A key. + /// + A = 65, + /// + /// B key. + /// + B = 66, + /// + /// C key. + /// + C = 67, + /// + /// D key. + /// + D = 68, + /// + /// E key. + /// + E = 69, + /// + /// F key. + /// + F = 70, + /// + /// G key. + /// + G = 71, + /// + /// H key. + /// + H = 72, + /// + /// I key. + /// + I = 73, + /// + /// J key. + /// + J = 74, + /// + /// K key. + /// + K = 75, + /// + /// L key. + /// + L = 76, + /// + /// M key. + /// + M = 77, + /// + /// N key. + /// + N = 78, + /// + /// O key. + /// + O = 79, + /// + /// P key. + /// + P = 80, + /// + /// Q key. + /// + Q = 81, + /// + /// R key. + /// + R = 82, + /// + /// S key. + /// + S = 83, + /// + /// T key. + /// + T = 84, + /// + /// U key. + /// + U = 85, + /// + /// V key. + /// + V = 86, + /// + /// W key. + /// + W = 87, + /// + /// X key. + /// + X = 88, + /// + /// Y key. + /// + Y = 89, + /// + /// Z key. + /// + Z = 90, + /// + /// Left Windows key. + /// + LeftWindows = 91, + /// + /// Right Windows key. + /// + RightWindows = 92, + /// + /// Applications key. + /// + Apps = 93, + /// + /// Computer Sleep key. + /// + Sleep = 95, + /// + /// Numeric keypad 0 key. + /// + NumPad0 = 96, + /// + /// Numeric keypad 1 key. + /// + NumPad1 = 97, + /// + /// Numeric keypad 2 key. + /// + NumPad2 = 98, + /// + /// Numeric keypad 3 key. + /// + NumPad3 = 99, + /// + /// Numeric keypad 4 key. + /// + NumPad4 = 100, + /// + /// Numeric keypad 5 key. + /// + NumPad5 = 101, + /// + /// Numeric keypad 6 key. + /// + NumPad6 = 102, + /// + /// Numeric keypad 7 key. + /// + NumPad7 = 103, + /// + /// Numeric keypad 8 key. + /// + NumPad8 = 104, + /// + /// Numeric keypad 9 key. + /// + NumPad9 = 105, + /// + /// Multiply key. + /// + Multiply = 106, + /// + /// Add key. + /// + Add = 107, + /// + /// Separator key. + /// + Separator = 108, + /// + /// Subtract key. + /// + Subtract = 109, + /// + /// Decimal key. + /// + Decimal = 110, + /// + /// Divide key. + /// + Divide = 111, + /// + /// F1 key. + /// + F1 = 112, + /// + /// F2 key. + /// + F2 = 113, + /// + /// F3 key. + /// + F3 = 114, + /// + /// F4 key. + /// + F4 = 115, + /// + /// F5 key. + /// + F5 = 116, + /// + /// F6 key. + /// + F6 = 117, + /// + /// F7 key. + /// + F7 = 118, + /// + /// F8 key. + /// + F8 = 119, + /// + /// F9 key. + /// + F9 = 120, + /// + /// F10 key. + /// + F10 = 121, + /// + /// F11 key. + /// + F11 = 122, + /// + /// F12 key. + /// + F12 = 123, + /// + /// F13 key. + /// + F13 = 124, + /// + /// F14 key. + /// + F14 = 125, + /// + /// F15 key. + /// + F15 = 126, + /// + /// F16 key. + /// + F16 = 127, + /// + /// F17 key. + /// + F17 = 128, + /// + /// F18 key. + /// + F18 = 129, + /// + /// F19 key. + /// + F19 = 130, + /// + /// F20 key. + /// + F20 = 131, + /// + /// F21 key. + /// + F21 = 132, + /// + /// F22 key. + /// + F22 = 133, + /// + /// F23 key. + /// + F23 = 134, + /// + /// F24 key. + /// + F24 = 135, + /// + /// NUM LOCK key. + /// + NumLock = 144, + /// + /// SCROLL LOCK key. + /// + Scroll = 145, + /// + /// Left SHIFT key. + /// + LeftShift = 160, + /// + /// Right SHIFT key. + /// + RightShift = 161, + /// + /// Left CONTROL key. + /// + LeftControl = 162, + /// + /// Right CONTROL key. + /// + RightControl = 163, + /// + /// Left ALT key. + /// + LeftAlt = 164, + /// + /// Right ALT key. + /// + RightAlt = 165, + /// + /// Browser Back key. + /// + BrowserBack = 166, + /// + /// Browser Forward key. + /// + BrowserForward = 167, + /// + /// Browser Refresh key. + /// + BrowserRefresh = 168, + /// + /// Browser Stop key. + /// + BrowserStop = 169, + /// + /// Browser Search key. + /// + BrowserSearch = 170, + /// + /// Browser Favorites key. + /// + BrowserFavorites = 171, + /// + /// Browser Start and Home key. + /// + BrowserHome = 172, + /// + /// Volume Mute key. + /// + VolumeMute = 173, + /// + /// Volume Down key. + /// + VolumeDown = 174, + /// + /// Volume Up key. + /// + VolumeUp = 175, + /// + /// Next Track key. + /// + MediaNextTrack = 176, + /// + /// Previous Track key. + /// + MediaPreviousTrack = 177, + /// + /// Stop Media key. + /// + MediaStop = 178, + /// + /// Play/Pause Media key. + /// + MediaPlayPause = 179, + /// + /// Start Mail key. + /// + LaunchMail = 180, + /// + /// Select Media key. + /// + SelectMedia = 181, + /// + /// Start Application 1 key. + /// + LaunchApplication1 = 182, + /// + /// Start Application 2 key. + /// + LaunchApplication2 = 183, + /// + /// The OEM Semicolon key on a US standard keyboard. + /// + OemSemicolon = 186, + /// + /// For any country/region, the '+' key. + /// + OemPlus = 187, + /// + /// For any country/region, the ',' key. + /// + OemComma = 188, + /// + /// For any country/region, the '-' key. + /// + OemMinus = 189, + /// + /// For any country/region, the '.' key. + /// + OemPeriod = 190, + /// + /// The OEM question mark key on a US standard keyboard. + /// + OemQuestion = 191, + /// + /// The OEM tilde key on a US standard keyboard. + /// + OemTilde = 192, + /// + /// The OEM open bracket key on a US standard keyboard. + /// + OemOpenBrackets = 219, + /// + /// The OEM pipe key on a US standard keyboard. + /// + OemPipe = 220, + /// + /// The OEM close bracket key on a US standard keyboard. + /// + OemCloseBrackets = 221, + /// + /// The OEM singled/double quote key on a US standard keyboard. + /// + OemQuotes = 222, + /// + /// Used for miscellaneous characters; it can vary by keyboard. + /// + Oem8 = 223, + /// + /// The OEM angle bracket or backslash key on the RT 102 key keyboard. + /// + OemBackslash = 226, + /// + /// IME PROCESS key. + /// + ProcessKey = 229, + /// + /// Attn key. + /// + Attn = 246, + /// + /// CrSel key. + /// + Crsel = 247, + /// + /// ExSel key. + /// + Exsel = 248, + /// + /// Erase EOF key. + /// + EraseEof = 249, + /// + /// Play key. + /// + Play = 250, + /// + /// Zoom key. + /// + Zoom = 251, + /// + /// PA1 key. + /// + Pa1 = 253, + /// + /// CLEAR key. + /// + OemClear = 254, + /// + /// Green ChatPad key. + /// + ChatPadGreen = 0xCA, + /// + /// Orange ChatPad key. + /// + ChatPadOrange = 0xCB, + /// + /// PAUSE key. + /// + Pause = 0x13, + /// + /// IME Convert key. + /// + ImeConvert = 0x1c, + /// + /// IME NoConvert key. + /// + ImeNoConvert = 0x1d, + /// + /// Kana key on Japanese keyboards. + /// + Kana = 0x15, + /// + /// Kanji key on Japanese keyboards. + /// + Kanji = 0x19, + /// + /// OEM Auto key. + /// + OemAuto = 0xf3, + /// + /// OEM Copy key. + /// + OemCopy = 0xf2, + /// + /// OEM Enlarge Window key. + /// + OemEnlW = 0xf4 } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/Integrations.cs b/src/Bannerlord.LauncherEx/Helpers/Integrations.cs index 678dc7e..3b37284 100644 --- a/src/Bannerlord.LauncherEx/Helpers/Integrations.cs +++ b/src/Bannerlord.LauncherEx/Helpers/Integrations.cs @@ -3,20 +3,19 @@ using System.Diagnostics; using System.IO; -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +public static class Integrations { - public static class Integrations - { - public static bool IsModOrganizer2 { get; } - public static string? ModOrganizer2Path { get; } + public static bool IsModOrganizer2 { get; } + public static string? ModOrganizer2Path { get; } - static Integrations() + static Integrations() + { + if (Process.GetCurrentProcess().ParentProcess() is { MainModule.FileVersionInfo.OriginalFilename: "ModOrganizer.exe", MainModule.FileName: { } path }) { - if (Process.GetCurrentProcess().ParentProcess() is { MainModule.FileVersionInfo.OriginalFilename: "ModOrganizer.exe", MainModule.FileName: { } path }) - { - IsModOrganizer2 = true; - ModOrganizer2Path = Path.GetDirectoryName(path); - } + IsModOrganizer2 = true; + ModOrganizer2Path = Path.GetDirectoryName(path); } } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/ModuleChecker.cs b/src/Bannerlord.LauncherEx/Helpers/ModuleChecker.cs index 55b9601..98e8dec 100644 --- a/src/Bannerlord.LauncherEx/Helpers/ModuleChecker.cs +++ b/src/Bannerlord.LauncherEx/Helpers/ModuleChecker.cs @@ -1,5 +1,4 @@ using Bannerlord.BUTR.Shared.Helpers; -using Bannerlord.LauncherManager.Extensions; using Bannerlord.LauncherManager.Models; using Bannerlord.ModuleManager; @@ -7,57 +6,49 @@ using Mono.Cecil.Rocks; using System; -using System.Collections.Generic; using System.IO; using System.Linq; using TaleWorlds.Library; -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +// TODO: NativeAOT won't cover that? +internal static class ModuleChecker { - // TODO: NativeAOT won't cover that? - internal static class ModuleChecker + public static bool IsObfuscated(ModuleInfoExtendedWithMetadata moduleInfoExtended) { - private static readonly HashSet MainModules = ModuleInfoHelper.GetPhysicalModules().Select(x => x.Id).ToHashSet(); - private static readonly HashSet ExternalModules = ModuleInfoHelper.GetPlatformModules().Select(x => x.Id).ToHashSet(); - - public static bool IsInstalledInMainAndExternalModuleDirectory(ModuleInfoExtendedWithPath moduleInfoExtended) => - MainModules.Contains(moduleInfoExtended.Id) && ExternalModules.Contains(moduleInfoExtended.Id); + static bool CanBeLoaded(SubModuleInfoExtended x) => + ModuleInfoHelper.CheckIfSubModuleCanBeLoaded(x, ApplicationPlatform.CurrentPlatform, ApplicationPlatform.CurrentRuntimeLibrary, TaleWorlds.MountAndBlade.DedicatedServerType.None, false); - public static bool IsObfuscated(ModuleInfoExtendedWithPath moduleInfoExtended) + foreach (var subModule in moduleInfoExtended.SubModules.Where(CanBeLoaded)) { - static bool CanBeLoaded(SubModuleInfoExtended x) => - ModuleInfoHelper.CheckIfSubModuleCanBeLoaded(x, ApplicationPlatform.CurrentPlatform, ApplicationPlatform.CurrentRuntimeLibrary, TaleWorlds.MountAndBlade.DedicatedServerType.None, false); + var asm = Path.GetFullPath(Path.Combine(moduleInfoExtended.Path, "bin", "Win64_Shipping_Client", subModule.DLLName)); + if (!File.Exists(asm)) continue; - foreach (var subModule in moduleInfoExtended.SubModules.Where(CanBeLoaded)) + try { - var asm = Path.GetFullPath(Path.Combine(moduleInfoExtended.Path, "bin", "Win64_Shipping_Client", subModule.DLLName)); - if (!File.Exists(asm)) continue; + using var moduleDefinition = ModuleDefinition.ReadModule(asm); - try + var hasObfuscationAttributeUsed = moduleDefinition.GetCustomAttributes().Any(x => x.Constructor.DeclaringType.Name switch { - using var moduleDefinition = ModuleDefinition.ReadModule(asm); - - var hasObfuscationAttributeUsed = moduleDefinition.GetCustomAttributes().Any(x => x.Constructor.DeclaringType.Name switch - { - "ConfusedByAttribute" => true, - _ => false - }); - var hasObfuscationAttributeDeclared = moduleDefinition.Types.Any(x => x.Name switch - { - "ConfusedByAttribute" => true, - _ => false - }); - // Every module should have a module initializer. If it's missing, someone is hiding it - var hasModuleInitializer = moduleDefinition.GetAllTypes().Any(x => x.Name == ""); - - return hasObfuscationAttributeUsed || hasObfuscationAttributeDeclared || !hasModuleInitializer; - } - // Failing to read the metadata is a direct sign of metadata manipulation - catch (Exception) { return true; } - } + "ConfusedByAttribute" => true, + _ => false + }); + var hasObfuscationAttributeDeclared = moduleDefinition.Types.Any(x => x.Name switch + { + "ConfusedByAttribute" => true, + _ => false + }); + // Every module should have a module initializer. If it's missing, someone is hiding it + var hasModuleInitializer = moduleDefinition.GetAllTypes().Any(x => x.Name == ""); - return false; + return hasObfuscationAttributeUsed || hasObfuscationAttributeDeclared || !hasModuleInitializer; + } + // Failing to read the metadata is a direct sign of metadata manipulation + catch (Exception) { return true; } } + + return false; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/TypeWrapper.cs b/src/Bannerlord.LauncherEx/Helpers/TypeWrapper.cs index 9e4f8c3..9e9bf1c 100644 --- a/src/Bannerlord.LauncherEx/Helpers/TypeWrapper.cs +++ b/src/Bannerlord.LauncherEx/Helpers/TypeWrapper.cs @@ -2,50 +2,49 @@ using System.Globalization; using System.Reflection; -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +internal class TypeWrapper : Type { - internal class TypeWrapper : Type - { - public override string Name { get; } = default!; - public override Guid GUID { get; } = default!; - public override Module Module { get; } = default!; - public override Assembly Assembly { get; } = default!; - public override string FullName { get; } = default!; - public override string Namespace { get; } = default!; - public override string AssemblyQualifiedName { get; } = default!; - public override Type BaseType { get; } = default!; - public override Type UnderlyingSystemType { get; } = default!; + public override string Name { get; } = default!; + public override Guid GUID { get; } = default!; + public override Module Module { get; } = default!; + public override Assembly Assembly { get; } = default!; + public override string FullName { get; } = default!; + public override string Namespace { get; } = default!; + public override string AssemblyQualifiedName { get; } = default!; + public override Type BaseType { get; } = default!; + public override Type UnderlyingSystemType { get; } = default!; - public TypeWrapper(string location) => Assembly = new AssemblyWrapper(location); + public TypeWrapper(string location) => Assembly = new AssemblyWrapper(location); - public override object[] GetCustomAttributes(bool inherit) => new object[] { }; - public override bool IsDefined(Type attributeType, bool inherit) => false; - public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => new ConstructorInfo[] { }; - public override Type? GetInterface(string name, bool ignoreCase) => null; - public override Type[] GetInterfaces() => new Type[] { }; - public override EventInfo? GetEvent(string name, BindingFlags bindingAttr) => null; - public override EventInfo[] GetEvents(BindingFlags bindingAttr) => new EventInfo[] { }; - public override Type[] GetNestedTypes(BindingFlags bindingAttr) => new Type[] { }; - public override Type? GetNestedType(string name, BindingFlags bindingAttr) => null; - public override Type? GetElementType() => null; - protected override bool HasElementTypeImpl() => false; - protected override PropertyInfo? GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder, Type? returnType, Type[]? types, ParameterModifier[]? modifiers) => null; - public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => new PropertyInfo[] { }; - protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) => null; - public override MethodInfo[] GetMethods(BindingFlags bindingAttr) => new MethodInfo[] { }; - public override FieldInfo? GetField(string name, BindingFlags bindingAttr) => null; - public override FieldInfo[] GetFields(BindingFlags bindingAttr) => new FieldInfo[] { }; - public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => new MemberInfo[] { }; - protected override TypeAttributes GetAttributeFlagsImpl() => TypeAttributes.NotPublic; - protected override bool IsArrayImpl() => false; - protected override bool IsByRefImpl() => false; - protected override bool IsPointerImpl() => false; - protected override bool IsPrimitiveImpl() => false; - protected override bool IsCOMObjectImpl() => false; - public override object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, object?[]? args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? namedParameters) => null; - protected override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[] types, ParameterModifier[]? modifiers) => null; + public override object[] GetCustomAttributes(bool inherit) => new object[] { }; + public override bool IsDefined(Type attributeType, bool inherit) => false; + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => new ConstructorInfo[] { }; + public override Type? GetInterface(string name, bool ignoreCase) => null; + public override Type[] GetInterfaces() => new Type[] { }; + public override EventInfo? GetEvent(string name, BindingFlags bindingAttr) => null; + public override EventInfo[] GetEvents(BindingFlags bindingAttr) => new EventInfo[] { }; + public override Type[] GetNestedTypes(BindingFlags bindingAttr) => new Type[] { }; + public override Type? GetNestedType(string name, BindingFlags bindingAttr) => null; + public override Type? GetElementType() => null; + protected override bool HasElementTypeImpl() => false; + protected override PropertyInfo? GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder, Type? returnType, Type[]? types, ParameterModifier[]? modifiers) => null; + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => new PropertyInfo[] { }; + protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) => null; + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) => new MethodInfo[] { }; + public override FieldInfo? GetField(string name, BindingFlags bindingAttr) => null; + public override FieldInfo[] GetFields(BindingFlags bindingAttr) => new FieldInfo[] { }; + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => new MemberInfo[] { }; + protected override TypeAttributes GetAttributeFlagsImpl() => TypeAttributes.NotPublic; + protected override bool IsArrayImpl() => false; + protected override bool IsByRefImpl() => false; + protected override bool IsPointerImpl() => false; + protected override bool IsPrimitiveImpl() => false; + protected override bool IsCOMObjectImpl() => false; + public override object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, object?[]? args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? namedParameters) => null; + protected override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[] types, ParameterModifier[]? modifiers) => null; - public override object[] GetCustomAttributes(Type attributeType, bool inherit) => new object[] { }; - } + public override object[] GetCustomAttributes(Type attributeType, bool inherit) => new object[] { }; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/UI/IRef.cs b/src/Bannerlord.LauncherEx/Helpers/UI/IRef.cs index 6954d03..0f1416b 100644 --- a/src/Bannerlord.LauncherEx/Helpers/UI/IRef.cs +++ b/src/Bannerlord.LauncherEx/Helpers/UI/IRef.cs @@ -3,161 +3,160 @@ using System.Reflection; using System.Runtime.CompilerServices; -namespace Bannerlord.LauncherEx.Helpers -{ - /// - /// https://github.com/Aragas/Bannerlord.MBOptionScreen/blob/dev/src/MCM/Abstractions/Ref/IRef.cs - /// - internal interface IRef : INotifyPropertyChanged - { - Type Type { get; } - object? Value { get; set; } - } +namespace Bannerlord.LauncherEx.Helpers; - /// - /// https://github.com/Aragas/Bannerlord.MBOptionScreen/blob/dev/src/MCM/Abstractions/Ref/PropertyRef.cs - /// - internal sealed class PropertyRef : IRef, IEquatable - { - /// - public event PropertyChangedEventHandler? PropertyChanged; +/// +/// https://github.com/Aragas/Bannerlord.MBOptionScreen/blob/dev/src/MCM/Abstractions/Ref/IRef.cs +/// +internal interface IRef : INotifyPropertyChanged +{ + Type Type { get; } + object? Value { get; set; } +} + +/// +/// https://github.com/Aragas/Bannerlord.MBOptionScreen/blob/dev/src/MCM/Abstractions/Ref/PropertyRef.cs +/// +internal sealed class PropertyRef : IRef, IEquatable +{ + /// + public event PropertyChangedEventHandler? PropertyChanged; - public PropertyInfo PropertyInfo { get; } - public object Instance { get; } + public PropertyInfo PropertyInfo { get; } + public object Instance { get; } - /// - public Type Type => PropertyInfo.PropertyType; - /// - public object? Value + /// + public Type Type => PropertyInfo.PropertyType; + /// + public object? Value + { + get => PropertyInfo.GetValue(Instance); + set { - get => PropertyInfo.GetValue(Instance); - set + if (PropertyInfo.CanWrite) { - if (PropertyInfo.CanWrite) - { - PropertyInfo.SetValue(Instance, value); - OnPropertyChanged(); - } + PropertyInfo.SetValue(Instance, value); + OnPropertyChanged(); } } + } - public PropertyRef(PropertyInfo propInfo, object instance) - { - PropertyInfo = propInfo; - Instance = instance; - } + public PropertyRef(PropertyInfo propInfo, object instance) + { + PropertyInfo = propInfo; + Instance = instance; + } - private void OnPropertyChanged([CallerMemberName] string? propertyName = null) => - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + private void OnPropertyChanged([CallerMemberName] string? propertyName = null) => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - public bool Equals(PropertyRef? other) - { - if (other is null) return false; - if (ReferenceEquals(this, other)) return true; - return PropertyInfo.Equals(other.PropertyInfo) && Instance.Equals(other.Instance); - } - /// - public override bool Equals(object? obj) - { - if (obj is null) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((PropertyRef) obj); - } - /// - public override int GetHashCode() => PropertyInfo.GetHashCode() ^ Instance.GetHashCode(); - public static bool operator ==(PropertyRef? left, PropertyRef? right) => Equals(left, right); - public static bool operator !=(PropertyRef? left, PropertyRef? right) => !Equals(left, right); + public bool Equals(PropertyRef? other) + { + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + return PropertyInfo.Equals(other.PropertyInfo) && Instance.Equals(other.Instance); } - - internal sealed class StorageRef : IRef + /// + public override bool Equals(object? obj) { - public event PropertyChangedEventHandler? PropertyChanged; + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((PropertyRef) obj); + } + /// + public override int GetHashCode() => PropertyInfo.GetHashCode() ^ Instance.GetHashCode(); + public static bool operator ==(PropertyRef? left, PropertyRef? right) => Equals(left, right); + public static bool operator !=(PropertyRef? left, PropertyRef? right) => !Equals(left, right); +} + +internal sealed class StorageRef : IRef +{ + public event PropertyChangedEventHandler? PropertyChanged; - public Type Type { get; } + public Type Type { get; } - private T? _value; - public object? Value + private T? _value; + public object? Value + { + get => _value; + set { - get => _value; - set + if (value is T val && !Equals(_value, val)) { - if (value is T val && !Equals(_value, val)) - { - _value = val; - OnPropertyChanged(); - } + _value = val; + OnPropertyChanged(); } } - - public StorageRef(T? value) - { - _value = value; - Type = value?.GetType() ?? typeof(T); - } - - private void OnPropertyChanged([CallerMemberName] string? propertyName = null) => - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - internal class ProxyRef : IRef, IEquatable> + public StorageRef(T? value) { - /// - public event PropertyChangedEventHandler? PropertyChanged; + _value = value; + Type = value?.GetType() ?? typeof(T); + } + + private void OnPropertyChanged([CallerMemberName] string? propertyName = null) => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); +} + +internal class ProxyRef : IRef, IEquatable> +{ + /// + public event PropertyChangedEventHandler? PropertyChanged; - private readonly Func _getter; - private readonly Action? _setter; + private readonly Func _getter; + private readonly Action? _setter; - /// - public Type Type => typeof(T); - /// - public object? Value + /// + public Type Type => typeof(T); + /// + public object? Value + { + get => _getter(); + set { - get => _getter(); - set + if (_setter is not null && value is T val) { - if (_setter is not null && value is T val) - { - _setter(val); - OnPropertyChanged(); - } + _setter(val); + OnPropertyChanged(); } } + } - public ProxyRef(Func getter, Action? setter) - { - _getter = getter ?? throw new ArgumentNullException(nameof(getter)); - _setter = setter; - } + public ProxyRef(Func getter, Action? setter) + { + _getter = getter ?? throw new ArgumentNullException(nameof(getter)); + _setter = setter; + } - protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) => - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - /// - public bool Equals(ProxyRef? other) - { - if (other is null) return false; - if (ReferenceEquals(this, other)) return true; - return _getter.Equals(other._getter) && Equals(_setter, other._setter); - } - /// - public override bool Equals(object? obj) - { - if (obj is null) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((ProxyRef) obj); - } - /// - public override int GetHashCode() - { - var hash = 269; - hash = (hash * 47) + _getter.GetHashCode(); - if (_setter is not null) - hash = (hash * 47) + _setter.GetHashCode(); - return hash; - } - public static bool operator ==(ProxyRef? left, ProxyRef? right) => Equals(left, right); - public static bool operator !=(ProxyRef? left, ProxyRef? right) => !Equals(left, right); + /// + public bool Equals(ProxyRef? other) + { + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + return _getter.Equals(other._getter) && Equals(_setter, other._setter); + } + /// + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((ProxyRef) obj); + } + /// + public override int GetHashCode() + { + var hash = 269; + hash = (hash * 47) + _getter.GetHashCode(); + if (_setter is not null) + hash = (hash * 47) + _setter.GetHashCode(); + return hash; } + public static bool operator ==(ProxyRef? left, ProxyRef? right) => Equals(left, right); + public static bool operator !=(ProxyRef? left, ProxyRef? right) => !Equals(left, right); } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/UI/MixinManager.cs b/src/Bannerlord.LauncherEx/Helpers/UI/MixinManager.cs index 5146602..b5ebdc9 100644 --- a/src/Bannerlord.LauncherEx/Helpers/UI/MixinManager.cs +++ b/src/Bannerlord.LauncherEx/Helpers/UI/MixinManager.cs @@ -13,165 +13,164 @@ using TaleWorlds.Library; using TaleWorlds.MountAndBlade.Launcher.Library; -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] +internal class BUTRDataSourcePropertyAttribute : Attribute { - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] - internal class BUTRDataSourcePropertyAttribute : Attribute - { - public string? OverrideName { get; set; } - } - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - internal class BUTRDataSourceMethodAttribute : Attribute - { - public string? OverrideName { get; set; } - } + public string? OverrideName { get; set; } +} +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] +internal class BUTRDataSourceMethodAttribute : Attribute +{ + public string? OverrideName { get; set; } +} - internal abstract class BUTRViewModel : ViewModel +internal abstract class BUTRViewModel : ViewModel +{ + protected BUTRViewModel() { - protected BUTRViewModel() + var properties = GetType().GetProperties(AccessTools.all); + foreach (var propertyInfo in properties) { - var properties = GetType().GetProperties(AccessTools.all); - foreach (var propertyInfo in properties) + if (propertyInfo.GetCustomAttribute() is { } attribute) { - if (propertyInfo.GetCustomAttribute() is { } attribute) - { - if (propertyInfo.GetMethod?.IsPrivate == true || propertyInfo.SetMethod?.IsPrivate == true) throw new Exception(); + if (propertyInfo.GetMethod?.IsPrivate == true || propertyInfo.SetMethod?.IsPrivate == true) throw new Exception(); - this.AddProperty(attribute.OverrideName ?? propertyInfo.Name, propertyInfo); - } + this.AddProperty(attribute.OverrideName ?? propertyInfo.Name, propertyInfo); } + } - var methods = GetType().GetMethods(AccessTools.all); - foreach (var methodInfo in methods) + var methods = GetType().GetMethods(AccessTools.all); + foreach (var methodInfo in methods) + { + if (methodInfo.GetCustomAttribute() is { } attribute) { - if (methodInfo.GetCustomAttribute() is { } attribute) - { - if (methodInfo.IsPrivate) throw new Exception(); + if (methodInfo.IsPrivate) throw new Exception(); - this.AddMethod(attribute.OverrideName ?? methodInfo.Name, methodInfo); - } + this.AddMethod(attribute.OverrideName ?? methodInfo.Name, methodInfo); } } + } - protected new bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null) + protected new bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) { - if (EqualityComparer.Default.Equals(field, value)) - { - return false; - } - field = value; - OnPropertyChanged(propertyName); - return true; + return false; } + field = value; + OnPropertyChanged(propertyName); + return true; } +} - internal abstract class ViewModelMixin - where TViewModelMixin : ViewModelMixin - where TViewModel : ViewModel - { - private readonly WeakReference _vm; +internal abstract class ViewModelMixin + where TViewModelMixin : ViewModelMixin + where TViewModel : ViewModel +{ + private readonly WeakReference _vm; - protected TViewModel? ViewModel => _vm.TryGetTarget(out var vm) ? vm : null; + protected TViewModel? ViewModel => _vm.TryGetTarget(out var vm) ? vm : null; - public TViewModelMixin Mixin => (TViewModelMixin) this; + public TViewModelMixin Mixin => (TViewModelMixin) this; - protected ViewModelMixin(TViewModel vm) - { - _vm = new WeakReference(vm); + protected ViewModelMixin(TViewModel vm) + { + _vm = new WeakReference(vm); - SetVMProperty(nameof(Mixin), GetType().Name); - foreach (var propertyInfo in GetType().GetProperties(AccessTools.all)) + SetVMProperty(nameof(Mixin), GetType().Name); + foreach (var propertyInfo in GetType().GetProperties(AccessTools.all)) + { + if (propertyInfo.GetCustomAttribute() is { } attribute) { - if (propertyInfo.GetCustomAttribute() is { } attribute) - { - if (propertyInfo.GetMethod?.IsPrivate == true || propertyInfo.SetMethod?.IsPrivate == true) throw new Exception(); - - var wrappedPropertyInfo = new WrappedPropertyInfo(propertyInfo, this); - vm.AddProperty(attribute.OverrideName ?? propertyInfo.Name, wrappedPropertyInfo); - wrappedPropertyInfo.PropertyChanged += (_, e) => ViewModel?.OnPropertyChanged(e.PropertyName); - } + if (propertyInfo.GetMethod?.IsPrivate == true || propertyInfo.SetMethod?.IsPrivate == true) throw new Exception(); + + var wrappedPropertyInfo = new WrappedPropertyInfo(propertyInfo, this); + vm.AddProperty(attribute.OverrideName ?? propertyInfo.Name, wrappedPropertyInfo); + wrappedPropertyInfo.PropertyChanged += (_, e) => ViewModel?.OnPropertyChanged(e.PropertyName); } - foreach (var methodInfo in GetType().GetMethods(AccessTools.all)) + } + foreach (var methodInfo in GetType().GetMethods(AccessTools.all)) + { + if (methodInfo.GetCustomAttribute() is { } attribute) { - if (methodInfo.GetCustomAttribute() is { } attribute) - { - if (methodInfo.IsPrivate) throw new Exception(); + if (methodInfo.IsPrivate) throw new Exception(); - var wrappedMethodInfo = new WrappedMethodInfo(methodInfo, this); - vm.AddMethod(attribute.OverrideName ?? methodInfo.Name, wrappedMethodInfo); - } + var wrappedMethodInfo = new WrappedMethodInfo(methodInfo, this); + vm.AddMethod(attribute.OverrideName ?? methodInfo.Name, wrappedMethodInfo); } } + } - protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) - { - ViewModel?.OnPropertyChanged(propertyName); - } + protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) + { + ViewModel?.OnPropertyChanged(propertyName); + } - protected void OnPropertyChangedWithValue(T value, [CallerMemberName] string? propertyName = null) where T : class - { + protected void OnPropertyChangedWithValue(T value, [CallerMemberName] string? propertyName = null) where T : class + { #if v100 || v101 || v102 || v103 ViewModel?.OnPropertyChangedWithValue((object) value, propertyName); #elif v110 || v111 ViewModel?.OnPropertyChangedWithValue(value, propertyName); #endif - } - - protected bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null) - { - if (EqualityComparer.Default.Equals(field, value)) - { - return false; - } - field = value; - OnPropertyChanged(propertyName); - return true; - } + } - protected void SetVMProperty(string property, string? overrideName = null) + protected bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) { - var propertyInfo = new WrappedPropertyInfo(AccessTools2.Property(GetType(), property)!, this); - ViewModel?.AddProperty(overrideName ?? property, propertyInfo); - propertyInfo.PropertyChanged += (_, e) => ViewModel?.OnPropertyChanged(e.PropertyName); + return false; } + field = value; + OnPropertyChanged(propertyName); + return true; + } - protected void SetVMMethod(string method, string? overrideName = null) - { - var methodInfo = new WrappedMethodInfo(AccessTools2.Method(GetType(), method)!, this); - ViewModel?.AddMethod(overrideName ?? method, methodInfo); - } + protected void SetVMProperty(string property, string? overrideName = null) + { + var propertyInfo = new WrappedPropertyInfo(AccessTools2.Property(GetType(), property)!, this); + ViewModel?.AddProperty(overrideName ?? property, propertyInfo); + propertyInfo.PropertyChanged += (_, e) => ViewModel?.OnPropertyChanged(e.PropertyName); } - internal static class MixinManager + protected void SetVMMethod(string method, string? overrideName = null) { - public static readonly Dictionary> Mixins = new(); + var methodInfo = new WrappedMethodInfo(AccessTools2.Method(GetType(), method)!, this); + ViewModel?.AddMethod(overrideName ?? method, methodInfo); + } +} - private static void AddMixin(ViewModel viewModel, object mixin) - { - if (Mixins.TryGetValue(viewModel, out var list)) - { - list.Add(mixin); - } - else - { - Mixins.Add(viewModel, new List { mixin }); - } - } +internal static class MixinManager +{ + public static readonly Dictionary> Mixins = new(); - public static LauncherVM AddMixins(LauncherVM launcherVM) + private static void AddMixin(ViewModel viewModel, object mixin) + { + if (Mixins.TryGetValue(viewModel, out var list)) { - AddMixin(launcherVM.News, new LauncherNewsVMMixin(launcherVM.News)); - AddMixin(launcherVM.ModsData, new LauncherModsVMMixin(launcherVM.ModsData)); - AddMixin(launcherVM.ConfirmStart, new LauncherConfirmStartVMMixin(launcherVM.ConfirmStart)); - AddMixin(launcherVM, new LauncherVMMixin(launcherVM)); - return launcherVM; + list.Add(mixin); } - - public static LauncherConfirmStartVM AddMixin(LauncherConfirmStartVM confirmStartVM) + else { - AddMixin(confirmStartVM, new LauncherConfirmStartVMMixin(confirmStartVM)); - return confirmStartVM; + Mixins.Add(viewModel, new List { mixin }); } } + + public static LauncherVM AddMixins(LauncherVM launcherVM) + { + AddMixin(launcherVM.News, new LauncherNewsVMMixin(launcherVM.News)); + AddMixin(launcherVM.ModsData, new LauncherModsVMMixin(launcherVM.ModsData)); + AddMixin(launcherVM.ConfirmStart, new LauncherConfirmStartVMMixin(launcherVM.ConfirmStart)); + AddMixin(launcherVM, new LauncherVMMixin(launcherVM)); + return launcherVM; + } + + public static LauncherConfirmStartVM AddMixin(LauncherConfirmStartVM confirmStartVM) + { + AddMixin(confirmStartVM, new LauncherConfirmStartVMMixin(confirmStartVM)); + return confirmStartVM; + } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/UI/PrefabExtensionManager.cs b/src/Bannerlord.LauncherEx/Helpers/UI/PrefabExtensionManager.cs index 6743efe..751cc2d 100644 --- a/src/Bannerlord.LauncherEx/Helpers/UI/PrefabExtensionManager.cs +++ b/src/Bannerlord.LauncherEx/Helpers/UI/PrefabExtensionManager.cs @@ -3,227 +3,226 @@ using System.Collections.Generic; using System.Xml; -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +/// +/// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Prefabs/IPrefabPatch.cs +/// +internal interface IPrefabPatch { } + +/// +/// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Prefabs/PrefabExtensionSetAttributePatch.cs +/// +internal abstract class PrefabExtensionSetAttributePatch : IPrefabPatch { - /// - /// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Prefabs/IPrefabPatch.cs - /// - internal interface IPrefabPatch { } - - /// - /// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Prefabs/PrefabExtensionSetAttributePatch.cs - /// - internal abstract class PrefabExtensionSetAttributePatch : IPrefabPatch - { - public abstract string Attribute { get; } - public abstract string Value { get; } - } - internal abstract class PrefabExtensionSetAttributesPatch : IPrefabPatch - { - public abstract List Attributes { get; } + public abstract string Attribute { get; } + public abstract string Value { get; } +} +internal abstract class PrefabExtensionSetAttributesPatch : IPrefabPatch +{ + public abstract List Attributes { get; } - public record struct Attribute(string Name, string Value); - } + public record struct Attribute(string Name, string Value); +} - /// - /// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Prefabs/PrefabExtensionInsertAsSiblingPatch.cs - /// - internal abstract class PrefabExtensionInsertAsSiblingPatch : IPrefabPatch - { - public enum InsertType { Prepend, Append } +/// +/// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Prefabs/PrefabExtensionInsertAsSiblingPatch.cs +/// +internal abstract class PrefabExtensionInsertAsSiblingPatch : IPrefabPatch +{ + public enum InsertType { Prepend, Append } - public virtual InsertType Type => InsertType.Append; + public virtual InsertType Type => InsertType.Append; - public abstract XmlDocument GetPrefabExtension(); - } + public abstract XmlDocument GetPrefabExtension(); +} - /// - /// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Prefabs/PrefabExtensionReplacePatch.cs - /// - internal abstract class PrefabExtensionReplacePatch : IPrefabPatch - { - public abstract XmlDocument GetPrefabExtension(); - } +/// +/// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Prefabs/PrefabExtensionReplacePatch.cs +/// +internal abstract class PrefabExtensionReplacePatch : IPrefabPatch +{ + public abstract XmlDocument GetPrefabExtension(); +} + +/// +/// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Prefabs/CustomPatch.cs +/// +internal abstract class PrefabExtensionCustomPatch : IPrefabPatch where T : XmlNode +{ + public abstract void Apply(T obj); +} + +/// +/// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Components/PrefabComponent.cs +/// +internal static class PrefabExtensionManager +{ + private static readonly ConcurrentDictionary>> MoviePatches = new(); - /// - /// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Prefabs/CustomPatch.cs - /// - internal abstract class PrefabExtensionCustomPatch : IPrefabPatch where T : XmlNode + public static void RegisterPatch(string movie, Action patcher) { - public abstract void Apply(T obj); + if (string.IsNullOrEmpty(movie)) + { + return; + } + + MoviePatches.GetOrAdd(movie, _ => new List>()).Add(patcher); } - /// - /// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Components/PrefabComponent.cs - /// - internal static class PrefabExtensionManager + public static void RegisterPatch(string movie, string? xpath, Action patcher) { - private static readonly ConcurrentDictionary>> MoviePatches = new(); - - public static void RegisterPatch(string movie, Action patcher) + RegisterPatch(movie, document => { - if (string.IsNullOrEmpty(movie)) + var node = document.SelectSingleNode(xpath ?? string.Empty); + if (node is null) { return; } - MoviePatches.GetOrAdd(movie, _ => new List>()).Add(patcher); - } + patcher(node); + }); + } - public static void RegisterPatch(string movie, string? xpath, Action patcher) + public static void RegisterPatch(string movie, string? xpath, PrefabExtensionCustomPatch patcher) + { + RegisterPatch(movie, document => { - RegisterPatch(movie, document => + var node = document.SelectSingleNode(xpath ?? string.Empty); + if (node is null) { - var node = document.SelectSingleNode(xpath ?? string.Empty); - if (node is null) - { - return; - } + return; + } - patcher(node); - }); - } + patcher.Apply(node); + }); + } - public static void RegisterPatch(string movie, string? xpath, PrefabExtensionCustomPatch patcher) + public static void RegisterPatch(string movie, string? xpath, PrefabExtensionSetAttributePatch patch) + { + RegisterPatch(movie, xpath, node => { - RegisterPatch(movie, document => + var ownerDocument = node as XmlDocument ?? node.OwnerDocument; + if (ownerDocument is null) { - var node = document.SelectSingleNode(xpath ?? string.Empty); - if (node is null) - { - return; - } - - patcher.Apply(node); - }); - } + return; + } - public static void RegisterPatch(string movie, string? xpath, PrefabExtensionSetAttributePatch patch) - { - RegisterPatch(movie, xpath, node => + if (node.NodeType != XmlNodeType.Element) { - var ownerDocument = node as XmlDocument ?? node.OwnerDocument; - if (ownerDocument is null) - { - return; - } - - if (node.NodeType != XmlNodeType.Element) - { - return; - } + return; + } - if (node.Attributes![patch.Attribute] is null) - { - var attribute = ownerDocument.CreateAttribute(patch.Attribute); - node.Attributes.Append(attribute); - } + if (node.Attributes![patch.Attribute] is null) + { + var attribute = ownerDocument.CreateAttribute(patch.Attribute); + node.Attributes.Append(attribute); + } - node.Attributes![patch.Attribute].Value = patch.Value; - }); - } + node.Attributes![patch.Attribute].Value = patch.Value; + }); + } - public static void RegisterPatch(string movie, string? xpath, PrefabExtensionSetAttributesPatch patch) + public static void RegisterPatch(string movie, string? xpath, PrefabExtensionSetAttributesPatch patch) + { + RegisterPatch(movie, xpath, node => { - RegisterPatch(movie, xpath, node => + var ownerDocument = node as XmlDocument ?? node.OwnerDocument; + if (ownerDocument is null) { - var ownerDocument = node as XmlDocument ?? node.OwnerDocument; - if (ownerDocument is null) - { - return; - } + return; + } - if (node.NodeType != XmlNodeType.Element) - { - return; - } + if (node.NodeType != XmlNodeType.Element) + { + return; + } - foreach (var (attribute, value) in patch.Attributes) + foreach (var (attribute, value) in patch.Attributes) + { + if (node.Attributes![attribute] is null) { - if (node.Attributes![attribute] is null) - { - var attr = ownerDocument.CreateAttribute(attribute); - node.Attributes.Append(attr); - } - - node.Attributes![attribute].Value = value; + var attr = ownerDocument.CreateAttribute(attribute); + node.Attributes.Append(attr); } - }); - } - public static void RegisterPatch(string movie, string? xpath, PrefabExtensionReplacePatch patch) + node.Attributes![attribute].Value = value; + } + }); + } + + public static void RegisterPatch(string movie, string? xpath, PrefabExtensionReplacePatch patch) + { + RegisterPatch(movie, xpath, node => { - RegisterPatch(movie, xpath, node => + var ownerDocument = node as XmlDocument ?? node.OwnerDocument; + if (ownerDocument is null) { - var ownerDocument = node as XmlDocument ?? node.OwnerDocument; - if (ownerDocument is null) - { - return; - } + return; + } - if (node.ParentNode is null) - { - return; - } + if (node.ParentNode is null) + { + return; + } - var extensionNode = patch.GetPrefabExtension().DocumentElement; - if (extensionNode is null) - { - return; - } + var extensionNode = patch.GetPrefabExtension().DocumentElement; + if (extensionNode is null) + { + return; + } - var importedExtensionNode = ownerDocument.ImportNode(extensionNode, true); + var importedExtensionNode = ownerDocument.ImportNode(extensionNode, true); - node.ParentNode.ReplaceChild(importedExtensionNode, node); - }); - } + node.ParentNode.ReplaceChild(importedExtensionNode, node); + }); + } - public static void RegisterPatch(string movie, string? xpath, PrefabExtensionInsertAsSiblingPatch patch) + public static void RegisterPatch(string movie, string? xpath, PrefabExtensionInsertAsSiblingPatch patch) + { + RegisterPatch(movie, xpath, node => { - RegisterPatch(movie, xpath, node => + var ownerDocument = node as XmlDocument ?? node.OwnerDocument; + if (ownerDocument is null) { - var ownerDocument = node as XmlDocument ?? node.OwnerDocument; - if (ownerDocument is null) - { - return; - } + return; + } - if (node.ParentNode is null) - { - return; - } + if (node.ParentNode is null) + { + return; + } - var extensionNode = patch.GetPrefabExtension().DocumentElement; - if (extensionNode is null) - { - return; - } + var extensionNode = patch.GetPrefabExtension().DocumentElement; + if (extensionNode is null) + { + return; + } - var importedExtensionNode = ownerDocument.ImportNode(extensionNode, true); + var importedExtensionNode = ownerDocument.ImportNode(extensionNode, true); - switch (patch.Type) - { - case PrefabExtensionInsertAsSiblingPatch.InsertType.Append: - node.ParentNode.InsertAfter(importedExtensionNode, node); - break; + switch (patch.Type) + { + case PrefabExtensionInsertAsSiblingPatch.InsertType.Append: + node.ParentNode.InsertAfter(importedExtensionNode, node); + break; - case PrefabExtensionInsertAsSiblingPatch.InsertType.Prepend: - node.ParentNode.InsertBefore(importedExtensionNode, node); - break; - } - }); - } + case PrefabExtensionInsertAsSiblingPatch.InsertType.Prepend: + node.ParentNode.InsertBefore(importedExtensionNode, node); + break; + } + }); + } - public static void ProcessMovieIfNeeded(string movie, XmlDocument document) - { - if (!MoviePatches.TryGetValue(movie, out var patches)) - return; + public static void ProcessMovieIfNeeded(string movie, XmlDocument document) + { + if (!MoviePatches.TryGetValue(movie, out var patches)) + return; - foreach (var patch in patches) - { - patch(document); - } + foreach (var patch in patches) + { + patch(document); } } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Helpers/UI/Settings.cs b/src/Bannerlord.LauncherEx/Helpers/UI/Settings.cs index b45c9bc..29a5130 100644 --- a/src/Bannerlord.LauncherEx/Helpers/UI/Settings.cs +++ b/src/Bannerlord.LauncherEx/Helpers/UI/Settings.cs @@ -1,55 +1,54 @@ using System.Diagnostics.CodeAnalysis; -namespace Bannerlord.LauncherEx.Helpers +namespace Bannerlord.LauncherEx.Helpers; + +/// +/// https://github.com/Aragas/Bannerlord.MBOptionScreen/blob/dev/src/MCM/Abstractions/Settings/SettingType.cs +/// +internal enum SettingType { - /// - /// https://github.com/Aragas/Bannerlord.MBOptionScreen/blob/dev/src/MCM/Abstractions/Settings/SettingType.cs - /// - internal enum SettingType - { - [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "For ReSharper")] - [SuppressMessage("ReSharper", "InconsistentNaming")] - NONE = -1, - Bool, - Int, - Float, - String, - Button, - } + [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "For ReSharper")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + NONE = -1, + Bool, + Int, + Float, + String, + Button, +} - /// - /// https://github.com/Aragas/Bannerlord.MBOptionScreen/blob/dev/src/MCM/Abstractions/Settings/Models/ISettingsPropertyDefinition.cs - /// - internal interface ISettingsPropertyDefinition - { - IRef PropertyReference { get; } +/// +/// https://github.com/Aragas/Bannerlord.MBOptionScreen/blob/dev/src/MCM/Abstractions/Settings/Models/ISettingsPropertyDefinition.cs +/// +internal interface ISettingsPropertyDefinition +{ + IRef PropertyReference { get; } - SettingType SettingType { get; } + SettingType SettingType { get; } - string DisplayName { get; } - string HintText { get; } - decimal MinValue { get; } - decimal MaxValue { get; } - string Content { get; } - } + string DisplayName { get; } + string HintText { get; } + decimal MinValue { get; } + decimal MaxValue { get; } + string Content { get; } +} - /// - /// https://github.com/Aragas/Bannerlord.MBOptionScreen/blob/dev/src/MCM/Abstractions/Settings/Models/SettingsPropertyDefinition.cs - /// - internal class SettingsPropertyDefinition : ISettingsPropertyDefinition - { - public string DisplayName { get; init; } = string.Empty; - public string HintText { get; init; } = string.Empty; - public IRef PropertyReference { get; init; } = default!; - public SettingType SettingType { get; init; } = default!; +/// +/// https://github.com/Aragas/Bannerlord.MBOptionScreen/blob/dev/src/MCM/Abstractions/Settings/Models/SettingsPropertyDefinition.cs +/// +internal class SettingsPropertyDefinition : ISettingsPropertyDefinition +{ + public string DisplayName { get; init; } = string.Empty; + public string HintText { get; init; } = string.Empty; + public IRef PropertyReference { get; init; } = default!; + public SettingType SettingType { get; init; } = default!; - public decimal MinValue { get; init; } = default!; - public decimal MaxValue { get; init; } = default!; - public string Content { get; init; } = default!; - } - internal class ConfigSettingsPropertyDefinition : SettingsPropertyDefinition - { - public string ConfigKey { get; init; } = string.Empty; - public string OriginalValue { get; init; } = string.Empty; - } + public decimal MinValue { get; init; } = default!; + public decimal MaxValue { get; init; } = default!; + public string Content { get; init; } = default!; +} +internal class ConfigSettingsPropertyDefinition : SettingsPropertyDefinition +{ + public string ConfigKey { get; init; } = string.Empty; + public string OriginalValue { get; init; } = string.Empty; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/LauncherSettings.cs b/src/Bannerlord.LauncherEx/LauncherSettings.cs index ec8a6bd..3afbb24 100644 --- a/src/Bannerlord.LauncherEx/LauncherSettings.cs +++ b/src/Bannerlord.LauncherEx/LauncherSettings.cs @@ -1,21 +1,20 @@ using System.Diagnostics.CodeAnalysis; -namespace Bannerlord.LauncherEx +namespace Bannerlord.LauncherEx; + +[SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "For ReSharper")] +[SuppressMessage("ReSharper", "UnusedMember.Global")] +public static class LauncherSettings { - [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "For ReSharper")] - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public static class LauncherSettings - { - public static bool AutomaticallyCheckForUpdates { get; set; } = false; - public static bool FixCommonIssues { get; set; } = false; - public static bool CompactModuleList { get; set; } = false; - public static bool DisableBinaryCheck { get; set; } = false; - public static bool HideRandomImage { get; set; } = false; - public static bool BetaSorting { get; set; } = false; - public static bool BigMode { get; set; } = true; - public static bool EnableDPIScaling { get; set; } = true; - public static bool DisableCrashHandlerWhenDebuggerIsAttached { get; set; } = false; - public static bool DisableCatchAutoGenExceptions { get; set; } = true; - public static bool UseVanillaCrashHandler { get; set; } = false; - } + public static bool AutomaticallyCheckForUpdates { get; set; } = false; + public static bool FixCommonIssues { get; set; } = false; + public static bool CompactModuleList { get; set; } = false; + public static bool DisableBinaryCheck { get; set; } = false; + public static bool HideRandomImage { get; set; } = false; + public static bool BetaSorting { get; set; } = false; + public static bool BigMode { get; set; } = true; + public static bool EnableDPIScaling { get; set; } = true; + public static bool DisableCrashHandlerWhenDebuggerIsAttached { get; set; } = false; + public static bool DisableCatchAutoGenExceptions { get; set; } = true; + public static bool UseVanillaCrashHandler { get; set; } = false; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Manager.cs b/src/Bannerlord.LauncherEx/Manager.cs index 2e0957b..d2f11a9 100644 --- a/src/Bannerlord.LauncherEx/Manager.cs +++ b/src/Bannerlord.LauncherEx/Manager.cs @@ -20,134 +20,133 @@ [assembly: InternalsVisibleTo("Bannerlord.LauncherEx.Tests")] // ReSharper disable once CheckNamespace -namespace Bannerlord.LauncherEx +namespace Bannerlord.LauncherEx; + +public static class Manager { - public static class Manager - { - //internal static readonly AssemblyCompatibilityChecker _compatibilityChecker = new(); - private static readonly Harmony _launcherHarmony = new("Bannerlord.LauncherEx"); + //internal static readonly AssemblyCompatibilityChecker _compatibilityChecker = new(); + private static readonly Harmony _launcherHarmony = new("Bannerlord.LauncherEx"); - public static event Action? OnDisable; + public static event Action? OnDisable; - public static string GetActiveLanguage() => ConfigReader.GetGameOptions(path => File.Exists(path) ? File.ReadAllBytes(path) : null).TryGetValue("Language", out var lang) ? lang : "English"; + public static string GetActiveLanguage() => ConfigReader.GetGameOptions(path => File.Exists(path) ? File.ReadAllBytes(path) : null).TryGetValue("Language", out var lang) ? lang : "English"; - public static void Initialize() - { - //AssemblyLoaderPatch.Enable(_launcherHarmony); - } + public static void Initialize() + { + //AssemblyLoaderPatch.Enable(_launcherHarmony); + } - public static void Enable() + public static void Enable() + { + ProgramPatch.Enable(_launcherHarmony); + UserDataManagerPatch.Enable(_launcherHarmony); + LauncherVMPatch.Enable(_launcherHarmony); + LauncherModsVMPatch.Enable(_launcherHarmony); + LauncherConfirmStartVMPatch.Enable(_launcherHarmony); + LauncherUIPatch.Enable(_launcherHarmony); + ViewModelPatch.Enable(_launcherHarmony); + WidgetPrefabPatch.Enable(_launcherHarmony); + + foreach (var language in typeof(Manager).Assembly.GetManifestResourceNames().Where(x => x.StartsWith("Bannerlord.LauncherEx.Resources.Localization") && x.EndsWith("strings.xml"))) + BUTRLocalizationManager.LoadLanguage(Load(language)); + BUTRLocalizationManager.ActiveLanguage = GetActiveLanguage(); + + GraphicsContextManager.Enable(_launcherHarmony); + GraphicsContextManager.CreateAndRegister("launcher_arrow_down", LoadStream("Bannerlord.LauncherEx.Resources.Textures.arrow_down.png")); + GraphicsContextManager.CreateAndRegister("launcher_arrow_left", LoadStream("Bannerlord.LauncherEx.Resources.Textures.arrow_left.png")); + GraphicsContextManager.CreateAndRegister("launcher_export", LoadStream("Bannerlord.LauncherEx.Resources.Textures.export.png")); + GraphicsContextManager.CreateAndRegister("launcher_import", LoadStream("Bannerlord.LauncherEx.Resources.Textures.import.png")); + GraphicsContextManager.CreateAndRegister("launcher_refresh", LoadStream("Bannerlord.LauncherEx.Resources.Textures.refresh.png")); + GraphicsContextManager.CreateAndRegister("launcher_folder", LoadStream("Bannerlord.LauncherEx.Resources.Textures.folder.png")); + GraphicsContextManager.CreateAndRegister("launcher_search", LoadStream("Bannerlord.LauncherEx.Resources.Textures.search.png")); + GraphicsContextManager.CreateAndRegister("warm_overlay", LoadStream("Bannerlord.LauncherEx.Resources.Textures.warm_overlay.png")); + + SpriteDataManager.Enable(_launcherHarmony); + SpriteDataManager.CreateAndRegister("launcher_arrow_down"); + SpriteDataManager.CreateAndRegister("launcher_arrow_left"); + SpriteDataManager.CreateAndRegister("launcher_import"); + SpriteDataManager.CreateAndRegister("launcher_export"); + SpriteDataManager.CreateAndRegister("launcher_refresh"); + SpriteDataManager.CreateAndRegister("launcher_folder"); + SpriteDataManager.CreateAndRegister("launcher_search"); + SpriteDataManager.CreateAndRegister("warm_overlay"); + + var asset = new AssetPackage(Path.Combine(BasePath.Name, ModuleInfoHelper.ModulesFolder, "Native/AssetPackages/gauntlet_ui.tpac")); + switch (BUTRLocalizationManager.ActiveLanguage) { - ProgramPatch.Enable(_launcherHarmony); - UserDataManagerPatch.Enable(_launcherHarmony); - LauncherVMPatch.Enable(_launcherHarmony); - LauncherModsVMPatch.Enable(_launcherHarmony); - LauncherConfirmStartVMPatch.Enable(_launcherHarmony); - LauncherUIPatch.Enable(_launcherHarmony); - ViewModelPatch.Enable(_launcherHarmony); - WidgetPrefabPatch.Enable(_launcherHarmony); - - foreach (var language in typeof(Manager).Assembly.GetManifestResourceNames().Where(x => x.StartsWith("Bannerlord.LauncherEx.Resources.Localization") && x.EndsWith("strings.xml"))) - BUTRLocalizationManager.LoadLanguage(Load(language)); - BUTRLocalizationManager.ActiveLanguage = GetActiveLanguage(); - - GraphicsContextManager.Enable(_launcherHarmony); - GraphicsContextManager.CreateAndRegister("launcher_arrow_down", LoadStream("Bannerlord.LauncherEx.Resources.Textures.arrow_down.png")); - GraphicsContextManager.CreateAndRegister("launcher_arrow_left", LoadStream("Bannerlord.LauncherEx.Resources.Textures.arrow_left.png")); - GraphicsContextManager.CreateAndRegister("launcher_export", LoadStream("Bannerlord.LauncherEx.Resources.Textures.export.png")); - GraphicsContextManager.CreateAndRegister("launcher_import", LoadStream("Bannerlord.LauncherEx.Resources.Textures.import.png")); - GraphicsContextManager.CreateAndRegister("launcher_refresh", LoadStream("Bannerlord.LauncherEx.Resources.Textures.refresh.png")); - GraphicsContextManager.CreateAndRegister("launcher_folder", LoadStream("Bannerlord.LauncherEx.Resources.Textures.folder.png")); - GraphicsContextManager.CreateAndRegister("launcher_search", LoadStream("Bannerlord.LauncherEx.Resources.Textures.search.png")); - GraphicsContextManager.CreateAndRegister("warm_overlay", LoadStream("Bannerlord.LauncherEx.Resources.Textures.warm_overlay.png")); - - SpriteDataManager.Enable(_launcherHarmony); - SpriteDataManager.CreateAndRegister("launcher_arrow_down"); - SpriteDataManager.CreateAndRegister("launcher_arrow_left"); - SpriteDataManager.CreateAndRegister("launcher_import"); - SpriteDataManager.CreateAndRegister("launcher_export"); - SpriteDataManager.CreateAndRegister("launcher_refresh"); - SpriteDataManager.CreateAndRegister("launcher_folder"); - SpriteDataManager.CreateAndRegister("launcher_search"); - SpriteDataManager.CreateAndRegister("warm_overlay"); - - var asset = new AssetPackage(Path.Combine(BasePath.Name, ModuleInfoHelper.ModulesFolder, "Native/AssetPackages/gauntlet_ui.tpac")); - switch (BUTRLocalizationManager.ActiveLanguage) - { - case BUTRLocalizationManager.ChineseTraditional or BUTRLocalizationManager.ChineseSimple when asset.GetTexture("ui_fonts_1") is { } chinese: - GraphicsContextManager.CreateAssetTextureAndRegister("simkai", chinese); - SpriteDataManager.CreateGenericAndRegister("simkai"); - break; - case BUTRLocalizationManager.Japanese when asset.GetTexture("ui_fonts_2") is { } japanese: - GraphicsContextManager.CreateAssetTextureAndRegister("SourceHanSansJP", japanese); - SpriteDataManager.CreateGenericAndRegister("SourceHanSansJP"); - break; - case BUTRLocalizationManager.Korean when asset.GetTexture("ui_fonts_4") is { } korean: - GraphicsContextManager.CreateAssetTextureAndRegister("NanumGothicKR", korean); - SpriteDataManager.CreateGenericAndRegister("NanumGothicKR"); - break; - } - - - BrushFactoryManager.Enable(_launcherHarmony); - BrushFactoryManager.CreateAndRegister(Load("Bannerlord.LauncherEx.Resources.Brushes.Launcher.xml")); - - WidgetFactoryManager.Enable(_launcherHarmony); - WidgetFactoryManager.Register(typeof(LauncherToggleButtonWidget)); - WidgetFactoryManager.Register(typeof(LauncherSearchBoxWidget)); - - WidgetFactoryManager.CreateAndRegister("Launcher.ToggleButton", Load("Bannerlord.LauncherEx.Resources.Prefabs.Widgets.Launcher.ToggleButton.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.SearchBox", Load("Bannerlord.LauncherEx.Resources.Prefabs.Widgets.Launcher.SearchBox.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.Scrollbar", Load("Bannerlord.LauncherEx.Resources.Prefabs.Widgets.Launcher.Scrollbar.xml")); - - WidgetFactoryManager.CreateAndRegister("Launcher.SettingsPropertyBoolView", Load("Bannerlord.LauncherEx.Resources.Prefabs.Properties.Launcher.SettingsPropertyBoolView.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.SettingsPropertyButtonView", Load("Bannerlord.LauncherEx.Resources.Prefabs.Properties.Launcher.SettingsPropertyButtonView.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.SettingsPropertyFloatView", Load("Bannerlord.LauncherEx.Resources.Prefabs.Properties.Launcher.SettingsPropertyFloatView.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.SettingsPropertyIntView", Load("Bannerlord.LauncherEx.Resources.Prefabs.Properties.Launcher.SettingsPropertyIntView.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.SettingsPropertyStringView", Load("Bannerlord.LauncherEx.Resources.Prefabs.Properties.Launcher.SettingsPropertyStringView.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.Options", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.Options.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.Options.OptionTuple", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.Options.OptionTuple.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.Mods2", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.Mods.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.Mods.ModuleTuple2", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.Mods.ModuleTuple.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.Saves", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.Saves.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.Saves.SaveTuple", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.Saves.SaveTuple.xml")); - WidgetFactoryManager.CreateAndRegister("Launcher.MessageBox", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.MessageBox.xml")); - - FontFactoryManager.Enable(_launcherHarmony); + case BUTRLocalizationManager.ChineseTraditional or BUTRLocalizationManager.ChineseSimple when asset.GetTexture("ui_fonts_1") is { } chinese: + GraphicsContextManager.CreateAssetTextureAndRegister("simkai", chinese); + SpriteDataManager.CreateGenericAndRegister("simkai"); + break; + case BUTRLocalizationManager.Japanese when asset.GetTexture("ui_fonts_2") is { } japanese: + GraphicsContextManager.CreateAssetTextureAndRegister("SourceHanSansJP", japanese); + SpriteDataManager.CreateGenericAndRegister("SourceHanSansJP"); + break; + case BUTRLocalizationManager.Korean when asset.GetTexture("ui_fonts_4") is { } korean: + GraphicsContextManager.CreateAssetTextureAndRegister("NanumGothicKR", korean); + SpriteDataManager.CreateGenericAndRegister("NanumGothicKR"); + break; } - private static XmlDocument Load(string embedPath) - { - using var stream = typeof(Manager).Assembly.GetManifestResourceStream(embedPath); - if (stream is null) throw new Exception($"Could not find embed resource '{embedPath}'!"); - using var xmlReader = XmlReader.Create(stream, new XmlReaderSettings { IgnoreComments = true }); - var doc = new XmlDocument(); - doc.Load(xmlReader); - return doc; - } - private static Stream LoadStream(string embedPath) - { - return typeof(Manager).Assembly.GetManifestResourceStream(embedPath) ?? throw new Exception($"Could not find embed resource '{embedPath}'!"); - } - public static void Disable() - { - OnDisable?.Invoke(); - //_compatibilityChecker.Dispose(); - GraphicsContextManager.Clear(); - SpriteDataManager.Clear(); - BrushFactoryManager.Clear(); - WidgetFactoryManager.Clear(); - BUTRLocalizationManager.Clear(); - _launcherHarmony.UnpatchAll(_launcherHarmony.Id); - } + BrushFactoryManager.Enable(_launcherHarmony); + BrushFactoryManager.CreateAndRegister(Load("Bannerlord.LauncherEx.Resources.Brushes.Launcher.xml")); - public static LauncherExData CurrentSettingsSnapshot() - { - var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Mount and Blade II Bannerlord", "Configs", "LauncherData.xml"); - if (!File.Exists(path)) return new(); + WidgetFactoryManager.Enable(_launcherHarmony); + WidgetFactoryManager.Register(typeof(LauncherToggleButtonWidget)); + WidgetFactoryManager.Register(typeof(LauncherSearchBoxWidget)); - return LauncherExData.FromUserDataXml(path) ?? new(); - } + WidgetFactoryManager.CreateAndRegister("Launcher.ToggleButton", Load("Bannerlord.LauncherEx.Resources.Prefabs.Widgets.Launcher.ToggleButton.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.SearchBox", Load("Bannerlord.LauncherEx.Resources.Prefabs.Widgets.Launcher.SearchBox.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.Scrollbar", Load("Bannerlord.LauncherEx.Resources.Prefabs.Widgets.Launcher.Scrollbar.xml")); + + WidgetFactoryManager.CreateAndRegister("Launcher.SettingsPropertyBoolView", Load("Bannerlord.LauncherEx.Resources.Prefabs.Properties.Launcher.SettingsPropertyBoolView.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.SettingsPropertyButtonView", Load("Bannerlord.LauncherEx.Resources.Prefabs.Properties.Launcher.SettingsPropertyButtonView.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.SettingsPropertyFloatView", Load("Bannerlord.LauncherEx.Resources.Prefabs.Properties.Launcher.SettingsPropertyFloatView.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.SettingsPropertyIntView", Load("Bannerlord.LauncherEx.Resources.Prefabs.Properties.Launcher.SettingsPropertyIntView.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.SettingsPropertyStringView", Load("Bannerlord.LauncherEx.Resources.Prefabs.Properties.Launcher.SettingsPropertyStringView.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.Options", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.Options.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.Options.OptionTuple", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.Options.OptionTuple.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.Mods2", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.Mods.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.Mods.ModuleTuple2", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.Mods.ModuleTuple.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.Saves", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.Saves.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.Saves.SaveTuple", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.Saves.SaveTuple.xml")); + WidgetFactoryManager.CreateAndRegister("Launcher.MessageBox", Load("Bannerlord.LauncherEx.Resources.Prefabs.Launcher.MessageBox.xml")); + + FontFactoryManager.Enable(_launcherHarmony); + } + + private static XmlDocument Load(string embedPath) + { + using var stream = typeof(Manager).Assembly.GetManifestResourceStream(embedPath); + if (stream is null) throw new Exception($"Could not find embed resource '{embedPath}'!"); + using var xmlReader = XmlReader.Create(stream, new XmlReaderSettings { IgnoreComments = true }); + var doc = new XmlDocument(); + doc.Load(xmlReader); + return doc; + } + private static Stream LoadStream(string embedPath) + { + return typeof(Manager).Assembly.GetManifestResourceStream(embedPath) ?? throw new Exception($"Could not find embed resource '{embedPath}'!"); + } + + public static void Disable() + { + OnDisable?.Invoke(); + //_compatibilityChecker.Dispose(); + GraphicsContextManager.Clear(); + SpriteDataManager.Clear(); + BrushFactoryManager.Clear(); + WidgetFactoryManager.Clear(); + BUTRLocalizationManager.Clear(); + _launcherHarmony.UnpatchAll(_launcherHarmony.Id); + } + + public static LauncherExData CurrentSettingsSnapshot() + { + var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Mount and Blade II Bannerlord", "Configs", "LauncherData.xml"); + if (!File.Exists(path)) return new(); + + return LauncherExData.FromUserDataXml(path) ?? new(); } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Mixins/LauncherConfirmStartVMMixin.cs b/src/Bannerlord.LauncherEx/Mixins/LauncherConfirmStartVMMixin.cs index cae8de7..e71ff35 100644 --- a/src/Bannerlord.LauncherEx/Mixins/LauncherConfirmStartVMMixin.cs +++ b/src/Bannerlord.LauncherEx/Mixins/LauncherConfirmStartVMMixin.cs @@ -3,15 +3,14 @@ using TaleWorlds.MountAndBlade.Launcher.Library; -namespace Bannerlord.LauncherEx.Mixins +namespace Bannerlord.LauncherEx.Mixins; + +internal sealed class LauncherConfirmStartVMMixin : ViewModelMixin { - internal sealed class LauncherConfirmStartVMMixin : ViewModelMixin - { - [BUTRDataSourceProperty] - public string CancelText2 => new BUTRTextObject("{=DzJmcvsP}Cancel").ToString(); - [BUTRDataSourceProperty] - public string ConfirmText2 => new BUTRTextObject("{=epTxGUqT}Confirm").ToString(); + [BUTRDataSourceProperty] + public string CancelText2 => new BUTRTextObject("{=DzJmcvsP}Cancel").ToString(); + [BUTRDataSourceProperty] + public string ConfirmText2 => new BUTRTextObject("{=epTxGUqT}Confirm").ToString(); - public LauncherConfirmStartVMMixin(LauncherConfirmStartVM launcherNewsVM) : base(launcherNewsVM) { } - } + public LauncherConfirmStartVMMixin(LauncherConfirmStartVM launcherNewsVM) : base(launcherNewsVM) { } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Mixins/LauncherModsVMMixin.cs b/src/Bannerlord.LauncherEx/Mixins/LauncherModsVMMixin.cs index 21cf508..84f13e3 100644 --- a/src/Bannerlord.LauncherEx/Mixins/LauncherModsVMMixin.cs +++ b/src/Bannerlord.LauncherEx/Mixins/LauncherModsVMMixin.cs @@ -1,209 +1,313 @@ -using Bannerlord.LauncherEx.Helpers; +using Bannerlord.BUTR.Shared.Helpers; +using Bannerlord.LauncherEx.Helpers; using Bannerlord.LauncherEx.ViewModels; using Bannerlord.LauncherManager.Localization; using Bannerlord.LauncherManager.Models; using Bannerlord.LauncherManager.Utils; using Bannerlord.ModuleManager; +using Newtonsoft.Json; + using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Net; +using System.Reflection; +using System.Text; using System.Threading; using System.Threading.Tasks; using TaleWorlds.Library; using TaleWorlds.MountAndBlade.Launcher.Library; -namespace Bannerlord.LauncherEx.Mixins -{ - internal enum ModuleType { Framework, Graphical, Standard, Patches } +using ApplicationVersion = TaleWorlds.Library.ApplicationVersion; - internal sealed class LauncherModsVMMixin : ViewModelMixin/*, IHasOrderer*/ - { - private readonly BUTRLauncherManagerHandler _launcherManagerHandler = BUTRLauncherManagerHandler.Default; - private readonly MBBindingList _modules = new(); - private readonly Dictionary _modulesLookup = new(); +namespace Bannerlord.LauncherEx.Mixins; + +// TODO: +internal enum ModuleType { Framework, Graphical, Standard, Patches } + +internal sealed class LauncherModsVMMixin : ViewModelMixin/*, IHasOrderer*/ +{ + private readonly BUTRLauncherManagerHandler _launcherManagerHandler = BUTRLauncherManagerHandler.Default; + private readonly MBBindingList _modules = new(); + private readonly Dictionary _modulesLookup = new(); + private IReadOnlyList _allModuleInfos; - [BUTRDataSourceProperty] - public bool GlobalCheckboxState { get => _checkboxState; set => SetField(ref _checkboxState, value); } - private bool _checkboxState; + [BUTRDataSourceProperty] + public bool GlobalCheckboxState { get => _checkboxState; set => SetField(ref _checkboxState, value); } + private bool _checkboxState; - [BUTRDataSourceProperty] - public bool IsDisabled2 { get => _isDisabled2; set => SetField(ref _isDisabled2, value); } - private bool _isDisabled2; + [BUTRDataSourceProperty] + public bool IsDisabled2 { get => _isDisabled2; set => SetField(ref _isDisabled2, value); } + private bool _isDisabled2; - [BUTRDataSourceProperty] - public MBBindingList Modules2 { get; } = new(); + [BUTRDataSourceProperty] + public MBBindingList Modules2 { get; } = new(); - // Fast lookup for the ViewModels + // Fast lookup for the ViewModels - [BUTRDataSourceProperty] - public bool IsForceSorted { get => _isForceSorted; set => SetField(ref _isForceSorted, value); } - private bool _isForceSorted; + [BUTRDataSourceProperty] + public bool IsForceSorted { get => _isForceSorted; set => SetField(ref _isForceSorted, value); } + private bool _isForceSorted; - [BUTRDataSourceProperty] - public LauncherHintVM? ForceSortedHint { get => _forceSortedHint; set => SetField(ref _forceSortedHint, value); } - private LauncherHintVM? _forceSortedHint; + [BUTRDataSourceProperty] + public LauncherHintVM? ForceSortedHint { get => _forceSortedHint; set => SetField(ref _forceSortedHint, value); } + private LauncherHintVM? _forceSortedHint; - [BUTRDataSourceProperty] - public string SearchText + [BUTRDataSourceProperty] + public string SearchText + { + get => _searchText; + set { - get => _searchText; - set + if (SetField(ref _searchText, value)) { - if (SetField(ref _searchText, value)) - { - SearchTextChanged(); - } + SearchTextChanged(); } } - private string _searchText = string.Empty; + } + private string _searchText = string.Empty; - [BUTRDataSourceProperty] - public string NameCategoryText2 => new BUTRTextObject("{=JtelOsIW}Name").ToString(); - [BUTRDataSourceProperty] - public string VersionCategoryText2 => new BUTRTextObject("{=14WBFIS1}Version").ToString(); + [BUTRDataSourceProperty] + public string NameCategoryText2 => new BUTRTextObject("{=JtelOsIW}Name").ToString(); + [BUTRDataSourceProperty] + public string VersionCategoryText2 => new BUTRTextObject("{=14WBFIS1}Version").ToString(); - public LauncherModsVMMixin(LauncherModsVM launcherModsVM) : base(launcherModsVM) - { - _launcherManagerHandler.RegisterModuleViewModelProvider(() => _modules, () => Modules2, SetViewModels); + [BUTRDataSourceProperty] + public LauncherHintVM? GlobalCheckboxHint { get => _globalCheckboxHint; set => SetField(ref _globalCheckboxHint, value); } + private LauncherHintVM? _globalCheckboxHint; - _launcherManagerHandler.RefreshModules(); - foreach (var moduleInfoExtended in _launcherManagerHandler.ExtendedModuleInfoCache.Values.OfType()) - { - var moduleVM = new BUTRLauncherModuleVM(moduleInfoExtended, ToggleModuleSelection, ValidateModule); - _modules.Add(moduleVM); - _modulesLookup[moduleVM.ModuleInfoExtended.Id] = moduleVM; - } - } + [BUTRDataSourceProperty] + public LauncherHintVM? RefreshHint { get => _refreshHint; set => SetField(ref _refreshHint, value); } + private LauncherHintVM? _refreshHint; + + [BUTRDataSourceProperty] + public LauncherHintVM? UpdateInfoHint { get => _updateInfoHint; set => SetField(ref _updateInfoHint, value); } + private LauncherHintVM? _updateInfoHint; - public ModuleInfoExtended? GetModuleById(string id) => _launcherManagerHandler.ExtendedModuleInfoCache.TryGetValue(id, out var mie) ? mie : null; - public ModuleInfoExtended? GetModuleByName(string name) => _launcherManagerHandler.ExtendedModuleInfoCache.Values.FirstOrDefault(x => x.Name == name); + public LauncherModsVMMixin(LauncherModsVM launcherModsVM) : base(launcherModsVM) + { + GlobalCheckboxHint = new LauncherHintVM(new BUTRTextObject("{=q5quVWMI}Toggle All Modules").ToString()); + RefreshHint = new LauncherHintVM(new BUTRTextObject("{=H5nMY4WU}Refresh Modules").ToString()); + UpdateInfoHint = new LauncherHintVM(new BUTRTextObject("{=zXWdahH9}Get Update Recommendations{NL}Clicking on this button will send your module list to the BUTR server to get compatibility scores and recommended versions.{NL}They are based on the crash reports from ButterLib.{NL}{NL}(Requires Internet Connection)") + .SetTextVariable("NL", Environment.NewLine).ToString()); + + _launcherManagerHandler.RegisterModuleViewModelProvider(() => _modules, () => Modules2, SetViewModels); - public void Initialize() + _launcherManagerHandler.RefreshModules(); + _allModuleInfos = _launcherManagerHandler.GetAllModules(); + foreach (var moduleInfoExtended in _launcherManagerHandler.ExtendedModuleInfoCache.Values.OfType()) { - Modules2.Clear(); + var moduleVM = new BUTRLauncherModuleVM(moduleInfoExtended, ToggleModuleSelection, ValidateModule, GetPossibleProviders); + _modules.Add(moduleVM); + _modulesLookup[moduleVM.ModuleInfoExtended.Id] = moduleVM; + } + } - var loadOrder = _launcherManagerHandler.LoadTWLoadOrder().ToDictionary(x => x.Key, x => x.Value.IsSelected); - if (_launcherManagerHandler.TryOrderByLoadOrder(loadOrder.Keys, x => loadOrder.TryGetValue(x, out var isSelected) && isSelected, out var issues, out var orderedModules)) - { - SetViewModels(orderedModules); - IsForceSorted = false; - } - else - { - IsForceSorted = true; - ForceSortedHint = new LauncherHintVM(new BUTRTextObject("{=pZVVdI5d}The Load Order was re-sorted with the default algorithm!{NL}Reasons:{NL}{REASONS}").SetTextVariable("REASONS", string.Join("\n", issues)).ToString()); + public ModuleInfoExtended? GetModuleById(string id) => _launcherManagerHandler.ExtendedModuleInfoCache.TryGetValue(id, out var mie) ? mie : null; + public ModuleInfoExtended? GetModuleByName(string name) => _launcherManagerHandler.ExtendedModuleInfoCache.Values.FirstOrDefault(x => x.Name == name); - // Beta sorting algorithm will fail currently in some cases, use the TW fallback - _launcherManagerHandler.TryOrderByLoadOrderTW(Enumerable.Empty(), x => loadOrder.TryGetValue(x, out var isSelected) && isSelected, out _, out orderedModules, true); - SetViewModels(orderedModules); // Set the ViewModels regarding the result + public void Initialize() + { + Modules2.Clear(); + + var loadOrder = _launcherManagerHandler.LoadLoadOrder().ToDictionary(x => x.Key, x => x.Value.IsSelected); + + /* + if (!LoadOrderChecker.IsLoadOrderCorrect()) + { - //TryOrderByLoadOrder(Enumerable.Empty(), x => loadOrder.TryGetValue(x, out var isSelected) && isSelected); - } } + */ - private void SetViewModels(IEnumerable orderedModuleViewModels) + if (_launcherManagerHandler.TryOrderByLoadOrder(loadOrder.Keys, x => loadOrder.TryGetValue(x, out var isSelected) && isSelected, out var issues, out var orderedModules)) { - Modules2.Clear(); - foreach (var viewModel in orderedModuleViewModels.OfType()) - Modules2.Add(viewModel); + SetViewModels(orderedModules); + IsForceSorted = false; + } + else + { + IsForceSorted = true; + ForceSortedHint = new LauncherHintVM(new BUTRTextObject("{=pZVVdI5d}The Load Order was re-sorted with the default algorithm!{NL}Reasons:{NL}{REASONS}").SetTextVariable("REASONS", string.Join("\n", issues)).ToString()); - // Validate all VM's after they were selected and ordered - foreach (var modules in Modules2) - modules.Validate(); + // Beta sorting algorithm will fail currently in some cases, use the TW fallback + _launcherManagerHandler.TryOrderByLoadOrderTW(Enumerable.Empty(), x => loadOrder.TryGetValue(x, out var isSelected) && isSelected, out _, out orderedModules, true); + SetViewModels(orderedModules); // Set the ViewModels regarding the result - _launcherManagerHandler.SetGameParametersLoadOrder(Modules2); + //TryOrderByLoadOrder(Enumerable.Empty(), x => loadOrder.TryGetValue(x, out var isSelected) && isSelected); } + } + + private void SetViewModels(IEnumerable orderedModuleViewModels) + { + Modules2.Clear(); + foreach (var viewModel in orderedModuleViewModels.OfType()) + Modules2.Add(viewModel); - private IEnumerable ValidateModule(BUTRLauncherModuleVM moduleVM) => SortHelper.ValidateModule(Modules2, _modulesLookup, moduleVM); - private void ToggleModuleSelection(BUTRLauncherModuleVM moduleVM) + _launcherManagerHandler.RefreshModules(); + _allModuleInfos = _launcherManagerHandler.GetAllModules(); + + // Validate all VM's after they were selected and ordered + foreach (var modules in Modules2) { - SortHelper.ToggleModuleSelection(Modules2, _modulesLookup, moduleVM); - _launcherManagerHandler.SetGameParametersLoadOrder(Modules2); + modules.Validate(); + modules.Refresh(); } - private void ChangeModulePosition(BUTRLauncherModuleVM targetModuleVM, int insertIndex, Action>? onIssues = null) + _launcherManagerHandler.SetGameParametersLoadOrder(Modules2); + } + + private IEnumerable ValidateModule(BUTRLauncherModuleVM moduleVM) => SortHelper.ValidateModule(Modules2, _modulesLookup, moduleVM); + private void ToggleModuleSelection(BUTRLauncherModuleVM moduleVM) + { + SortHelper.ToggleModuleSelection(Modules2, _modulesLookup, moduleVM); + _launcherManagerHandler.SetGameParametersLoadOrder(Modules2); + } + private ICollection GetPossibleProviders(ModuleInfoExtendedWithMetadata moduleInfo) => _allModuleInfos + .Where(x => x.Id == moduleInfo.Id && x.ModuleProviderType != moduleInfo.ModuleProviderType) + .Select(x => x.ModuleProviderType) + .ToList(); + + private void ChangeModulePosition(BUTRLauncherModuleVM targetModuleVM, int insertIndex, Action>? onIssues = null) + { + if (SortHelper.ChangeModulePosition(Modules2, _modulesLookup, targetModuleVM, insertIndex, onIssues)) { - if (SortHelper.ChangeModulePosition(Modules2, _modulesLookup, targetModuleVM, insertIndex, onIssues)) - { - _launcherManagerHandler.SetGameParametersLoadOrder(Modules2); - } + _launcherManagerHandler.SetGameParametersLoadOrder(Modules2); } + } - private void SearchTextChanged() + private void SearchTextChanged() + { + var searchText = SearchText; + if (string.IsNullOrEmpty(searchText)) { - var searchText = SearchText; - if (string.IsNullOrEmpty(searchText)) - { - foreach (var moduleVM in Modules2) - { - moduleVM.IsVisible = true; - } - return; - } - foreach (var moduleVM in Modules2) { - moduleVM.IsVisible = moduleVM.Name.IndexOf(SearchText, StringComparison.OrdinalIgnoreCase) != -1; + moduleVM.IsVisible = true; } + return; } - [BUTRDataSourceMethod] - public void ExecuteRefresh() + foreach (var moduleVM in Modules2) { - static IEnumerable Sort(IEnumerable source) - { - var orderedModules = source - .OrderByDescending(x => x.IsOfficial) - .ThenBy(x => x.Id, new AlphanumComparatorFast()) - .ToArray(); + moduleVM.IsVisible = moduleVM.Name.IndexOf(SearchText, StringComparison.OrdinalIgnoreCase) != -1; + } + } - return ModuleSorter.TopologySort(orderedModules, module => ModuleUtilities.GetDependencies(orderedModules, module)); - } + [BUTRDataSourceMethod] + public void ExecuteRefresh() + { + static IEnumerable Sort(IEnumerable source) + { + var orderedModules = source + .OrderByDescending(x => x.IsOfficial) + .ThenBy(x => x.Id, new AlphanumComparatorFast()) + .ToArray(); - var sorted = Sort(Modules2.Select(x => x.ModuleInfoExtended)).Select((x, i) => new { Item = x.Id, Index = i }).ToDictionary(x => x.Item, x => x.Index); - Modules2.Sort(new ByIndexComparer(x => sorted.TryGetValue(x.ModuleInfoExtended.Id, out var idx) ? idx : -1)); - //var sorted = Sort(Modules2.Select(x => x.ModuleInfoExtended)).Select(x => x.Id).ToList(); - //SortBy(sorted); - _launcherManagerHandler.SetGameParametersLoadOrder(Modules2); + return ModuleSorter.TopologySort(orderedModules, module => ModuleUtilities.GetDependencies(orderedModules, module)); } - [BUTRDataSourceMethod] - public void OnDrop(BUTRLauncherModuleVM targetModuleVM, int insertIndex, string type) + var sorted = Sort(Modules2.Select(x => x.ModuleInfoExtended)).Select((x, i) => new { Item = x.Id, Index = i }).ToDictionary(x => x.Item, x => x.Index); + Modules2.Sort(new ByIndexComparer(x => sorted.TryGetValue(x.ModuleInfoExtended.Id, out var idx) ? idx : -1)); + //var sorted = Sort(Modules2.Select(x => x.ModuleInfoExtended)).Select(x => x.Id).ToList(); + //SortBy(sorted); + + _launcherManagerHandler.RefreshModules(); + _allModuleInfos = _launcherManagerHandler.GetAllModules(); + foreach (var moduleVM in Modules2) + moduleVM.Refresh(); + + _launcherManagerHandler.SetGameParametersLoadOrder(Modules2); + } + + [BUTRDataSourceMethod] + public void OnDrop(BUTRLauncherModuleVM targetModuleVM, int insertIndex, string type) + { + if (type == "Module") { - if (type == "Module") + ChangeModulePosition(targetModuleVM, insertIndex, issues => { - ChangeModulePosition(targetModuleVM, insertIndex, issues => + HintManager.ShowHint(new BUTRTextObject("{=sP1a61KE}Failed to place the module to the desired position! Placing to the nearest available!{NL}Reason:{NL}{REASONS}") + .SetTextVariable("REASONS", string.Join("\n", issues)).ToString()); + Task.Factory.StartNew(async () => { - HintManager.ShowHint(new BUTRTextObject("{=sP1a61KE}Failed to place the module to the desired position! Placing to the nearest available!{NL}Reason:{NL}{REASONS}") - .SetTextVariable("REASONS", string.Join("\n", issues)).ToString()); - Task.Factory.StartNew(async () => - { - await Task.Delay(5000); - HintManager.HideHint(); - }, CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Current); - }); - } + await Task.Delay(5000); + HintManager.HideHint(); + }, CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Current); + }); } + } - [BUTRDataSourceMethod] - public void ExecuteGlobalCheckbox() + [BUTRDataSourceMethod] + public void ExecuteUpdateCheck() + { + try { - GlobalCheckboxState = !GlobalCheckboxState; + var uploadUrlAttr = typeof(LauncherModsVMMixin).Assembly.GetCustomAttributes().FirstOrDefault(a => a.Key == "BUTRCompatibilityScoreUrl"); + if (uploadUrlAttr is null) + return; + + var gameVersion = ApplicationVersionHelper.GameVersion() ?? ApplicationVersion.Empty; + var selectedModules = Modules2.Select(x => new + { + ModuleId = x.ModuleInfoExtended.Id, + ModuleVersion = x.ModuleInfoExtended.Version.ToString() + }).ToArray(); + var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new + { + GameVersion = $"{ApplicationVersion.GetPrefix(gameVersion.ApplicationVersionType)}{gameVersion.Major}.{gameVersion.Minor}.{gameVersion.Revision}", + Modules = selectedModules + })); + + var responseDefinition = new { Modules = new[] { new { ModuleId = "", Compatibility = 0d, RecommendedModuleVersion = "" } } }; + + var httpWebRequest = WebRequest.CreateHttp(uploadUrlAttr.Value); + httpWebRequest.Method = "POST"; + httpWebRequest.ContentType = "application/json"; + httpWebRequest.UserAgent = $"BLSE LauncherEx v{typeof(LauncherModsVMMixin).Assembly.GetName().Version}"; + httpWebRequest.Headers.Add("Tenant", "1"); + + using var writeStream = httpWebRequest.GetRequestStream(); + writeStream.Write(data, 0, data.Length); + + using var response = httpWebRequest.GetResponse(); + using var stream = response.GetResponseStream(); + using var responseReader = new StreamReader(stream ?? Stream.Null); + var json = responseReader.ReadLine() ?? string.Empty; + var result = JsonConvert.DeserializeAnonymousType(json, responseDefinition); + if (result is null) return; foreach (var moduleVM in Modules2) + moduleVM.RemoveUpdateInfo(); + + foreach (var module in result.Modules) { - if (GlobalCheckboxState) - { - if (moduleVM.IsValid && !moduleVM.IsSelected) - ToggleModuleSelection(moduleVM); - } - else - { - if (!moduleVM.ModuleInfoExtended.IsNative() && moduleVM.IsSelected) - ToggleModuleSelection(moduleVM); - } + if (Modules2.FirstOrDefault(x => x.ModuleInfoExtended.Id == module.ModuleId) is not { } moduleVM) continue; + if (module.Compatibility > 0d) + moduleVM.SetUpdateInfo(module.Compatibility, module.RecommendedModuleVersion); + } + } + catch (Exception) { /* ignore */ } + } + + [BUTRDataSourceMethod] + public void ExecuteGlobalCheckbox() + { + GlobalCheckboxState = !GlobalCheckboxState; + + foreach (var moduleVM in Modules2) + { + if (GlobalCheckboxState) + { + if (moduleVM.IsValid && !moduleVM.IsSelected) + ToggleModuleSelection(moduleVM); + } + else + { + if (!moduleVM.ModuleInfoExtended.IsNative() && moduleVM.IsSelected) + ToggleModuleSelection(moduleVM); } } } diff --git a/src/Bannerlord.LauncherEx/Mixins/LauncherNewsVMMixin.cs b/src/Bannerlord.LauncherEx/Mixins/LauncherNewsVMMixin.cs index 4521185..130371e 100644 --- a/src/Bannerlord.LauncherEx/Mixins/LauncherNewsVMMixin.cs +++ b/src/Bannerlord.LauncherEx/Mixins/LauncherNewsVMMixin.cs @@ -2,14 +2,13 @@ using TaleWorlds.MountAndBlade.Launcher.Library; -namespace Bannerlord.LauncherEx.Mixins +namespace Bannerlord.LauncherEx.Mixins; + +internal sealed class LauncherNewsVMMixin : ViewModelMixin { - internal sealed class LauncherNewsVMMixin : ViewModelMixin - { - [BUTRDataSourceProperty] - public bool IsDisabled2 { get => _isDisabled2; set => SetField(ref _isDisabled2, value); } - private bool _isDisabled2; - - public LauncherNewsVMMixin(LauncherNewsVM launcherNewsVM) : base(launcherNewsVM) { } - } + [BUTRDataSourceProperty] + public bool IsDisabled2 { get => _isDisabled2; set => SetField(ref _isDisabled2, value); } + private bool _isDisabled2; + + public LauncherNewsVMMixin(LauncherNewsVM launcherNewsVM) : base(launcherNewsVM) { } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Mixins/LauncherVMMixin.cs b/src/Bannerlord.LauncherEx/Mixins/LauncherVMMixin.cs index df5aa48..bfbe935 100644 --- a/src/Bannerlord.LauncherEx/Mixins/LauncherVMMixin.cs +++ b/src/Bannerlord.LauncherEx/Mixins/LauncherVMMixin.cs @@ -17,525 +17,524 @@ using TaleWorlds.MountAndBlade.Launcher.Library; using TaleWorlds.MountAndBlade.Launcher.Library.UserDatas; -namespace Bannerlord.LauncherEx.Mixins +namespace Bannerlord.LauncherEx.Mixins; + +internal sealed class LauncherVMMixin : ViewModelMixin { - internal sealed class LauncherVMMixin : ViewModelMixin - { - private delegate void ExecuteConfirmUnverifiedDLLStartDelegate(LauncherVM instance); - private static readonly ExecuteConfirmUnverifiedDLLStartDelegate? ExecuteConfirmUnverifiedDLLStartOriginal = - AccessTools2.GetDelegate(typeof(LauncherVM), "ExecuteConfirmUnverifiedDLLStart"); + private delegate void ExecuteConfirmUnverifiedDLLStartDelegate(LauncherVM instance); + private static readonly ExecuteConfirmUnverifiedDLLStartDelegate? ExecuteConfirmUnverifiedDLLStartOriginal = + AccessTools2.GetDelegate(typeof(LauncherVM), "ExecuteConfirmUnverifiedDLLStart"); - private delegate void ExecuteStartGameDelegate(LauncherVM instance, int mode); - private static readonly ExecuteStartGameDelegate? ExecuteStartGame = - AccessTools2.GetDelegate(typeof(LauncherVM), "ExecuteStartGame"); + private delegate void ExecuteStartGameDelegate(LauncherVM instance, int mode); + private static readonly ExecuteStartGameDelegate? ExecuteStartGame = + AccessTools2.GetDelegate(typeof(LauncherVM), "ExecuteStartGame"); - private static readonly AccessTools.FieldRef? UserDataManagerFieldRef = - AccessTools2.FieldRefAccess("_userDataManager"); + private static readonly AccessTools.FieldRef? UserDataManagerFieldRef = + AccessTools2.FieldRefAccess("_userDataManager"); - private delegate void SetIsDigitalCompanionDelegate(LauncherVM instance, bool value); - private static readonly SetIsDigitalCompanionDelegate? SetIsDigitalCompanion = - AccessTools2.GetPropertySetterDelegate(typeof(LauncherVM), "IsDigitalCompanion"); + private delegate void SetIsDigitalCompanionDelegate(LauncherVM instance, bool value); + private static readonly SetIsDigitalCompanionDelegate? SetIsDigitalCompanion = + AccessTools2.GetPropertySetterDelegate(typeof(LauncherVM), "IsDigitalCompanion"); - private delegate void UpdateAndSaveUserModsDataDelegate(LauncherVM instance, bool isMultiplayer); - private static readonly UpdateAndSaveUserModsDataDelegate? UpdateAndSaveUserModsDataMethod = - AccessTools2.GetDelegate(typeof(LauncherVM), "UpdateAndSaveUserModsData"); + private delegate void UpdateAndSaveUserModsDataDelegate(LauncherVM instance, bool isMultiplayer); + private static readonly UpdateAndSaveUserModsDataDelegate? UpdateAndSaveUserModsDataMethod = + AccessTools2.GetDelegate(typeof(LauncherVM), "UpdateAndSaveUserModsData"); - private delegate void RefreshDelegate(LauncherVM instance); - private static readonly RefreshDelegate? Refresh = - AccessTools2.GetDelegate(typeof(LauncherVM), "Refresh"); + private delegate void RefreshDelegate(LauncherVM instance); + private static readonly RefreshDelegate? Refresh = + AccessTools2.GetDelegate(typeof(LauncherVM), "Refresh"); - private enum TopTabs { NONE, Singleplayer, Multiplayer, Options, DigitalCompanion } - private TopTabs _state; + private enum TopTabs { NONE, Singleplayer, Multiplayer, Options, DigitalCompanion } + private TopTabs _state; - [BUTRDataSourceProperty] - public bool IsSingleplayer2 + [BUTRDataSourceProperty] + public bool IsSingleplayer2 + { + get => _state == TopTabs.Singleplayer; + set { - get => _state == TopTabs.Singleplayer; - set + if (value && _state != TopTabs.Singleplayer && ViewModel is not null) { - if (value && _state != TopTabs.Singleplayer && ViewModel is not null) + if (_state == TopTabs.Options) { - if (_state == TopTabs.Options) - { - SaveOptions(); - } + SaveOptions(); + } - _state = TopTabs.Singleplayer; + _state = TopTabs.Singleplayer; - SetState(); - } + SetState(); } } + } - [BUTRDataSourceProperty] - public bool IsMultiplayer2 + [BUTRDataSourceProperty] + public bool IsMultiplayer2 + { + get => _state == TopTabs.Multiplayer; + set { - get => _state == TopTabs.Multiplayer; - set + if (value && _state != TopTabs.Multiplayer && ViewModel is not null) { - if (value && _state != TopTabs.Multiplayer && ViewModel is not null) + if (_state == TopTabs.Options) { - if (_state == TopTabs.Options) - { - SaveOptions(); - } + SaveOptions(); + } - _state = TopTabs.Multiplayer; + _state = TopTabs.Multiplayer; - SetState(); - } + SetState(); } } + } - [BUTRDataSourceProperty] - public bool IsOptions + [BUTRDataSourceProperty] + public bool IsOptions + { + get => _state == TopTabs.Options; + set { - get => _state == TopTabs.Options; - set + if (value && _state != TopTabs.Options && ViewModel is not null) { - if (value && _state != TopTabs.Options && ViewModel is not null) - { - _state = TopTabs.Options; + _state = TopTabs.Options; - SetState(); - } + SetState(); } } + } - [BUTRDataSourceProperty] - public bool IsDigitalCompanion2 + [BUTRDataSourceProperty] + public bool IsDigitalCompanion2 + { + get => _state == TopTabs.DigitalCompanion; + set { - get => _state == TopTabs.DigitalCompanion; - set + if (value && _state != TopTabs.DigitalCompanion && ViewModel is not null) { - if (value && _state != TopTabs.DigitalCompanion && ViewModel is not null) + if (_state == TopTabs.Options) { - if (_state == TopTabs.Options) - { - SaveOptions(); - } + SaveOptions(); + } - _state = TopTabs.DigitalCompanion; + _state = TopTabs.DigitalCompanion; - SetState(); - } + SetState(); } } + } - [BUTRDataSourceProperty] - public bool IsModsDataSelected + [BUTRDataSourceProperty] + public bool IsModsDataSelected + { + get => _isModsDataSelected; + set { - get => _isModsDataSelected; - set + if (SetField(ref _isModsDataSelected, value)) { - if (SetField(ref _isModsDataSelected, value)) - { - OnPropertyChanged(nameof(ShowImportExport)); - OnPropertyChanged(nameof(ShowContinueSingleplayerButton)); - } + OnPropertyChanged(nameof(ShowImportExport)); + OnPropertyChanged(nameof(ShowContinueSingleplayerButton)); } } - private bool _isModsDataSelected; + } + private bool _isModsDataSelected; - [BUTRDataSourceProperty] - public bool IsSavesDataSelected + [BUTRDataSourceProperty] + public bool IsSavesDataSelected + { + get => _isSavesDataSelected; + set { - get => _isSavesDataSelected; - set + if (SetField(ref _isSavesDataSelected, value)) { - if (SetField(ref _isSavesDataSelected, value)) - { - OnPropertyChanged(nameof(ShowPlaySingleplayerButton)); - OnPropertyChanged(nameof(ShowContinueSingleplayerButton)); - OnPropertyChanged(nameof(ShowImportExport)); - } + OnPropertyChanged(nameof(ShowPlaySingleplayerButton)); + OnPropertyChanged(nameof(ShowContinueSingleplayerButton)); + OnPropertyChanged(nameof(ShowImportExport)); } } - private bool _isSavesDataSelected; + } + private bool _isSavesDataSelected; - [BUTRDataSourceProperty] - public HorizontalAlignment PlayButtonAlignment => _state == TopTabs.Singleplayer ? HorizontalAlignment.Right : HorizontalAlignment.Center; + [BUTRDataSourceProperty] + public HorizontalAlignment PlayButtonAlignment => _state == TopTabs.Singleplayer ? HorizontalAlignment.Right : HorizontalAlignment.Center; - [BUTRDataSourceProperty] - public bool RandomImageSwitch { get => _randomImageSwitch; set => SetField(ref _randomImageSwitch, value); } - private bool _randomImageSwitch; - - [BUTRDataSourceProperty] - public string OptionsText { get => _optionsText; set => SetField(ref _optionsText, value); } - private string _optionsText = new BUTRTextObject("{=yS5hbWCL}Options").ToString(); - - [BUTRDataSourceProperty] - public string LauncherText { get => _launcherText; set => SetField(ref _launcherText, value); } - private string _launcherText = new BUTRTextObject("{=V66qoU6n}Launcher").ToString(); - - [BUTRDataSourceProperty] - public string GameText { get => _gameText; set => SetField(ref _gameText, value); } - private string _gameText = new BUTRTextObject("{=ro4RMgyt}Game").ToString(); - - [BUTRDataSourceProperty] - public string EngineText { get => _engineText; set => SetField(ref _engineText, value); } - private string _engineText = new BUTRTextObject("{=q4rQuTgG}Engine").ToString(); - - [BUTRDataSourceProperty] - public string SavesText { get => _savesText; set => SetField(ref _savesText, value); } - private string _savesText = new BUTRTextObject("{=d5OjKcGE}Saves").ToString(); - - [BUTRDataSourceProperty] - public string BLSEVersionText { get => _blseVersionText; set => SetField(ref _blseVersionText, value); } - private string _blseVersionText; - - [BUTRDataSourceProperty] - public string BUTRLoaderVersionText { get => _butrLoaderVersionText; set => SetField(ref _butrLoaderVersionText, value); } - private string _butrLoaderVersionText; - - [BUTRDataSourceProperty] - public BUTRLauncherOptionsVM OptionsLauncherData { get => _optionsLauncherData; set => SetField(ref _optionsLauncherData, value); } - private BUTRLauncherOptionsVM _optionsLauncherData; - - [BUTRDataSourceProperty] - public BUTRLauncherOptionsVM OptionsGameData { get => _optionsGameData; set => SetField(ref _optionsGameData, value); } - private BUTRLauncherOptionsVM _optionsGameData; - - [BUTRDataSourceProperty] - public BUTRLauncherOptionsVM OptionsEngineData { get => _optionsEngineData; set => SetField(ref _optionsEngineData, value); } - private BUTRLauncherOptionsVM _optionsEngineData; - - [BUTRDataSourceProperty] - public BUTRLauncherSavesVM? SavesData { get => _savesData; set => SetField(ref _savesData, value); } - private BUTRLauncherSavesVM? _savesData; - - [BUTRDataSourceProperty] - public BUTRLauncherMessageBoxVM? MessageBox { get => _messageBox; set => SetField(ref _messageBox, value); } - private BUTRLauncherMessageBoxVM? _messageBox = new(); - - [BUTRDataSourceProperty] - public bool ShowMods => IsSingleplayer2 || IsMultiplayer2; - [BUTRDataSourceProperty] - public bool ShowNews => IsSingleplayer2 || IsMultiplayer2 || IsDigitalCompanion2; - - [BUTRDataSourceProperty] - public bool ShowRandomImage { get => _showRandomImage; set => SetField(ref _showRandomImage, value); } - private bool _showRandomImage; - - [BUTRDataSourceProperty] - public bool ShowImportExport => IsSingleplayer2 && (IsModsDataSelected || (IsSavesDataSelected && SavesData?.Selected is not null)); - - [BUTRDataSourceProperty] - public bool ShowBUTRLoaderVersionText => IsSingleplayer2 || IsOptions; - - [BUTRDataSourceProperty] - public bool ShowPlaySingleplayerButton => IsSingleplayer2 && !IsSavesDataSelected; - [BUTRDataSourceProperty] - public bool ShowContinueSingleplayerButton => IsSingleplayer2 && (!IsSavesDataSelected || SavesData?.Selected is not null); - - [BUTRDataSourceProperty] - public float ContentTabControlMarginRight { get => _contentTabControlMarginRight; set => SetField(ref _contentTabControlMarginRight, value); } - private float _contentTabControlMarginRight = 0; - - [BUTRDataSourceProperty] - public float ContentTabControlMarginBottom { get => _contentTabControlMarginBottom; set => SetField(ref _contentTabControlMarginBottom, value); } - private float _contentTabControlMarginBottom = 114; - - [BUTRDataSourceProperty] - public float BUTRLoaderVersionMarginBottom { get => _butrLoaderVersionMarginBottom; set => SetField(ref _butrLoaderVersionMarginBottom, value); } - private float _butrLoaderVersionMarginBottom = 90; - - [BUTRDataSourceProperty] - public float BLSEVersionMarginBottom { get => _blseLoaderVersionMarginBottom; set => SetField(ref _blseLoaderVersionMarginBottom, value); } - private float _blseLoaderVersionMarginBottom = 70; - - [BUTRDataSourceProperty] - public float DividerMarginBottom { get => _dividerMarginBottom; set => SetField(ref _dividerMarginBottom, value); } - private float _dividerMarginBottom = 113; - - [BUTRDataSourceProperty] - public float BackgroundHeight { get => _backgroundHeight; set => SetField(ref _backgroundHeight, value); } - private float _backgroundHeight = 581; // 700 - - [BUTRDataSourceProperty] - public string SingleplayerText2 => new BUTRTextObject("{=Hk7FBBSa}Singleplayer").ToString(); - [BUTRDataSourceProperty] - public string MultiplayerText2 => new BUTRTextObject("{=UOGhdUWE}Multiplayer").ToString(); - [BUTRDataSourceProperty] - public string DigitalCompanionText2 => new BUTRTextObject("{=VDTcZpPr}Digital Companion").ToString(); - [BUTRDataSourceProperty] - public string NewsText2 => new BUTRTextObject("{=Tg0If68v}News").ToString(); - [BUTRDataSourceProperty] - public string ModsText2 => new BUTRTextObject("{=YGU9eXM0}Mods").ToString(); - [BUTRDataSourceProperty] - public string PlayText2 => new BUTRTextObject("{=xYv4iv7C}PLAY").ToString(); - [BUTRDataSourceProperty] - public string ContinueText2 => new BUTRTextObject("{=6B3iZLqR}CONTINUE").ToString(); - [BUTRDataSourceProperty] - public string LaunchText2 => new BUTRTextObject("{=eUt6GKkQ}LAUNCH").ToString(); - - private readonly UserDataManager? _userDataManager; - private readonly LauncherModsVMMixin? _launcherModsVMMixin; - private readonly BUTRLauncherManagerHandler _launcherManagerHandler = BUTRLauncherManagerHandler.Default; - - private ModuleListHandler? _currentModuleListHandler; - - public LauncherVMMixin(LauncherVM launcherVM) : base(launcherVM) - { - _launcherManagerHandler.RegisterStateProvider(() => new LauncherState(isSingleplayer: IsSingleplayer2)); + [BUTRDataSourceProperty] + public bool RandomImageSwitch { get => _randomImageSwitch; set => SetField(ref _randomImageSwitch, value); } + private bool _randomImageSwitch; + + [BUTRDataSourceProperty] + public string OptionsText { get => _optionsText; set => SetField(ref _optionsText, value); } + private string _optionsText = new BUTRTextObject("{=yS5hbWCL}Options").ToString(); + + [BUTRDataSourceProperty] + public string LauncherText { get => _launcherText; set => SetField(ref _launcherText, value); } + private string _launcherText = new BUTRTextObject("{=V66qoU6n}Launcher").ToString(); + + [BUTRDataSourceProperty] + public string GameText { get => _gameText; set => SetField(ref _gameText, value); } + private string _gameText = new BUTRTextObject("{=ro4RMgyt}Game").ToString(); + + [BUTRDataSourceProperty] + public string EngineText { get => _engineText; set => SetField(ref _engineText, value); } + private string _engineText = new BUTRTextObject("{=q4rQuTgG}Engine").ToString(); + + [BUTRDataSourceProperty] + public string SavesText { get => _savesText; set => SetField(ref _savesText, value); } + private string _savesText = new BUTRTextObject("{=d5OjKcGE}Saves").ToString(); + + [BUTRDataSourceProperty] + public string BLSEVersionText { get => _blseVersionText; set => SetField(ref _blseVersionText, value); } + private string _blseVersionText; + + [BUTRDataSourceProperty] + public string BUTRLoaderVersionText { get => _butrLoaderVersionText; set => SetField(ref _butrLoaderVersionText, value); } + private string _butrLoaderVersionText; + + [BUTRDataSourceProperty] + public BUTRLauncherOptionsVM OptionsLauncherData { get => _optionsLauncherData; set => SetField(ref _optionsLauncherData, value); } + private BUTRLauncherOptionsVM _optionsLauncherData; + + [BUTRDataSourceProperty] + public BUTRLauncherOptionsVM OptionsGameData { get => _optionsGameData; set => SetField(ref _optionsGameData, value); } + private BUTRLauncherOptionsVM _optionsGameData; + + [BUTRDataSourceProperty] + public BUTRLauncherOptionsVM OptionsEngineData { get => _optionsEngineData; set => SetField(ref _optionsEngineData, value); } + private BUTRLauncherOptionsVM _optionsEngineData; + + [BUTRDataSourceProperty] + public BUTRLauncherSavesVM? SavesData { get => _savesData; set => SetField(ref _savesData, value); } + private BUTRLauncherSavesVM? _savesData; + + [BUTRDataSourceProperty] + public BUTRLauncherMessageBoxVM? MessageBox { get => _messageBox; set => SetField(ref _messageBox, value); } + private BUTRLauncherMessageBoxVM? _messageBox = new(); + + [BUTRDataSourceProperty] + public bool ShowMods => IsSingleplayer2 || IsMultiplayer2; + [BUTRDataSourceProperty] + public bool ShowNews => IsSingleplayer2 || IsMultiplayer2 || IsDigitalCompanion2; + + [BUTRDataSourceProperty] + public bool ShowRandomImage { get => _showRandomImage; set => SetField(ref _showRandomImage, value); } + private bool _showRandomImage; + + [BUTRDataSourceProperty] + public bool ShowImportExport => IsSingleplayer2 && (IsModsDataSelected || (IsSavesDataSelected && SavesData?.Selected is not null)); + + [BUTRDataSourceProperty] + public bool ShowBUTRLoaderVersionText => IsSingleplayer2 || IsOptions; + + [BUTRDataSourceProperty] + public bool ShowPlaySingleplayerButton => IsSingleplayer2 && !IsSavesDataSelected; + [BUTRDataSourceProperty] + public bool ShowContinueSingleplayerButton => IsSingleplayer2 && (!IsSavesDataSelected || SavesData?.Selected is not null); + + [BUTRDataSourceProperty] + public float ContentTabControlMarginRight { get => _contentTabControlMarginRight; set => SetField(ref _contentTabControlMarginRight, value); } + private float _contentTabControlMarginRight = 0; + + [BUTRDataSourceProperty] + public float ContentTabControlMarginBottom { get => _contentTabControlMarginBottom; set => SetField(ref _contentTabControlMarginBottom, value); } + private float _contentTabControlMarginBottom = 114; + + [BUTRDataSourceProperty] + public float BUTRLoaderVersionMarginBottom { get => _butrLoaderVersionMarginBottom; set => SetField(ref _butrLoaderVersionMarginBottom, value); } + private float _butrLoaderVersionMarginBottom = 90; + + [BUTRDataSourceProperty] + public float BLSEVersionMarginBottom { get => _blseLoaderVersionMarginBottom; set => SetField(ref _blseLoaderVersionMarginBottom, value); } + private float _blseLoaderVersionMarginBottom = 70; + + [BUTRDataSourceProperty] + public float DividerMarginBottom { get => _dividerMarginBottom; set => SetField(ref _dividerMarginBottom, value); } + private float _dividerMarginBottom = 113; + + [BUTRDataSourceProperty] + public float BackgroundHeight { get => _backgroundHeight; set => SetField(ref _backgroundHeight, value); } + private float _backgroundHeight = 581; // 700 + + [BUTRDataSourceProperty] + public string SingleplayerText2 => new BUTRTextObject("{=Hk7FBBSa}Singleplayer").ToString(); + [BUTRDataSourceProperty] + public string MultiplayerText2 => new BUTRTextObject("{=UOGhdUWE}Multiplayer").ToString(); + [BUTRDataSourceProperty] + public string DigitalCompanionText2 => new BUTRTextObject("{=VDTcZpPr}Digital Companion").ToString(); + [BUTRDataSourceProperty] + public string NewsText2 => new BUTRTextObject("{=Tg0If68v}News").ToString(); + [BUTRDataSourceProperty] + public string ModsText2 => new BUTRTextObject("{=YGU9eXM0}Mods").ToString(); + [BUTRDataSourceProperty] + public string PlayText2 => new BUTRTextObject("{=xYv4iv7C}PLAY").ToString(); + [BUTRDataSourceProperty] + public string ContinueText2 => new BUTRTextObject("{=6B3iZLqR}CONTINUE").ToString(); + [BUTRDataSourceProperty] + public string LaunchText2 => new BUTRTextObject("{=eUt6GKkQ}LAUNCH").ToString(); + + private readonly UserDataManager? _userDataManager; + private readonly LauncherModsVMMixin? _launcherModsVMMixin; + private readonly BUTRLauncherManagerHandler _launcherManagerHandler = BUTRLauncherManagerHandler.Default; + + private ModuleListHandler? _currentModuleListHandler; + + public LauncherVMMixin(LauncherVM launcherVM) : base(launcherVM) + { + _launcherManagerHandler.RegisterStateProvider(() => new LauncherState(isSingleplayer: IsSingleplayer2)); - _userDataManager = UserDataManagerFieldRef?.Invoke(launcherVM); + _userDataManager = UserDataManagerFieldRef?.Invoke(launcherVM); - var blseMetadata = AccessTools2.TypeByName("Bannerlord.BLSE.BLSEInterceptorAttribute")?.Assembly.GetCustomAttributes(); - var launcherExMetadata = typeof(LauncherVMMixin).Assembly.GetCustomAttributes(); - _blseVersionText = $"BLSE v{blseMetadata?.FirstOrDefault(x => x.Key == "BLSEVersion")?.Value ?? "0.0.0.0"}"; - _butrLoaderVersionText = $"LauncherEx v{launcherExMetadata.FirstOrDefault(x => x.Key == "LauncherExVersion")?.Value ?? "0.0.0.0"}"; + var blseMetadata = AccessTools2.TypeByName("Bannerlord.BLSE.BLSEInterceptorAttribute")?.Assembly.GetCustomAttributes(); + var launcherExMetadata = typeof(LauncherVMMixin).Assembly.GetCustomAttributes(); + _blseVersionText = $"BLSE v{blseMetadata?.FirstOrDefault(x => x.Key == "BLSEVersion")?.Value ?? "0.0.0.0"}"; + _butrLoaderVersionText = $"LauncherEx v{launcherExMetadata.FirstOrDefault(x => x.Key == "LauncherExVersion")?.Value ?? "0.0.0.0"}"; - _optionsEngineData = new BUTRLauncherOptionsVM(OptionsType.Engine, SaveUserData, RefreshOptions); - _optionsGameData = new BUTRLauncherOptionsVM(OptionsType.Game, SaveUserData, RefreshOptions); - _optionsLauncherData = new BUTRLauncherOptionsVM(OptionsType.Launcher, SaveUserData, RefreshOptions); + _optionsEngineData = new BUTRLauncherOptionsVM(OptionsType.Engine, SaveUserData, RefreshOptions); + _optionsGameData = new BUTRLauncherOptionsVM(OptionsType.Game, SaveUserData, RefreshOptions); + _optionsLauncherData = new BUTRLauncherOptionsVM(OptionsType.Launcher, SaveUserData, RefreshOptions); - if (launcherVM.GetPropertyValue(nameof(LauncherVM.ModsData)) is LauncherModsVM lmvm && lmvm.GetMixin() is { } mixin) - { - _launcherModsVMMixin = mixin; + if (launcherVM.GetPropertyValue(nameof(LauncherVM.ModsData)) is LauncherModsVM lmvm && lmvm.GetMixin() is { } mixin) + { + _launcherModsVMMixin = mixin; - _savesData = new BUTRLauncherSavesVM(mixin.GetModuleById, mixin.GetModuleByName); - _savesData.PropertyChanged += (_, args) => + _savesData = new BUTRLauncherSavesVM(mixin.GetModuleById, mixin.GetModuleByName); + _savesData.PropertyChanged += (_, args) => + { + if (args.PropertyName == "SaveSelected") { - if (args.PropertyName == "SaveSelected") - { - OnPropertyChanged(nameof(ShowImportExport)); - OnPropertyChanged(nameof(ShowContinueSingleplayerButton)); - } - }; - } + OnPropertyChanged(nameof(ShowImportExport)); + OnPropertyChanged(nameof(ShowContinueSingleplayerButton)); + } + }; + } - ShowRandomImage = !LauncherSettings.HideRandomImage; - ContentTabControlMarginRight = LauncherSettings.HideRandomImage ? 5 : 114; - BackgroundHeight = LauncherSettings.BigMode ? 700 : 581; + ShowRandomImage = !LauncherSettings.HideRandomImage; + ContentTabControlMarginRight = LauncherSettings.HideRandomImage ? 5 : 114; + BackgroundHeight = LauncherSettings.BigMode ? 700 : 581; - IsDigitalCompanion2 = (bool?) launcherVM.GetPropertyValue("IsDigitalCompanion") ?? false; - IsMultiplayer2 = launcherVM.IsMultiplayer; - IsSingleplayer2 = launcherVM.IsSingleplayer; + IsDigitalCompanion2 = (bool?) launcherVM.GetPropertyValue("IsDigitalCompanion") ?? false; + IsMultiplayer2 = launcherVM.IsMultiplayer; + IsSingleplayer2 = launcherVM.IsSingleplayer; - Refresh?.Invoke(launcherVM); - } + Refresh?.Invoke(launcherVM); + } - private void SetState() - { - if (ViewModel is null) return; - - OnPropertyChanged(nameof(IsSingleplayer2)); - OnPropertyChanged(nameof(IsMultiplayer2)); - OnPropertyChanged(nameof(IsOptions)); - OnPropertyChanged(nameof(IsDigitalCompanion2)); - OnPropertyChanged(nameof(ShowBUTRLoaderVersionText)); - OnPropertyChanged(nameof(PlayButtonAlignment)); - OnPropertyChanged(nameof(ShowNews)); - OnPropertyChanged(nameof(ShowMods)); - OnPropertyChanged(nameof(ShowPlaySingleplayerButton)); - OnPropertyChanged(nameof(ShowContinueSingleplayerButton)); - - ViewModel.IsSingleplayer = IsSingleplayer2; - ViewModel.IsMultiplayer = IsMultiplayer2; - SetIsDigitalCompanion?.Invoke(ViewModel, IsDigitalCompanion2); - - RandomImageSwitch = !RandomImageSwitch; - - ViewModel.News.SetPropertyValue(nameof(LauncherNewsVMMixin.IsDisabled2), !ShowNews); - ViewModel.ModsData.SetPropertyValue(nameof(LauncherModsVMMixin.IsDisabled2), !ShowMods); - if (SavesData is not null) - SavesData.IsDisabled = !IsSingleplayer2; - OptionsLauncherData.IsDisabled = !IsOptions; - OptionsGameData.IsDisabled = !IsOptions; - OptionsEngineData.IsDisabled = !IsOptions; - if (IsOptions) - RefreshOptions(); - - ContentTabControlMarginBottom = IsOptions ? 65 : 114; - BUTRLoaderVersionMarginBottom = IsOptions ? 45 : 90; - BLSEVersionMarginBottom = IsOptions ? 25 : 70; - DividerMarginBottom = IsOptions ? 64 : 113; - } + private void SetState() + { + if (ViewModel is null) return; + + OnPropertyChanged(nameof(IsSingleplayer2)); + OnPropertyChanged(nameof(IsMultiplayer2)); + OnPropertyChanged(nameof(IsOptions)); + OnPropertyChanged(nameof(IsDigitalCompanion2)); + OnPropertyChanged(nameof(ShowBUTRLoaderVersionText)); + OnPropertyChanged(nameof(PlayButtonAlignment)); + OnPropertyChanged(nameof(ShowNews)); + OnPropertyChanged(nameof(ShowMods)); + OnPropertyChanged(nameof(ShowPlaySingleplayerButton)); + OnPropertyChanged(nameof(ShowContinueSingleplayerButton)); + + ViewModel.IsSingleplayer = IsSingleplayer2; + ViewModel.IsMultiplayer = IsMultiplayer2; + SetIsDigitalCompanion?.Invoke(ViewModel, IsDigitalCompanion2); + + RandomImageSwitch = !RandomImageSwitch; + + ViewModel.News.SetPropertyValue(nameof(LauncherNewsVMMixin.IsDisabled2), !ShowNews); + ViewModel.ModsData.SetPropertyValue(nameof(LauncherModsVMMixin.IsDisabled2), !ShowMods); + if (SavesData is not null) + SavesData.IsDisabled = !IsSingleplayer2; + OptionsLauncherData.IsDisabled = !IsOptions; + OptionsGameData.IsDisabled = !IsOptions; + OptionsEngineData.IsDisabled = !IsOptions; + if (IsOptions) + RefreshOptions(); + + ContentTabControlMarginBottom = IsOptions ? 65 : 114; + BUTRLoaderVersionMarginBottom = IsOptions ? 45 : 90; + BLSEVersionMarginBottom = IsOptions ? 25 : 70; + DividerMarginBottom = IsOptions ? 64 : 113; + } - public void RefreshOptions() - { - OptionsLauncherData.Refresh(); - OptionsGameData.Refresh(); - OptionsEngineData.Refresh(); - } + public void RefreshOptions() + { + OptionsLauncherData.Refresh(); + OptionsGameData.Refresh(); + OptionsEngineData.Refresh(); + } - public void SaveUserData() - { - if (ViewModel is null) return; + public void SaveUserData() + { + if (ViewModel is null) return; - ShowRandomImage = !LauncherSettings.HideRandomImage; - ContentTabControlMarginRight = LauncherSettings.HideRandomImage ? 5 : 114; - BackgroundHeight = LauncherSettings.BigMode ? 700 : 581; - UpdateAndSaveUserModsDataMethod?.Invoke(ViewModel, IsMultiplayer2); - } + ShowRandomImage = !LauncherSettings.HideRandomImage; + ContentTabControlMarginRight = LauncherSettings.HideRandomImage ? 5 : 114; + BackgroundHeight = LauncherSettings.BigMode ? 700 : 581; + UpdateAndSaveUserModsDataMethod?.Invoke(ViewModel, IsMultiplayer2); + } - public void SaveOptions() - { - OptionsLauncherData.Save(); - OptionsGameData.Save(); - OptionsEngineData.Save(); - } + public void SaveOptions() + { + OptionsLauncherData.Save(); + OptionsGameData.Save(); + OptionsEngineData.Save(); + } - // Ensure save is triggered when launching the game - [BUTRDataSourceMethod] - public void ExecuteConfirmUnverifiedDLLStart() - { - if (ViewModel is null) return; + // Ensure save is triggered when launching the game + [BUTRDataSourceMethod] + public void ExecuteConfirmUnverifiedDLLStart() + { + if (ViewModel is null) return; - SaveUserData(); - ExecuteConfirmUnverifiedDLLStartOriginal?.Invoke(ViewModel); - } + SaveUserData(); + ExecuteConfirmUnverifiedDLLStartOriginal?.Invoke(ViewModel); + } - [BUTRDataSourceMethod] - public void ExecuteBeginHintImport() + [BUTRDataSourceMethod] + public void ExecuteBeginHintImport() + { + if (IsSingleplayer2 && IsModsDataSelected) { - if (IsSingleplayer2 && IsModsDataSelected) - { - HintManager.ShowHint(new BUTRTextObject("{=Aws9irMU}Import Load Order")); - } - if (IsSingleplayer2 && IsSavesDataSelected) - { - HintManager.ShowHint(new BUTRTextObject("{=4wKr76gx}Import Save's Load Order")); - } + HintManager.ShowHint(new BUTRTextObject("{=Aws9irMU}Import Load Order")); } - - [BUTRDataSourceMethod] - public void ExecuteBeginHintExport() + if (IsSingleplayer2 && IsSavesDataSelected) { - if (IsSingleplayer2 && IsModsDataSelected) - { - HintManager.ShowHint(new BUTRTextObject("{=XdZGqnFW}Export Current Load Order")); - } - if (IsSingleplayer2 && IsSavesDataSelected) - { - HintManager.ShowHint(new BUTRTextObject("{=G55IdM6M}Export Save's Load Order")); - } + HintManager.ShowHint(new BUTRTextObject("{=4wKr76gx}Import Save's Load Order")); } + } - [BUTRDataSourceMethod] - public void ExecuteEndHint() + [BUTRDataSourceMethod] + public void ExecuteBeginHintExport() + { + if (IsSingleplayer2 && IsModsDataSelected) { - HintManager.HideHint(); + HintManager.ShowHint(new BUTRTextObject("{=XdZGqnFW}Export Current Load Order")); } - - [BUTRDataSourceMethod] - public void ExecuteImport() + if (IsSingleplayer2 && IsSavesDataSelected) { - if (ViewModel is null || _launcherModsVMMixin is null || UpdateAndSaveUserModsDataMethod is null) return; + HintManager.ShowHint(new BUTRTextObject("{=G55IdM6M}Export Save's Load Order")); + } + } + + [BUTRDataSourceMethod] + public void ExecuteEndHint() + { + HintManager.HideHint(); + } - _currentModuleListHandler = new ModuleListHandler(_launcherManagerHandler); + [BUTRDataSourceMethod] + public void ExecuteImport() + { + if (ViewModel is null || _launcherModsVMMixin is null || UpdateAndSaveUserModsDataMethod is null) return; + + _currentModuleListHandler = new ModuleListHandler(_launcherManagerHandler); - if (IsSingleplayer2 && IsModsDataSelected) + if (IsSingleplayer2 && IsModsDataSelected) + { + var thread = new Thread(() => { - var thread = new Thread(() => + _currentModuleListHandler.Import(result => { - _currentModuleListHandler.Import(result => - { - if (!result) return; - UpdateAndSaveUserModsDataMethod(ViewModel, false); - HintManager.ShowHint(new BUTRTextObject("{=eohqbvHU}Successfully imported list!")); - }); + if (!result) return; + UpdateAndSaveUserModsDataMethod(ViewModel, false); + HintManager.ShowHint(new BUTRTextObject("{=eohqbvHU}Successfully imported list!")); }); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - thread.Join(); - } - if (IsSingleplayer2 && IsSavesDataSelected && SavesData?.Selected?.Name is { } saveName) + }); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); + } + if (IsSingleplayer2 && IsSavesDataSelected && SavesData?.Selected?.Name is { } saveName) + { + var thread = new Thread(() => { - var thread = new Thread(() => + _currentModuleListHandler.ImportSaveFile(saveName, result => { - _currentModuleListHandler.ImportSaveFile(saveName, result => - { - if (!result) return; - UpdateAndSaveUserModsDataMethod(ViewModel, false); - HintManager.ShowHint(new BUTRTextObject("{=eohqbvHU}Successfully imported list!")); - }); + if (!result) return; + UpdateAndSaveUserModsDataMethod(ViewModel, false); + HintManager.ShowHint(new BUTRTextObject("{=eohqbvHU}Successfully imported list!")); }); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - thread.Join(); - } + }); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); } + } - [BUTRDataSourceMethod] - public void ExecuteExport() - { - if (ViewModel is null || _launcherModsVMMixin is null) return; + [BUTRDataSourceMethod] + public void ExecuteExport() + { + if (ViewModel is null || _launcherModsVMMixin is null) return; - _currentModuleListHandler = new ModuleListHandler(_launcherManagerHandler); + _currentModuleListHandler = new ModuleListHandler(_launcherManagerHandler); - if (IsSingleplayer2 && IsModsDataSelected) + if (IsSingleplayer2 && IsModsDataSelected) + { + var thread = new Thread(() => { - var thread = new Thread(() => - { - _currentModuleListHandler.Export(); - }); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - thread.Join(); - } - if (IsSingleplayer2 && IsSavesDataSelected && SavesData?.Selected?.Name is { } saveName) + _currentModuleListHandler.Export(); + }); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); + } + if (IsSingleplayer2 && IsSavesDataSelected && SavesData?.Selected?.Name is { } saveName) + { + var thread = new Thread(() => { - var thread = new Thread(() => - { - _currentModuleListHandler.ExportSaveFile(saveName); - }); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - thread.Join(); - } + _currentModuleListHandler.ExportSaveFile(saveName); + }); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); } + } - [BUTRDataSourceMethod(OverrideName = "ExecuteStartGame")] - public void ExecuteStartGameOverride(int mode) - { - if (ViewModel is null || ExecuteStartGame is null) return; + [BUTRDataSourceMethod(OverrideName = "ExecuteStartGame")] + public void ExecuteStartGameOverride(int mode) + { + if (ViewModel is null || ExecuteStartGame is null) return; - if (IsSavesDataSelected && SavesData?.Selected is { } saveVM) + if (IsSavesDataSelected && SavesData?.Selected is { } saveVM) + { + _launcherManagerHandler.SetGameParameterSaveFile(saveVM.Name); + if (saveVM.HasWarning || saveVM.HasError) { - _launcherManagerHandler.SetGameParameterSaveFile(saveVM.Name); - if (saveVM.HasWarning || saveVM.HasError) + var description = new StringBuilder(); + if (saveVM.HasError) + { + description.Append(saveVM.ErrorHint?.Text ?? string.Empty); + } + + if (saveVM is { HasError: true, HasWarning: true }) { - var description = new StringBuilder(); - if (saveVM.HasError) - { - description.Append(saveVM.ErrorHint?.Text ?? string.Empty); - } - - if (saveVM is { HasError: true, HasWarning: true }) - { - description.Append("\n"); - } - - if (saveVM.HasWarning) - { - description.Append(saveVM.WarningHint?.Text ?? string.Empty); - } - - description.Append("\n\n"); - description.Append(new BUTRTextObject("{=MlYQ0uX7}An unstable experience could occur.")); description.Append("\n"); - description.Append(new BUTRTextObject("{=qvzptzrE}Do you wish to continue loading the save?")); + } - MessageBox?.Show(new BUTRTextObject("{=dDprK7Mz}WARNING").ToString(), description.ToString(), () => ExecuteStartGame(ViewModel, 0), null); - return; + if (saveVM.HasWarning) + { + description.Append(saveVM.WarningHint?.Text ?? string.Empty); } - ExecuteStartGame(ViewModel, 0); + description.Append("\n\n"); + description.Append(new BUTRTextObject("{=MlYQ0uX7}An unstable experience could occur.")); + description.Append("\n"); + description.Append(new BUTRTextObject("{=qvzptzrE}Do you wish to continue loading the save?")); + + MessageBox?.Show(new BUTRTextObject("{=dDprK7Mz}WARNING").ToString(), description.ToString(), () => ExecuteStartGame(ViewModel, 0), null); return; } - if (mode == 1) - { - _launcherManagerHandler.SetGameParameterContinueLastSaveFile(true); + ExecuteStartGame(ViewModel, 0); + return; + } - ExecuteStartGame(ViewModel, 1); - return; - } + if (mode == 1) + { + _launcherManagerHandler.SetGameParameterContinueLastSaveFile(true); - ExecuteStartGame(ViewModel, mode); + ExecuteStartGame(ViewModel, 1); + return; } + + ExecuteStartGame(ViewModel, mode); } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Options/LauncherExData.cs b/src/Bannerlord.LauncherEx/Options/LauncherExData.cs index dfe853f..2c187af 100644 --- a/src/Bannerlord.LauncherEx/Options/LauncherExData.cs +++ b/src/Bannerlord.LauncherEx/Options/LauncherExData.cs @@ -3,62 +3,61 @@ using System.Xml; using System.Xml.Serialization; -namespace Bannerlord.LauncherEx.Options +namespace Bannerlord.LauncherEx.Options; + +public sealed class LauncherExData { - public sealed class LauncherExData + public static LauncherExData? FromUserDataXml(string path) { - public static LauncherExData? FromUserDataXml(string path) + var xmlSerializer = new XmlSerializer(typeof(LauncherExData), new XmlRootAttribute("UserData")); + try { - var xmlSerializer = new XmlSerializer(typeof(LauncherExData), new XmlRootAttribute("UserData")); - try - { - using var xmlReader = XmlReader.Create(path); - return (LauncherExData) xmlSerializer.Deserialize(xmlReader); - } - catch (Exception e) - { - Trace.WriteLine(e); - return null; - } + using var xmlReader = XmlReader.Create(path); + return (LauncherExData) xmlSerializer.Deserialize(xmlReader); } - - public bool AutomaticallyCheckForUpdates { get; set; } - public bool FixCommonIssues { get; set; } - public bool CompactModuleList { get; set; } - public bool DisableBinaryCheck { get; set; } - public bool HideRandomImage { get; set; } - public bool BetaSorting { get; set; } - public bool BigMode { get; set; } - public bool EnableDPIScaling { get; set; } - public bool DisableCrashHandlerWhenDebuggerIsAttached { get; set; } - public bool DisableCatchAutoGenExceptions { get; set; } - public bool UseVanillaCrashHandler { get; set; } - - public LauncherExData() { } - public LauncherExData( - bool automaticallyCheckForUpdates, - bool fixCommonIssues, - bool compactModuleList, - bool hideRandomImage, - bool disableBinaryCheck, - bool betaSorting, - bool bigMode, - bool enableDPIScaling, - bool disableCrashHandlerWhenDebuggerIsAttached, - bool disableCatchAutoGenExceptions, - bool useVanillaCrashHandler) + catch (Exception e) { - AutomaticallyCheckForUpdates = automaticallyCheckForUpdates; - FixCommonIssues = fixCommonIssues; - CompactModuleList = compactModuleList; - DisableBinaryCheck = disableBinaryCheck; - HideRandomImage = hideRandomImage; - BetaSorting = betaSorting; - BigMode = bigMode; - EnableDPIScaling = enableDPIScaling; - DisableCrashHandlerWhenDebuggerIsAttached = disableCrashHandlerWhenDebuggerIsAttached; - DisableCatchAutoGenExceptions = disableCatchAutoGenExceptions; - UseVanillaCrashHandler = useVanillaCrashHandler; + Trace.WriteLine(e); + return null; } } + + public bool AutomaticallyCheckForUpdates { get; set; } + public bool FixCommonIssues { get; set; } + public bool CompactModuleList { get; set; } + public bool DisableBinaryCheck { get; set; } + public bool HideRandomImage { get; set; } + public bool BetaSorting { get; set; } + public bool BigMode { get; set; } + public bool EnableDPIScaling { get; set; } + public bool DisableCrashHandlerWhenDebuggerIsAttached { get; set; } + public bool DisableCatchAutoGenExceptions { get; set; } + public bool UseVanillaCrashHandler { get; set; } + + public LauncherExData() { } + public LauncherExData( + bool automaticallyCheckForUpdates, + bool fixCommonIssues, + bool compactModuleList, + bool hideRandomImage, + bool disableBinaryCheck, + bool betaSorting, + bool bigMode, + bool enableDPIScaling, + bool disableCrashHandlerWhenDebuggerIsAttached, + bool disableCatchAutoGenExceptions, + bool useVanillaCrashHandler) + { + AutomaticallyCheckForUpdates = automaticallyCheckForUpdates; + FixCommonIssues = fixCommonIssues; + CompactModuleList = compactModuleList; + DisableBinaryCheck = disableBinaryCheck; + HideRandomImage = hideRandomImage; + BetaSorting = betaSorting; + BigMode = bigMode; + EnableDPIScaling = enableDPIScaling; + DisableCrashHandlerWhenDebuggerIsAttached = disableCrashHandlerWhenDebuggerIsAttached; + DisableCatchAutoGenExceptions = disableCatchAutoGenExceptions; + UseVanillaCrashHandler = useVanillaCrashHandler; + } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Patches/LauncherConfirmStartVMPatch.cs b/src/Bannerlord.LauncherEx/Patches/LauncherConfirmStartVMPatch.cs index 94f38b3..854ebc4 100644 --- a/src/Bannerlord.LauncherEx/Patches/LauncherConfirmStartVMPatch.cs +++ b/src/Bannerlord.LauncherEx/Patches/LauncherConfirmStartVMPatch.cs @@ -5,23 +5,22 @@ using TaleWorlds.MountAndBlade.Launcher.Library; -namespace Bannerlord.LauncherEx.Patches +namespace Bannerlord.LauncherEx.Patches; + +internal static class LauncherConfirmStartVMPatch { - internal static class LauncherConfirmStartVMPatch + public static bool Enable(Harmony harmony) { - public static bool Enable(Harmony harmony) - { - var res1 = harmony.TryPatch( - AccessTools2.Method(typeof(LauncherConfirmStartVM), "EnableWith"), - prefix: AccessTools2.DeclaredMethod(typeof(LauncherConfirmStartVMPatch), nameof(EnableWithPrefix))); + var res1 = harmony.TryPatch( + AccessTools2.Method(typeof(LauncherConfirmStartVM), "EnableWith"), + prefix: AccessTools2.DeclaredMethod(typeof(LauncherConfirmStartVMPatch), nameof(EnableWithPrefix))); - return res1; - } + return res1; + } - public static bool EnableWithPrefix(Action? ____onConfirm) - { - ____onConfirm?.Invoke(); - return false; - } + public static bool EnableWithPrefix(Action? ____onConfirm) + { + ____onConfirm?.Invoke(); + return false; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Patches/LauncherModsVMPatch.cs b/src/Bannerlord.LauncherEx/Patches/LauncherModsVMPatch.cs index 4e0a659..a80f66a 100644 --- a/src/Bannerlord.LauncherEx/Patches/LauncherModsVMPatch.cs +++ b/src/Bannerlord.LauncherEx/Patches/LauncherModsVMPatch.cs @@ -7,31 +7,30 @@ using TaleWorlds.MountAndBlade.Launcher.Library; using TaleWorlds.MountAndBlade.Launcher.Library.UserDatas; -namespace Bannerlord.LauncherEx.Patches +namespace Bannerlord.LauncherEx.Patches; + +internal static class LauncherModsVMPatch { - internal static class LauncherModsVMPatch + private static readonly AccessTools.FieldRef? _userData = + AccessTools2.FieldRefAccess("_userData"); + + public static bool Enable(Harmony harmony) { - private static readonly AccessTools.FieldRef? _userData = - AccessTools2.FieldRefAccess("_userData"); + var res1 = harmony.TryPatch( + AccessTools2.Method(typeof(LauncherModsVM), "LoadSubModules"), + prefix: AccessTools2.DeclaredMethod(typeof(LauncherModsVMPatch), nameof(LoadSubModulesPrefix))); - public static bool Enable(Harmony harmony) - { - var res1 = harmony.TryPatch( - AccessTools2.Method(typeof(LauncherModsVM), "LoadSubModules"), - prefix: AccessTools2.DeclaredMethod(typeof(LauncherModsVMPatch), nameof(LoadSubModulesPrefix))); + return true; + } + public static bool LoadSubModulesPrefix(LauncherModsVM __instance) + { + if (_userData is null) return true; - } - - public static bool LoadSubModulesPrefix(LauncherModsVM __instance) - { - if (_userData is null) - return true; - if (__instance.GetMixin() is { } mixin) - mixin.Initialize(); + if (__instance.GetMixin() is { } mixin) + mixin.Initialize(); - return false; - } + return false; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Patches/LauncherUIPatch.cs b/src/Bannerlord.LauncherEx/Patches/LauncherUIPatch.cs index 4a7bb1b..b70b34a 100644 --- a/src/Bannerlord.LauncherEx/Patches/LauncherUIPatch.cs +++ b/src/Bannerlord.LauncherEx/Patches/LauncherUIPatch.cs @@ -12,68 +12,67 @@ using TaleWorlds.MountAndBlade.Launcher.Library; using TaleWorlds.MountAndBlade.Launcher.Library.UserDatas; -namespace Bannerlord.LauncherEx.Patches +namespace Bannerlord.LauncherEx.Patches; + +internal static class LauncherUIPatch { - internal static class LauncherUIPatch + public static bool Enable(Harmony harmony) { - public static bool Enable(Harmony harmony) - { - var res1 = harmony.TryPatch( - AccessTools2.DeclaredMethod(typeof(LauncherUI), "Initialize"), - postfix: AccessTools2.DeclaredMethod(typeof(LauncherUIPatch), nameof(InitializePostfix))); - if (!res1) return false; + var res1 = harmony.TryPatch( + AccessTools2.DeclaredMethod(typeof(LauncherUI), "Initialize"), + postfix: AccessTools2.DeclaredMethod(typeof(LauncherUIPatch), nameof(InitializePostfix))); + if (!res1) return false; - var res2 = harmony.TryPatch( - AccessTools2.DeclaredMethod(typeof(LauncherUI), "Update"), - postfix: AccessTools2.DeclaredMethod(typeof(LauncherUIPatch), nameof(UpdatePostfix))); - if (!res2) return false; + var res2 = harmony.TryPatch( + AccessTools2.DeclaredMethod(typeof(LauncherUI), "Update"), + postfix: AccessTools2.DeclaredMethod(typeof(LauncherUIPatch), nameof(UpdatePostfix))); + if (!res2) return false; - var res3 = harmony.TryPatch( - AccessTools2.DeclaredPropertyGetter(typeof(LauncherUI), "AdditionalArgs"), - postfix: AccessTools2.DeclaredMethod(typeof(LauncherUIPatch), nameof(AdditionalArgsPostfix))); - if (!res3) return false; + var res3 = harmony.TryPatch( + AccessTools2.DeclaredPropertyGetter(typeof(LauncherUI), "AdditionalArgs"), + postfix: AccessTools2.DeclaredMethod(typeof(LauncherUIPatch), nameof(AdditionalArgsPostfix))); + if (!res3) return false; - return true; - } + return true; + } - [MethodImpl(MethodImplOptions.NoInlining)] - private static void InitializePostfix(GauntletMovie ____movie, LauncherVM ____viewModel, UserDataManager ____userDataManager) - { - BUTRLauncherManagerHandler.Initialize(____userDataManager); + [MethodImpl(MethodImplOptions.NoInlining)] + private static void InitializePostfix(GauntletMovie ____movie, LauncherVM ____viewModel, UserDataManager ____userDataManager) + { + BUTRLauncherManagerHandler.Initialize(____userDataManager); - // Add to the existing VM our own properties - MixinManager.AddMixins(____viewModel); - ____movie.RefreshDataSource(____viewModel); - } + // Add to the existing VM our own properties + MixinManager.AddMixins(____viewModel); + ____movie.RefreshDataSource(____viewModel); + } - private static void UpdatePostfix(UIContext ____context) + private static void UpdatePostfix(UIContext ____context) + { + if (Input.InputManager is BUTRInputManager butrInputManager) { - if (Input.InputManager is BUTRInputManager butrInputManager) + if (____context.EventManager?.FocusedWidget is { } focusedWidget) { - if (____context.EventManager?.FocusedWidget is { } focusedWidget) - { - butrInputManager.Update(); + butrInputManager.Update(); - focusedWidget.HandleInput(butrInputManager.ReleasedChars); - } - } - else - { - Input.Initialize(new BUTRInputManager(Input.InputManager), null); + focusedWidget.HandleInput(butrInputManager.ReleasedChars); } } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static void AdditionalArgsPostfix() + else { - if (Input.InputManager is BUTRInputManager butrInputManager) - { - Input.Initialize(butrInputManager.InputManager, null); - butrInputManager.Dispose(); - } + Input.Initialize(new BUTRInputManager(Input.InputManager), null); } + } - [MethodImpl(MethodImplOptions.NoInlining)] - private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; + [MethodImpl(MethodImplOptions.NoInlining)] + private static void AdditionalArgsPostfix() + { + if (Input.InputManager is BUTRInputManager butrInputManager) + { + Input.Initialize(butrInputManager.InputManager, null); + butrInputManager.Dispose(); + } } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Patches/LauncherVMPatch.cs b/src/Bannerlord.LauncherEx/Patches/LauncherVMPatch.cs index 1a6913d..fbbbb73 100644 --- a/src/Bannerlord.LauncherEx/Patches/LauncherVMPatch.cs +++ b/src/Bannerlord.LauncherEx/Patches/LauncherVMPatch.cs @@ -11,52 +11,51 @@ using TaleWorlds.MountAndBlade.Launcher.Library; using TaleWorlds.MountAndBlade.Launcher.Library.UserDatas; -namespace Bannerlord.LauncherEx.Patches +namespace Bannerlord.LauncherEx.Patches; + +internal static class LauncherVMPatch { - internal static class LauncherVMPatch + public static bool Enable(Harmony harmony) { - public static bool Enable(Harmony harmony) - { - var res1 = harmony.TryPatch( - AccessTools2.Method(typeof(LauncherVM), "ExecuteConfirmUnverifiedDLLStart"), - transpiler: AccessTools2.DeclaredMethod(typeof(LauncherVMPatch), nameof(BlankTranspiler))); - if (!res1) return false; - - var res2 = harmony.TryPatch( - AccessTools2.Method(typeof(LauncherVM), "GetApplicationVersionOfModule"), - prefix: AccessTools2.DeclaredMethod(typeof(LauncherVMPatch), nameof(GetApplicationVersionOfModulePrefix))); - if (!res2) return false; - - var res3 = harmony.TryPatch( - AccessTools2.Method(typeof(LauncherVM), "UpdateAndSaveUserModsData"), - prefix: AccessTools2.DeclaredMethod(typeof(LauncherVMPatch), nameof(UpdateAndSaveUserModsDataPrefix))); - if (!res3) return false; - - // Preventing inlining ExecuteConfirmUnverifiedDLLStart - harmony.TryPatch( - AccessTools2.Constructor(typeof(LauncherVM), new[] { typeof(UserDataManager), typeof(Action), typeof(Action) }), - transpiler: AccessTools2.DeclaredMethod(typeof(LauncherVMPatch), nameof(BlankTranspiler))); - // Preventing inlining ExecuteConfirmUnverifiedDLLStart - - return true; - } + var res1 = harmony.TryPatch( + AccessTools2.Method(typeof(LauncherVM), "ExecuteConfirmUnverifiedDLLStart"), + transpiler: AccessTools2.DeclaredMethod(typeof(LauncherVMPatch), nameof(BlankTranspiler))); + if (!res1) return false; + + var res2 = harmony.TryPatch( + AccessTools2.Method(typeof(LauncherVM), "GetApplicationVersionOfModule"), + prefix: AccessTools2.DeclaredMethod(typeof(LauncherVMPatch), nameof(GetApplicationVersionOfModulePrefix))); + if (!res2) return false; + + var res3 = harmony.TryPatch( + AccessTools2.Method(typeof(LauncherVM), "UpdateAndSaveUserModsData"), + prefix: AccessTools2.DeclaredMethod(typeof(LauncherVMPatch), nameof(UpdateAndSaveUserModsDataPrefix))); + if (!res3) return false; + + // Preventing inlining ExecuteConfirmUnverifiedDLLStart + harmony.TryPatch( + AccessTools2.Constructor(typeof(LauncherVM), new[] { typeof(UserDataManager), typeof(Action), typeof(Action) }), + transpiler: AccessTools2.DeclaredMethod(typeof(LauncherVMPatch), nameof(BlankTranspiler))); + // Preventing inlining ExecuteConfirmUnverifiedDLLStart + + return true; + } - // Disable Vanilla's saving - public static bool UpdateAndSaveUserModsDataPrefix() => false; + // Disable Vanilla's saving + public static bool UpdateAndSaveUserModsDataPrefix() => false; - [MethodImpl(MethodImplOptions.NoOptimization)] - public static bool GetApplicationVersionOfModulePrefix(string id, ref ApplicationVersion __result) + [MethodImpl(MethodImplOptions.NoOptimization)] + public static bool GetApplicationVersionOfModulePrefix(string id, ref ApplicationVersion __result) + { + if (FeatureIds.LauncherFeatures.Contains(id)) { - if (FeatureIds.LauncherFeatures.Contains(id)) - { - __result = ApplicationVersion.Empty; - return false; - } - - return true; + __result = ApplicationVersion.Empty; + return false; } - [MethodImpl(MethodImplOptions.NoInlining)] - private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; + return true; } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Patches/ProgramPatch.cs b/src/Bannerlord.LauncherEx/Patches/ProgramPatch.cs index 25c8f3c..017a2a8 100644 --- a/src/Bannerlord.LauncherEx/Patches/ProgramPatch.cs +++ b/src/Bannerlord.LauncherEx/Patches/ProgramPatch.cs @@ -5,29 +5,28 @@ using TaleWorlds.MountAndBlade.Launcher.Library; -namespace Bannerlord.LauncherEx.Patches +namespace Bannerlord.LauncherEx.Patches; + +internal static class ProgramPatch { - internal static class ProgramPatch + public static bool Enable(Harmony harmony) { - public static bool Enable(Harmony harmony) - { - var res1 = harmony.TryPatch( - AccessTools2.Method(typeof(Program), "AuxFinalize"), - postfix: AccessTools2.DeclaredMethod(typeof(ProgramPatch), nameof(AuxFinalizePostfix))); - if (!res1) return false; + var res1 = harmony.TryPatch( + AccessTools2.Method(typeof(Program), "AuxFinalize"), + postfix: AccessTools2.DeclaredMethod(typeof(ProgramPatch), nameof(AuxFinalizePostfix))); + if (!res1) return false; - return true; - } + return true; + } - private static void AuxFinalizePostfix() + private static void AuxFinalizePostfix() + { + if (LauncherSettings.FixCommonIssues) { - if (LauncherSettings.FixCommonIssues) - { - BUTRLauncherManagerHandler.Default.CheckForRootHarmony(); - } - - Manager.Disable(); + BUTRLauncherManagerHandler.Default.CheckForRootHarmony(); } + Manager.Disable(); } + } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Patches/UserDataManagerPatch.cs b/src/Bannerlord.LauncherEx/Patches/UserDataManagerPatch.cs index edfcbbe..a98ef13 100644 --- a/src/Bannerlord.LauncherEx/Patches/UserDataManagerPatch.cs +++ b/src/Bannerlord.LauncherEx/Patches/UserDataManagerPatch.cs @@ -14,95 +14,94 @@ using TaleWorlds.MountAndBlade.Launcher.Library.UserDatas; -namespace Bannerlord.LauncherEx.Patches +namespace Bannerlord.LauncherEx.Patches; + +internal static class UserDataManagerPatch { - internal static class UserDataManagerPatch + public static bool Enable(Harmony harmony) { - public static bool Enable(Harmony harmony) - { - var res1 = harmony.TryPatch( - AccessTools2.DeclaredMethod(typeof(UserDataManager), "LoadUserData"), - prefix: AccessTools2.DeclaredMethod(typeof(UserDataManagerPatch), nameof(LoadUserDataPrefix))); - if (!res1) return false; + var res1 = harmony.TryPatch( + AccessTools2.DeclaredMethod(typeof(UserDataManager), "LoadUserData"), + prefix: AccessTools2.DeclaredMethod(typeof(UserDataManagerPatch), nameof(LoadUserDataPrefix))); + if (!res1) return false; - var res2 = harmony.TryPatch( - AccessTools2.DeclaredMethod(typeof(UserDataManager), "SaveUserData"), - postfix: AccessTools2.DeclaredMethod(typeof(UserDataManagerPatch), nameof(SaveUserDataPostfix))); - if (!res2) return false; + var res2 = harmony.TryPatch( + AccessTools2.DeclaredMethod(typeof(UserDataManager), "SaveUserData"), + postfix: AccessTools2.DeclaredMethod(typeof(UserDataManagerPatch), nameof(SaveUserDataPostfix))); + if (!res2) return false; - return true; - } + return true; + } - [SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "")] - [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "For Resharper")] - [SuppressMessage("ReSharper", "InconsistentNaming")] - [SuppressMessage("ReSharper", "RedundantAssignment")] - [MethodImpl(MethodImplOptions.NoInlining)] - private static bool LoadUserDataPrefix(string ____filePath) + [SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "")] + [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "For Resharper")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + [SuppressMessage("ReSharper", "RedundantAssignment")] + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool LoadUserDataPrefix(string ____filePath) + { + if (!File.Exists(____filePath)) { - if (!File.Exists(____filePath)) - { - return true; - } - - var userDataOptions = LauncherExData.FromUserDataXml(____filePath) ?? new(); - LauncherSettings.AutomaticallyCheckForUpdates = userDataOptions.AutomaticallyCheckForUpdates; - LauncherSettings.FixCommonIssues = userDataOptions.FixCommonIssues; - LauncherSettings.CompactModuleList = userDataOptions.CompactModuleList; - LauncherSettings.HideRandomImage = userDataOptions.HideRandomImage; - LauncherSettings.DisableBinaryCheck = userDataOptions.DisableBinaryCheck; - LauncherSettings.BetaSorting = userDataOptions.BetaSorting; - LauncherSettings.BigMode = userDataOptions.BigMode; - LauncherSettings.EnableDPIScaling = userDataOptions.EnableDPIScaling; - LauncherSettings.DisableCrashHandlerWhenDebuggerIsAttached = userDataOptions.DisableCrashHandlerWhenDebuggerIsAttached; - LauncherSettings.DisableCatchAutoGenExceptions = userDataOptions.DisableCatchAutoGenExceptions; - LauncherSettings.UseVanillaCrashHandler = userDataOptions.UseVanillaCrashHandler; - return true; } - [SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "")] - [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "For Resharper")] - [SuppressMessage("ReSharper", "InconsistentNaming")] - [SuppressMessage("ReSharper", "RedundantAssignment")] - [MethodImpl(MethodImplOptions.NoInlining)] - private static void SaveUserDataPostfix(string ____filePath) - { - var xDoc = new XmlDocument(); - xDoc.Load(____filePath); - var rootNode = xDoc.DocumentElement!; + var userDataOptions = LauncherExData.FromUserDataXml(____filePath) ?? new(); + LauncherSettings.AutomaticallyCheckForUpdates = userDataOptions.AutomaticallyCheckForUpdates; + LauncherSettings.FixCommonIssues = userDataOptions.FixCommonIssues; + LauncherSettings.CompactModuleList = userDataOptions.CompactModuleList; + LauncherSettings.HideRandomImage = userDataOptions.HideRandomImage; + LauncherSettings.DisableBinaryCheck = userDataOptions.DisableBinaryCheck; + LauncherSettings.BetaSorting = userDataOptions.BetaSorting; + LauncherSettings.BigMode = userDataOptions.BigMode; + LauncherSettings.EnableDPIScaling = userDataOptions.EnableDPIScaling; + LauncherSettings.DisableCrashHandlerWhenDebuggerIsAttached = userDataOptions.DisableCrashHandlerWhenDebuggerIsAttached; + LauncherSettings.DisableCatchAutoGenExceptions = userDataOptions.DisableCatchAutoGenExceptions; + LauncherSettings.UseVanillaCrashHandler = userDataOptions.UseVanillaCrashHandler; - var xmlSerializer = new XmlSerializer(typeof(LauncherExData)); - using var xout = new StringWriter(); - using var writer = XmlWriter.Create(xout, new XmlWriterSettings { OmitXmlDeclaration = true }); - try - { - xmlSerializer.Serialize(writer, new LauncherExData( - LauncherSettings.AutomaticallyCheckForUpdates, - LauncherSettings.FixCommonIssues, - LauncherSettings.CompactModuleList, - LauncherSettings.HideRandomImage, - LauncherSettings.DisableBinaryCheck, - LauncherSettings.BetaSorting, - LauncherSettings.BigMode, - LauncherSettings.EnableDPIScaling, - LauncherSettings.DisableCrashHandlerWhenDebuggerIsAttached, - LauncherSettings.DisableCatchAutoGenExceptions, - LauncherSettings.UseVanillaCrashHandler)); - } - catch (Exception value) - { - Trace.WriteLine(value); - } + return true; + } - var xfrag = xDoc.CreateDocumentFragment(); - xfrag.InnerXml = xout.ToString(); - foreach (var element in xfrag.FirstChild.ChildNodes.OfType().ToList()) - { - rootNode.AppendChild(element); - } + [SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "")] + [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "For Resharper")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + [SuppressMessage("ReSharper", "RedundantAssignment")] + [MethodImpl(MethodImplOptions.NoInlining)] + private static void SaveUserDataPostfix(string ____filePath) + { + var xDoc = new XmlDocument(); + xDoc.Load(____filePath); + var rootNode = xDoc.DocumentElement!; - xDoc.Save(____filePath); + var xmlSerializer = new XmlSerializer(typeof(LauncherExData)); + using var xout = new StringWriter(); + using var writer = XmlWriter.Create(xout, new XmlWriterSettings { OmitXmlDeclaration = true }); + try + { + xmlSerializer.Serialize(writer, new LauncherExData( + LauncherSettings.AutomaticallyCheckForUpdates, + LauncherSettings.FixCommonIssues, + LauncherSettings.CompactModuleList, + LauncherSettings.HideRandomImage, + LauncherSettings.DisableBinaryCheck, + LauncherSettings.BetaSorting, + LauncherSettings.BigMode, + LauncherSettings.EnableDPIScaling, + LauncherSettings.DisableCrashHandlerWhenDebuggerIsAttached, + LauncherSettings.DisableCatchAutoGenExceptions, + LauncherSettings.UseVanillaCrashHandler)); + } + catch (Exception value) + { + Trace.WriteLine(value); } + + var xfrag = xDoc.CreateDocumentFragment(); + xfrag.InnerXml = xout.ToString(); + foreach (var element in xfrag.FirstChild.ChildNodes.OfType().ToList()) + { + rootNode.AppendChild(element); + } + + xDoc.Save(____filePath); } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Patches/ViewModelPatch.cs b/src/Bannerlord.LauncherEx/Patches/ViewModelPatch.cs index 1db074d..1f1c2a9 100644 --- a/src/Bannerlord.LauncherEx/Patches/ViewModelPatch.cs +++ b/src/Bannerlord.LauncherEx/Patches/ViewModelPatch.cs @@ -10,30 +10,29 @@ using TaleWorlds.Library; -namespace Bannerlord.LauncherEx.Patches +namespace Bannerlord.LauncherEx.Patches; + +internal static class ViewModelPatch { - internal static class ViewModelPatch + public static bool Enable(Harmony harmony) { - public static bool Enable(Harmony harmony) - { - var res1 = harmony.TryPatch( - AccessTools2.DeclaredConstructor(typeof(ViewModel)), - prefix: AccessTools2.DeclaredMethod(typeof(ViewModelPatch), nameof(ViewModelCtorPrefix))); - if (!res1) return false; + var res1 = harmony.TryPatch( + AccessTools2.DeclaredConstructor(typeof(ViewModel)), + prefix: AccessTools2.DeclaredMethod(typeof(ViewModelPatch), nameof(ViewModelCtorPrefix))); + if (!res1) return false; - return true; - } + return true; + } - private static bool ViewModelCtorPrefix(ViewModel __instance, ref Type ____type, ref object ____propertiesAndMethods) + private static bool ViewModelCtorPrefix(ViewModel __instance, ref Type ____type, ref object ____propertiesAndMethods) + { + if (__instance is BUTRViewModel && ViewModelExtensions.DataSourceTypeBindingPropertiesCollectionCtor is { } ctor) { - if (__instance is BUTRViewModel && ViewModelExtensions.DataSourceTypeBindingPropertiesCollectionCtor is { } ctor) - { - ____type = __instance.GetType(); - ____propertiesAndMethods = ctor(new Dictionary(), new Dictionary()); - - return false; - } - return true; + ____type = __instance.GetType(); + ____propertiesAndMethods = ctor(new Dictionary(), new Dictionary()); + + return false; } + return true; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Patches/WidgetPrefabPatch.cs b/src/Bannerlord.LauncherEx/Patches/WidgetPrefabPatch.cs index 3b0666c..1a0f5a6 100644 --- a/src/Bannerlord.LauncherEx/Patches/WidgetPrefabPatch.cs +++ b/src/Bannerlord.LauncherEx/Patches/WidgetPrefabPatch.cs @@ -14,188 +14,188 @@ using TaleWorlds.GauntletUI.PrefabSystem; -namespace Bannerlord.LauncherEx.Patches +namespace Bannerlord.LauncherEx.Patches; + +// https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Patches/WidgetPrefabPatch.cs +internal static class WidgetPrefabPatch { - // https://github.com/BUTR/Bannerlord.UIExtenderEx/blob/dev/src/Bannerlord.UIExtenderEx/Patches/WidgetPrefabPatch.cs - internal static class WidgetPrefabPatch + public static bool Enable(Harmony harmony) { - public static bool Enable(Harmony harmony) - { - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension31.Movie, UILauncherPrefabExtension31.XPath, new UILauncherPrefabExtension31()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension32.Movie, UILauncherPrefabExtension32.XPath, new UILauncherPrefabExtension32()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension34.Movie, UILauncherPrefabExtension34.XPath, new UILauncherPrefabExtension34()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension35.Movie, UILauncherPrefabExtension35.XPath, new UILauncherPrefabExtension35()); - - // Options - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension3.Movie, UILauncherPrefabExtension3.XPath, new UILauncherPrefabExtension3()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension4.Movie, UILauncherPrefabExtension4.XPath, new UILauncherPrefabExtension4()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension5.Movie, UILauncherPrefabExtension5.XPath, new UILauncherPrefabExtension5()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension6.Movie, UILauncherPrefabExtension6.XPath, new UILauncherPrefabExtension6()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension20.Movie, UILauncherPrefabExtension20.XPath, new UILauncherPrefabExtension20()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension21.Movie, UILauncherPrefabExtension21.XPath, new UILauncherPrefabExtension21()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension7.Movie, UILauncherPrefabExtension7.XPath, new UILauncherPrefabExtension7()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension22.Movie, UILauncherPrefabExtension22.XPath, new UILauncherPrefabExtension22()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension23.Movie, UILauncherPrefabExtension23.XPath, new UILauncherPrefabExtension23()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension10.Movie, UILauncherPrefabExtension10.XPath, new UILauncherPrefabExtension10()); - // Options - - // Saves - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension24.Movie, UILauncherPrefabExtension24.XPath, new UILauncherPrefabExtension24()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension25.Movie, UILauncherPrefabExtension25.XPath, new UILauncherPrefabExtension25()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension26.Movie, UILauncherPrefabExtension26.XPath, new UILauncherPrefabExtension26()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension33.Movie, UILauncherPrefabExtension33.XPath, new UILauncherPrefabExtension33()); - // Saves - - // News - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension8.Movie, UILauncherPrefabExtension8.XPath, new UILauncherPrefabExtension8()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension12.Movie, UILauncherPrefabExtension12.XPath, new UILauncherPrefabExtension12()); - // News - - // Mods - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension9.Movie, UILauncherPrefabExtension9.XPath, new UILauncherPrefabExtension9()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension13.Movie, UILauncherPrefabExtension13.XPath, new UILauncherPrefabExtension13()); - // Mods - - - // Import/Export - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension14.Movie, UILauncherPrefabExtension14.XPath, new UILauncherPrefabExtension14()); - // Import/Export - - // Minor - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension1.Movie, UILauncherPrefabExtension1.XPath, new UILauncherPrefabExtension1()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension2.Movie, UILauncherPrefabExtension2.XPath, new UILauncherPrefabExtension2()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension15.Movie, UILauncherPrefabExtension15.XPath, new UILauncherPrefabExtension15()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension16.Movie, UILauncherPrefabExtension16.XPath, new UILauncherPrefabExtension16()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension17.Movie, UILauncherPrefabExtension17.XPath, new UILauncherPrefabExtension17()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension18.Movie, UILauncherPrefabExtension18.XPath, new UILauncherPrefabExtension18()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension19.Movie, UILauncherPrefabExtension19.XPath, new UILauncherPrefabExtension19()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension28.Movie, UILauncherPrefabExtension28.XPath, new UILauncherPrefabExtension28()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension29.Movie, UILauncherPrefabExtension29.XPath, new UILauncherPrefabExtension29()); - PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension30.Movie, UILauncherPrefabExtension30.XPath, new UILauncherPrefabExtension30()); - // Minor - - // Compact - PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension4.Movie, ModuleTuplePrefabExtension4.XPath, new ModuleTuplePrefabExtension4()); - PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension6.Movie, ModuleTuplePrefabExtension6.XPath, new ModuleTuplePrefabExtension6()); - PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension7.Movie, ModuleTuplePrefabExtension7.XPath, new ModuleTuplePrefabExtension7()); - - PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension8.Movie, ModuleTuplePrefabExtension8.XPath, new ModuleTuplePrefabExtension8()); - PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension9.Movie, ModuleTuplePrefabExtension9.XPath, new ModuleTuplePrefabExtension9()); - PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension10.Movie, ModuleTuplePrefabExtension10.XPath, new ModuleTuplePrefabExtension10()); - PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension11.Movie, ModuleTuplePrefabExtension11.XPath, new ModuleTuplePrefabExtension11()); - PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension12.Movie, ModuleTuplePrefabExtension12.XPath, new ModuleTuplePrefabExtension12()); - PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension13.Movie, ModuleTuplePrefabExtension13.XPath, new ModuleTuplePrefabExtension13()); - PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension14.Movie, ModuleTuplePrefabExtension14.XPath, new ModuleTuplePrefabExtension14()); - // Compact - - var res1 = harmony.TryPatch( - AccessTools2.DeclaredMethod(typeof(WidgetPrefab), "LoadFrom"), - transpiler: AccessTools2.DeclaredMethod(typeof(WidgetPrefabPatch), nameof(WidgetPrefab_LoadFrom_Transpiler))); - if (!res1) return false; - - var res2 = harmony.TryCreateReversePatcher( - AccessTools2.DeclaredMethod(typeof(WidgetPrefab), "LoadFrom"), - AccessTools2.DeclaredMethod(typeof(WidgetPrefabPatch), nameof(LoadFromDocument))); - if (res2 is null) return false; - res2.Patch(); - - return true; - } + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension31.Movie, UILauncherPrefabExtension31.XPath, new UILauncherPrefabExtension31()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension32.Movie, UILauncherPrefabExtension32.XPath, new UILauncherPrefabExtension32()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension34.Movie, UILauncherPrefabExtension34.XPath, new UILauncherPrefabExtension34()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension35.Movie, UILauncherPrefabExtension35.XPath, new UILauncherPrefabExtension35()); + + // Options + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension3.Movie, UILauncherPrefabExtension3.XPath, new UILauncherPrefabExtension3()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension4.Movie, UILauncherPrefabExtension4.XPath, new UILauncherPrefabExtension4()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension5.Movie, UILauncherPrefabExtension5.XPath, new UILauncherPrefabExtension5()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension6.Movie, UILauncherPrefabExtension6.XPath, new UILauncherPrefabExtension6()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension20.Movie, UILauncherPrefabExtension20.XPath, new UILauncherPrefabExtension20()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension21.Movie, UILauncherPrefabExtension21.XPath, new UILauncherPrefabExtension21()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension7.Movie, UILauncherPrefabExtension7.XPath, new UILauncherPrefabExtension7()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension22.Movie, UILauncherPrefabExtension22.XPath, new UILauncherPrefabExtension22()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension23.Movie, UILauncherPrefabExtension23.XPath, new UILauncherPrefabExtension23()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension10.Movie, UILauncherPrefabExtension10.XPath, new UILauncherPrefabExtension10()); + // Options + + // Saves + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension24.Movie, UILauncherPrefabExtension24.XPath, new UILauncherPrefabExtension24()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension25.Movie, UILauncherPrefabExtension25.XPath, new UILauncherPrefabExtension25()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension26.Movie, UILauncherPrefabExtension26.XPath, new UILauncherPrefabExtension26()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension33.Movie, UILauncherPrefabExtension33.XPath, new UILauncherPrefabExtension33()); + // Saves + + // News + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension8.Movie, UILauncherPrefabExtension8.XPath, new UILauncherPrefabExtension8()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension12.Movie, UILauncherPrefabExtension12.XPath, new UILauncherPrefabExtension12()); + // News + + // Mods + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension9.Movie, UILauncherPrefabExtension9.XPath, new UILauncherPrefabExtension9()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension13.Movie, UILauncherPrefabExtension13.XPath, new UILauncherPrefabExtension13()); + // Mods + + + // Import/Export + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension14.Movie, UILauncherPrefabExtension14.XPath, new UILauncherPrefabExtension14()); + // Import/Export + + // Minor + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension1.Movie, UILauncherPrefabExtension1.XPath, new UILauncherPrefabExtension1()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension2.Movie, UILauncherPrefabExtension2.XPath, new UILauncherPrefabExtension2()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension15.Movie, UILauncherPrefabExtension15.XPath, new UILauncherPrefabExtension15()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension16.Movie, UILauncherPrefabExtension16.XPath, new UILauncherPrefabExtension16()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension17.Movie, UILauncherPrefabExtension17.XPath, new UILauncherPrefabExtension17()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension18.Movie, UILauncherPrefabExtension18.XPath, new UILauncherPrefabExtension18()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension19.Movie, UILauncherPrefabExtension19.XPath, new UILauncherPrefabExtension19()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension28.Movie, UILauncherPrefabExtension28.XPath, new UILauncherPrefabExtension28()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension29.Movie, UILauncherPrefabExtension29.XPath, new UILauncherPrefabExtension29()); + PrefabExtensionManager.RegisterPatch(UILauncherPrefabExtension30.Movie, UILauncherPrefabExtension30.XPath, new UILauncherPrefabExtension30()); + // Minor + + // Compact + PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension4.Movie, ModuleTuplePrefabExtension4.XPath, new ModuleTuplePrefabExtension4()); + PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension6.Movie, ModuleTuplePrefabExtension6.XPath, new ModuleTuplePrefabExtension6()); + PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension7.Movie, ModuleTuplePrefabExtension7.XPath, new ModuleTuplePrefabExtension7()); + + PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension8.Movie, ModuleTuplePrefabExtension8.XPath, new ModuleTuplePrefabExtension8()); + PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension9.Movie, ModuleTuplePrefabExtension9.XPath, new ModuleTuplePrefabExtension9()); + PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension10.Movie, ModuleTuplePrefabExtension10.XPath, new ModuleTuplePrefabExtension10()); + PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension11.Movie, ModuleTuplePrefabExtension11.XPath, new ModuleTuplePrefabExtension11()); + PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension12.Movie, ModuleTuplePrefabExtension12.XPath, new ModuleTuplePrefabExtension12()); + PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension13.Movie, ModuleTuplePrefabExtension13.XPath, new ModuleTuplePrefabExtension13()); + PrefabExtensionManager.RegisterPatch(ModuleTuplePrefabExtension14.Movie, ModuleTuplePrefabExtension14.XPath, new ModuleTuplePrefabExtension14()); + // Compact + + var res1 = harmony.TryPatch( + AccessTools2.DeclaredMethod(typeof(WidgetPrefab), "LoadFrom"), + transpiler: AccessTools2.DeclaredMethod(typeof(WidgetPrefabPatch), nameof(WidgetPrefab_LoadFrom_Transpiler))); + if (!res1) return false; + + var res2 = harmony.TryCreateReversePatcher( + AccessTools2.DeclaredMethod(typeof(WidgetPrefab), "LoadFrom"), + AccessTools2.DeclaredMethod(typeof(WidgetPrefabPatch), nameof(LoadFromDocument))); + if (res2 is null) return false; + res2.Patch(); + + return true; + } - [MethodImpl(MethodImplOptions.NoInlining)] - private static void ProcessMovie(string path, XmlDocument document) - { - var movieName = Path.GetFileNameWithoutExtension(path); - PrefabExtensionManager.ProcessMovieIfNeeded(movieName, document); - } + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ProcessMovie(string path, XmlDocument document) + { + var movieName = Path.GetFileNameWithoutExtension(path); + PrefabExtensionManager.ProcessMovieIfNeeded(movieName, document); + } - private static int GetWidgetPrefabConstructorIndex(IList instructions, MethodBase originalMethod) - { - var constructor = AccessTools2.DeclaredConstructor(typeof(WidgetPrefab)); + private static int GetWidgetPrefabConstructorIndex(IList instructions, MethodBase originalMethod) + { + var constructor = AccessTools2.DeclaredConstructor(typeof(WidgetPrefab)); - var locals = originalMethod.GetMethodBody()?.LocalVariables; - var widgetPrefabLocal = locals?.FirstOrDefault(x => x.LocalType == typeof(WidgetPrefab)); + var locals = originalMethod.GetMethodBody()?.LocalVariables; + var widgetPrefabLocal = locals?.FirstOrDefault(x => x.LocalType == typeof(WidgetPrefab)); - if (widgetPrefabLocal is null) - return -1; + if (widgetPrefabLocal is null) + return -1; - var constructorIndex = -1; - for (var i = 0; i < instructions.Count - 2; i++) - { - if (instructions[i + 0].opcode != OpCodes.Newobj || !Equals(instructions[i + 0].operand, constructor)) - continue; + var constructorIndex = -1; + for (var i = 0; i < instructions.Count - 2; i++) + { + if (instructions[i + 0].opcode != OpCodes.Newobj || !Equals(instructions[i + 0].operand, constructor)) + continue; - if (!instructions[i + 1].IsStloc()) - continue; + if (!instructions[i + 1].IsStloc()) + continue; - constructorIndex = i; - break; - } - return constructorIndex; + constructorIndex = i; + break; } + return constructorIndex; + } - [MethodImpl(MethodImplOptions.NoInlining)] - private static IEnumerable WidgetPrefab_LoadFrom_Transpiler(IEnumerable instructions, MethodBase method) - { - var instructionsList = instructions.ToList(); + [MethodImpl(MethodImplOptions.NoInlining)] + private static IEnumerable WidgetPrefab_LoadFrom_Transpiler(IEnumerable instructions, MethodBase method) + { + var instructionsList = instructions.ToList(); - IEnumerable ReturnDefault() - { - return instructionsList.AsEnumerable(); - } + IEnumerable ReturnDefault() + { + return instructionsList.AsEnumerable(); + } - var widgetPrefabConstructorIndex = GetWidgetPrefabConstructorIndex(instructionsList, method); - if (widgetPrefabConstructorIndex == -1) - return ReturnDefault(); + var widgetPrefabConstructorIndex = GetWidgetPrefabConstructorIndex(instructionsList, method); + if (widgetPrefabConstructorIndex == -1) + return ReturnDefault(); - // ProcessMovie(path, xmlDocument); - instructionsList.InsertRange(widgetPrefabConstructorIndex + 1, new List + // ProcessMovie(path, xmlDocument); + instructionsList.InsertRange(widgetPrefabConstructorIndex + 1, new List { new (OpCodes.Ldarg_2), new (OpCodes.Ldloc_0), new (OpCodes.Call, AccessTools2.DeclaredMethod(typeof(WidgetPrefabPatch), nameof(ProcessMovie))) }); - return instructionsList.AsEnumerable(); - } + return instructionsList.AsEnumerable(); + } - // We can call a slightly modified native game call this way + // We can call a slightly modified native game call this way + [MethodImpl(MethodImplOptions.NoInlining)] + public static WidgetPrefab? LoadFromDocument(PrefabExtensionContext prefabExtensionContext, WidgetAttributeContext widgetAttributeContext, string path, XmlDocument document) + { + // Replaces reading XML from file with assigning it from the new local variable `XmlDocument document` [MethodImpl(MethodImplOptions.NoInlining)] - public static WidgetPrefab? LoadFromDocument(PrefabExtensionContext prefabExtensionContext, WidgetAttributeContext widgetAttributeContext, string path, XmlDocument document) + static IEnumerable Transpiler(IEnumerable instructions) { - // Replaces reading XML from file with assigning it from the new local variable `XmlDocument document` - [MethodImpl(MethodImplOptions.NoInlining)] - static IEnumerable Transpiler(IEnumerable instructions) - { - var returnNull = new List + var returnNull = new List { new (OpCodes.Ldnull), new (OpCodes.Ret) }.AsEnumerable(); - var instructionsList = instructions.ToList(); + var instructionsList = instructions.ToList(); - var method = AccessTools2.DeclaredMethod(typeof(WidgetPrefab), "LoadFrom"); - var locals = method?.GetMethodBody()?.LocalVariables; - var xmlDocumentLocal = locals?.FirstOrDefault(x => x.LocalType == typeof(XmlDocument)); + var method = AccessTools2.DeclaredMethod(typeof(WidgetPrefab), "LoadFrom"); + var locals = method?.GetMethodBody()?.LocalVariables; + var xmlDocumentLocal = locals?.FirstOrDefault(x => x.LocalType == typeof(XmlDocument)); - if (xmlDocumentLocal is null) - return returnNull; + if (xmlDocumentLocal is null) + return returnNull; - var widgetPrefabConstructorIndex = GetWidgetPrefabConstructorIndex(instructionsList, method!); - if (widgetPrefabConstructorIndex == -1) - return returnNull; + var widgetPrefabConstructorIndex = GetWidgetPrefabConstructorIndex(instructionsList, method!); + if (widgetPrefabConstructorIndex == -1) + return returnNull; - for (var i = 0; i < widgetPrefabConstructorIndex; i++) - { - instructionsList[i] = new CodeInstruction(OpCodes.Nop); - } + for (var i = 0; i < widgetPrefabConstructorIndex; i++) + { + instructionsList[i] = new CodeInstruction(OpCodes.Nop); + } - instructionsList[widgetPrefabConstructorIndex - 2] = new CodeInstruction(OpCodes.Ldarg_S, 3); - instructionsList[widgetPrefabConstructorIndex - 1] = new CodeInstruction(OpCodes.Stloc_S, xmlDocumentLocal.LocalIndex); + instructionsList[widgetPrefabConstructorIndex - 2] = new CodeInstruction(OpCodes.Ldarg_S, 3); + instructionsList[widgetPrefabConstructorIndex - 1] = new CodeInstruction(OpCodes.Stloc_S, xmlDocumentLocal.LocalIndex); - // ProcessMovie(path, xmlDocument); - instructionsList.InsertRange(widgetPrefabConstructorIndex + 1, new List + // ProcessMovie(path, xmlDocument); + instructionsList.InsertRange(widgetPrefabConstructorIndex + 1, new List { new (OpCodes.Ldarg_S, 2), new (OpCodes.Ldloc_S, xmlDocumentLocal.LocalIndex), @@ -203,20 +203,19 @@ static IEnumerable Transpiler(IEnumerable inst }); - return instructionsList.AsEnumerable(); - } + return instructionsList.AsEnumerable(); + } - // make compiler happy - _ = Transpiler(null!); + // make compiler happy + _ = Transpiler(null!); - // make analyzer happy - prefabExtensionContext.AddExtension(null); - widgetAttributeContext.RegisterKeyType(null); - path.Do(null); - document.Validate(null); + // make analyzer happy + prefabExtensionContext.AddExtension(null); + widgetAttributeContext.RegisterKeyType(null); + path.Do(null); + document.Validate(null); - // make compiler happy - return null!; - } + // make compiler happy + return null!; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/PrefabExtensions/ModuleTuplePrefabExtension.Compact.cs b/src/Bannerlord.LauncherEx/PrefabExtensions/ModuleTuplePrefabExtension.Compact.cs index 9e16ea8..52a3ca6 100644 --- a/src/Bannerlord.LauncherEx/PrefabExtensions/ModuleTuplePrefabExtension.Compact.cs +++ b/src/Bannerlord.LauncherEx/PrefabExtensions/ModuleTuplePrefabExtension.Compact.cs @@ -2,94 +2,93 @@ using System.Collections.Generic; -namespace Bannerlord.LauncherEx.PrefabExtensions +namespace Bannerlord.LauncherEx.PrefabExtensions; + +internal sealed class ModuleTuplePrefabExtension4 : PrefabExtensionSetAttributesPatch { - internal sealed class ModuleTuplePrefabExtension4 : PrefabExtensionSetAttributesPatch - { - public static string Movie => "Launcher.Mods.ModuleTuple2"; - public static string XPath => "descendant::ListPanel[@Id='EntryPanel']"; + public static string Movie => "Launcher.Mods.ModuleTuple2"; + public static string XPath => "descendant::ListPanel[@Id='EntryPanel']"; - public override List Attributes => new() - { - new Attribute("SuggestedHeight", LauncherSettings.CompactModuleList ? "24" : "26"), - new Attribute("MarginBottom", LauncherSettings.CompactModuleList ? "2" : "10"), - }; - } - internal sealed class ModuleTuplePrefabExtension6 : PrefabExtensionSetAttributePatch + public override List Attributes => new() { - public static string Movie => "Launcher.Mods.ModuleTuple2"; - public static string XPath => "descendant::TextWidget[@Text='@Name']"; + new Attribute("SuggestedHeight", LauncherSettings.CompactModuleList ? "24" : "26"), + new Attribute("MarginBottom", LauncherSettings.CompactModuleList ? "2" : "10"), + }; +} +internal sealed class ModuleTuplePrefabExtension6 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "Launcher.Mods.ModuleTuple2"; + public static string XPath => "descendant::TextWidget[@Text='@Name']"; - public override string Attribute => "Brush.FontSize"; - public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; - } - internal sealed class ModuleTuplePrefabExtension7 : PrefabExtensionSetAttributePatch - { - public static string Movie => "Launcher.Mods.ModuleTuple2"; - public static string XPath => "descendant::TextWidget[@Text='@VersionText']"; + public override string Attribute => "Brush.FontSize"; + public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; +} +internal sealed class ModuleTuplePrefabExtension7 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "Launcher.Mods.ModuleTuple2"; + public static string XPath => "descendant::TextWidget[@Text='@VersionText']"; - public override string Attribute => "Brush.FontSize"; - public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; - } + public override string Attribute => "Brush.FontSize"; + public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; +} - internal sealed class ModuleTuplePrefabExtension14 : PrefabExtensionSetAttributesPatch - { - public static string Movie => "Launcher.Saves.SaveTuple"; - public static string XPath => "descendant::ListPanel[@Id='EntryPanel']"; +internal sealed class ModuleTuplePrefabExtension14 : PrefabExtensionSetAttributesPatch +{ + public static string Movie => "Launcher.Saves.SaveTuple"; + public static string XPath => "descendant::ListPanel[@Id='EntryPanel']"; - public override List Attributes => new() - { - new Attribute("SuggestedHeight", LauncherSettings.CompactModuleList ? "24" : "26"), - new Attribute("MarginBottom", LauncherSettings.CompactModuleList ? "2" : "10"), - }; - } - internal sealed class ModuleTuplePrefabExtension8 : PrefabExtensionSetAttributePatch + public override List Attributes => new() { - public static string Movie => "Launcher.Saves.SaveTuple"; - public static string XPath => "descendant::TextWidget[@Text='@Name']"; + new Attribute("SuggestedHeight", LauncherSettings.CompactModuleList ? "24" : "26"), + new Attribute("MarginBottom", LauncherSettings.CompactModuleList ? "2" : "10"), + }; +} +internal sealed class ModuleTuplePrefabExtension8 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "Launcher.Saves.SaveTuple"; + public static string XPath => "descendant::TextWidget[@Text='@Name']"; - public override string Attribute => "Brush.FontSize"; - public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; - } - internal sealed class ModuleTuplePrefabExtension9 : PrefabExtensionSetAttributePatch - { - public static string Movie => "Launcher.Saves.SaveTuple"; - public static string XPath => "descendant::TextWidget[@Text='@Version']"; + public override string Attribute => "Brush.FontSize"; + public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; +} +internal sealed class ModuleTuplePrefabExtension9 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "Launcher.Saves.SaveTuple"; + public static string XPath => "descendant::TextWidget[@Text='@Version']"; - public override string Attribute => "Brush.FontSize"; - public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; - } - internal sealed class ModuleTuplePrefabExtension10 : PrefabExtensionSetAttributePatch - { - public static string Movie => "Launcher.Saves.SaveTuple"; - public static string XPath => "descendant::TextWidget[@Text='@CharacterName']"; + public override string Attribute => "Brush.FontSize"; + public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; +} +internal sealed class ModuleTuplePrefabExtension10 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "Launcher.Saves.SaveTuple"; + public static string XPath => "descendant::TextWidget[@Text='@CharacterName']"; - public override string Attribute => "Brush.FontSize"; - public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; - } - internal sealed class ModuleTuplePrefabExtension11 : PrefabExtensionSetAttributePatch - { - public static string Movie => "Launcher.Saves.SaveTuple"; - public static string XPath => "descendant::TextWidget[@Text='@Level']"; + public override string Attribute => "Brush.FontSize"; + public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; +} +internal sealed class ModuleTuplePrefabExtension11 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "Launcher.Saves.SaveTuple"; + public static string XPath => "descendant::TextWidget[@Text='@Level']"; - public override string Attribute => "Brush.FontSize"; - public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; - } - internal sealed class ModuleTuplePrefabExtension12 : PrefabExtensionSetAttributePatch - { - public static string Movie => "Launcher.Saves.SaveTuple"; - public static string XPath => "descendant::TextWidget[@Text='@Days']"; + public override string Attribute => "Brush.FontSize"; + public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; +} +internal sealed class ModuleTuplePrefabExtension12 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "Launcher.Saves.SaveTuple"; + public static string XPath => "descendant::TextWidget[@Text='@Days']"; - public override string Attribute => "Brush.FontSize"; - public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; - } - internal sealed class ModuleTuplePrefabExtension13 : PrefabExtensionSetAttributePatch - { - public static string Movie => "Launcher.Saves.SaveTuple"; - public static string XPath => "descendant::TextWidget[@Text='@CreatedAt']"; + public override string Attribute => "Brush.FontSize"; + public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; +} +internal sealed class ModuleTuplePrefabExtension13 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "Launcher.Saves.SaveTuple"; + public static string XPath => "descendant::TextWidget[@Text='@CreatedAt']"; - public override string Attribute => "Brush.FontSize"; - public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; - } + public override string Attribute => "Brush.FontSize"; + public override string Value => LauncherSettings.CompactModuleList ? "20" : "26"; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.ImportExport.cs b/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.ImportExport.cs index 456f122..92604dd 100644 --- a/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.ImportExport.cs +++ b/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.ImportExport.cs @@ -2,40 +2,39 @@ using System.Xml; -namespace Bannerlord.LauncherEx.PrefabExtensions +namespace Bannerlord.LauncherEx.PrefabExtensions; + +internal sealed class UILauncherPrefabExtension14 : PrefabExtensionInsertAsSiblingPatch { - internal sealed class UILauncherPrefabExtension14 : PrefabExtensionInsertAsSiblingPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::TextWidget[@Text='@VersionText']"; + public static string Movie => "UILauncher"; + public static string XPath => "descendant::TextWidget[@Text='@VersionText']"; - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); - public UILauncherPrefabExtension14() - { - XmlDocument.LoadXml(@" - + public UILauncherPrefabExtension14() + { + XmlDocument.LoadXml(""" + - + - + -"); - } - - public override XmlDocument GetPrefabExtension() => XmlDocument; +"""); } + + public override XmlDocument GetPrefabExtension() => XmlDocument; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Minor.cs b/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Minor.cs index bb15267..08a88ab 100644 --- a/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Minor.cs +++ b/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Minor.cs @@ -7,221 +7,220 @@ using TaleWorlds.MountAndBlade.Launcher.Library; -namespace Bannerlord.LauncherEx.PrefabExtensions +namespace Bannerlord.LauncherEx.PrefabExtensions; + +/// +/// BLSE text up the Version. Singleplayer +/// +internal sealed class UILauncherPrefabExtension1 : PrefabExtensionInsertAsSiblingPatch { - /// - /// BLSE text up the Version. Singleplayer - /// - internal sealed class UILauncherPrefabExtension1 : PrefabExtensionInsertAsSiblingPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::TextWidget[@Text='@VersionText']"; + public static string Movie => "UILauncher"; + public static string XPath => "descendant::TextWidget[@Text='@VersionText']"; - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); - public UILauncherPrefabExtension1() - { - XmlDocument.LoadXml(@$" - -"); - } - - public override XmlDocument GetPrefabExtension() => XmlDocument; - } - /// - /// BUTRLoader text up the Version. Singleplayer - /// - internal sealed class UILauncherPrefabExtension2 : PrefabExtensionInsertAsSiblingPatch + public UILauncherPrefabExtension1() { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::TextWidget[@Text='@BLSEVersionText']"; + XmlDocument.LoadXml(""" + +"""); + } - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); + public override XmlDocument GetPrefabExtension() => XmlDocument; +} +/// +/// BUTRLoader text up the Version. Singleplayer +/// +internal sealed class UILauncherPrefabExtension2 : PrefabExtensionInsertAsSiblingPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::TextWidget[@Text='@BLSEVersionText']"; - public UILauncherPrefabExtension2() - { - XmlDocument.LoadXml(@$" - -"); - } + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); - public override XmlDocument GetPrefabExtension() => XmlDocument; + public UILauncherPrefabExtension2() + { + XmlDocument.LoadXml(""" + +"""); } - /// - /// Reset MarginRight of TopMenu widget - /// - internal sealed class UILauncherPrefabExtension15 : PrefabExtensionSetAttributePatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::Widget[@Id='TopMenu']"; + public override XmlDocument GetPrefabExtension() => XmlDocument; +} - public override string Attribute => "MarginRight"; - public override string Value => "10"; - } +/// +/// Reset MarginRight of TopMenu widget +/// +internal sealed class UILauncherPrefabExtension15 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::Widget[@Id='TopMenu']"; - /// - /// Move the random image to the left by 100 pixels - /// - internal sealed class UILauncherPrefabExtension16 : PrefabExtensionSetAttributePatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::LauncherRandomImageWidget"; + public override string Attribute => "MarginRight"; + public override string Value => "10"; +} - public override string Attribute => "MarginRight"; - public override string Value => "-100"; - } +/// +/// Move the random image to the left by 100 pixels +/// +internal sealed class UILauncherPrefabExtension16 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::LauncherRandomImageWidget"; - /// - /// Make the random image hideable - /// - internal sealed class UILauncherPrefabExtension17 : PrefabExtensionSetAttributesPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::LauncherRandomImageWidget"; + public override string Attribute => "MarginRight"; + public override string Value => "-100"; +} - public override List Attributes => new() - { - new Attribute("IsHidden", ""), - new Attribute("IsVisible", "@ShowRandomImage"), - }; - } +/// +/// Make the random image hideable +/// +internal sealed class UILauncherPrefabExtension17 : PrefabExtensionSetAttributesPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::LauncherRandomImageWidget"; - /// - /// Set our dynamic margin for random image - /// - internal sealed class UILauncherPrefabExtension18 : PrefabExtensionSetAttributePatch + public override List Attributes => new() { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::TabControl[@Id='ContentPanel']"; + new Attribute("IsHidden", ""), + new Attribute("IsVisible", "@ShowRandomImage"), + }; +} + +/// +/// Set our dynamic margin for random image +/// +internal sealed class UILauncherPrefabExtension18 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::TabControl[@Id='ContentPanel']"; - public override string Attribute => "MarginRight"; - public override string Value => "@ContentTabControlMarginRight"; - } + public override string Attribute => "MarginRight"; + public override string Value => "@ContentTabControlMarginRight"; +} - internal sealed class UILauncherPrefabExtension28 : PrefabExtensionSetAttributePatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::TabControl[@Id='ContentPanel']"; +internal sealed class UILauncherPrefabExtension28 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::TabControl[@Id='ContentPanel']"; - public override string Attribute => "MarginBottom"; - public override string Value => "@ContentTabControlMarginBottom"; - } + public override string Attribute => "MarginBottom"; + public override string Value => "@ContentTabControlMarginBottom"; +} - internal sealed class UILauncherPrefabExtension29 : PrefabExtensionSetAttributePatch - { - public static string Movie => "UILauncher"; - public static string XPath => "/Prefab/Window/LauncherDragWindowAreaWidget/Children/Widget/Children/Widget/Children/Widget[4]"; +internal sealed class UILauncherPrefabExtension29 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "/Prefab/Window/LauncherDragWindowAreaWidget/Children/Widget/Children/Widget/Children/Widget[4]"; - public override string Attribute => "MarginBottom"; - public override string Value => "@DividerMarginBottom"; - } + public override string Attribute => "MarginBottom"; + public override string Value => "@DividerMarginBottom"; +} - /// - /// Replaces the standard values with our overrides - /// - internal sealed class UILauncherPrefabExtension19 : PrefabExtensionCustomPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "/Prefab"; +/// +/// Replaces the standard values with our overrides +/// +internal sealed class UILauncherPrefabExtension19 : PrefabExtensionCustomPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "/Prefab"; - public override void Apply(XmlNode node) + public override void Apply(XmlNode node) + { + foreach (var selectNode in node.OwnerDocument?.SelectNodes("//*")?.OfType() ?? Enumerable.Empty()) { - foreach (var selectNode in node.OwnerDocument?.SelectNodes("//*")?.OfType() ?? Enumerable.Empty()) + foreach (var attribute in selectNode.Attributes?.OfType() ?? Enumerable.Empty()) { - foreach (var attribute in selectNode.Attributes?.OfType() ?? Enumerable.Empty()) + attribute.Value = attribute.Value switch { - attribute.Value = attribute.Value switch - { - $"@{nameof(LauncherVM.IsSingleplayer)}" => $"@{nameof(LauncherVMMixin.IsSingleplayer2)}", - $"@{nameof(LauncherVM.IsMultiplayer)}" => $"@{nameof(LauncherVMMixin.IsMultiplayer2)}", - $"@{"IsDigitalCompanion"}" => $"@{nameof(LauncherVMMixin.IsDigitalCompanion2)}", - _ => attribute.Value - }; - } + $"@{nameof(LauncherVM.IsSingleplayer)}" => $"@{nameof(LauncherVMMixin.IsSingleplayer2)}", + $"@{nameof(LauncherVM.IsMultiplayer)}" => $"@{nameof(LauncherVMMixin.IsMultiplayer2)}", + $"@{"IsDigitalCompanion"}" => $"@{nameof(LauncherVMMixin.IsDigitalCompanion2)}", + _ => attribute.Value + }; } } } +} - internal sealed class UILauncherPrefabExtension30 : PrefabExtensionSetAttributePatch - { - public static string Movie => "UILauncher"; - public static string XPath => "/Prefab/Window/LauncherDragWindowAreaWidget/Children/Widget/Children/Widget"; +internal sealed class UILauncherPrefabExtension30 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "/Prefab/Window/LauncherDragWindowAreaWidget/Children/Widget/Children/Widget"; - public override string Attribute => "SuggestedHeight"; - public override string Value => "@BackgroundHeight"; - } + public override string Attribute => "SuggestedHeight"; + public override string Value => "@BackgroundHeight"; +} - internal sealed class UILauncherPrefabExtension31 : PrefabExtensionCustomPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "/Prefab"; +internal sealed class UILauncherPrefabExtension31 : PrefabExtensionCustomPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "/Prefab"; - public override void Apply(XmlNode node) + public override void Apply(XmlNode node) + { + foreach (var selectNode in node.OwnerDocument?.SelectNodes("//*")?.OfType() ?? Enumerable.Empty()) { - foreach (var selectNode in node.OwnerDocument?.SelectNodes("//*")?.OfType() ?? Enumerable.Empty()) + foreach (var attribute in selectNode.Attributes?.OfType() ?? Enumerable.Empty()) { - foreach (var attribute in selectNode.Attributes?.OfType() ?? Enumerable.Empty()) - { - if (attribute.Name != "Text") continue; - if (!attribute.Value.StartsWith("@")) continue; - if (!attribute.Value.EndsWith("Text")) continue; - if (attribute.Value == "@Text") continue; - if (attribute.Value == "@VersionText") continue; - attribute.Value = $"{attribute.Value}2"; - } + if (attribute.Name != "Text") continue; + if (!attribute.Value.StartsWith("@")) continue; + if (!attribute.Value.EndsWith("Text")) continue; + if (attribute.Value == "@Text") continue; + if (attribute.Value == "@VersionText") continue; + attribute.Value = $"{attribute.Value}2"; } } } +} - internal sealed class UILauncherPrefabExtension34 : PrefabExtensionCustomPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "/Prefab"; +internal sealed class UILauncherPrefabExtension34 : PrefabExtensionCustomPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "/Prefab"; - public override void Apply(XmlNode node) + public override void Apply(XmlNode node) + { + foreach (var selectNode in node.OwnerDocument?.SelectNodes("//*")?.OfType() ?? Enumerable.Empty()) { - foreach (var selectNode in node.OwnerDocument?.SelectNodes("//*")?.OfType() ?? Enumerable.Empty()) + foreach (var attribute in selectNode.Attributes?.OfType() ?? Enumerable.Empty()) { - foreach (var attribute in selectNode.Attributes?.OfType() ?? Enumerable.Empty()) - { - if (attribute.Name != "Brush") continue; - if (attribute.Value != "Launcher.PlayButton.Text") continue; - if (attribute.Value == "@VersionText") continue; - attribute.Value = "Launcher.Button.Text"; - } + if (attribute.Name != "Brush") continue; + if (attribute.Value != "Launcher.PlayButton.Text") continue; + if (attribute.Value == "@VersionText") continue; + attribute.Value = "Launcher.Button.Text"; } } } +} - internal sealed class UILauncherPrefabExtension32 : PrefabExtensionInsertAsSiblingPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::Launcher.ConfirmStart"; +internal sealed class UILauncherPrefabExtension32 : PrefabExtensionInsertAsSiblingPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::Launcher.ConfirmStart"; - private XmlDocument XmlDocument { get; } = new(); + private XmlDocument XmlDocument { get; } = new(); - public UILauncherPrefabExtension32() - { - XmlDocument.LoadXml(@" - -"); - } - - public override XmlDocument GetPrefabExtension() => XmlDocument; + public UILauncherPrefabExtension32() + { + XmlDocument.LoadXml(""" + +"""); } - // No more multiplayer for LauncherEx - internal sealed class UILauncherPrefabExtension35 : PrefabExtensionSetAttributesPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::ButtonWidget[@Id='PlayMultiplayerButton']"; + public override XmlDocument GetPrefabExtension() => XmlDocument; +} - public override List Attributes => new() - { - new Attribute("IsDisabled", "true"), - }; - } +// No more multiplayer for LauncherEx +internal sealed class UILauncherPrefabExtension35 : PrefabExtensionSetAttributesPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::ButtonWidget[@Id='PlayMultiplayerButton']"; + + public override List Attributes => new() + { + new Attribute("IsDisabled", "true"), + }; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Mods.Saves.cs b/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Mods.Saves.cs index 5d8e0d0..6b22ce5 100644 --- a/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Mods.Saves.cs +++ b/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Mods.Saves.cs @@ -3,67 +3,66 @@ using System.Collections.Generic; using System.Xml; -namespace Bannerlord.LauncherEx.PrefabExtensions +namespace Bannerlord.LauncherEx.PrefabExtensions; + +internal sealed class UILauncherPrefabExtension24 : PrefabExtensionInsertAsSiblingPatch { - internal sealed class UILauncherPrefabExtension24 : PrefabExtensionInsertAsSiblingPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::TabToggleWidget[5]"; + public static string Movie => "UILauncher"; + public static string XPath => "descendant::TabToggleWidget[5]"; - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); - public UILauncherPrefabExtension24() - { - XmlDocument.LoadXml(@" - + public UILauncherPrefabExtension24() + { + XmlDocument.LoadXml(""" + - + -"); - } - - public override XmlDocument GetPrefabExtension() => XmlDocument; +"""); } - internal sealed class UILauncherPrefabExtension25 : PrefabExtensionInsertAsSiblingPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::Launcher.Options[@Id='OptionsGamePage']"; - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); + public override XmlDocument GetPrefabExtension() => XmlDocument; +} +internal sealed class UILauncherPrefabExtension25 : PrefabExtensionInsertAsSiblingPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::Launcher.Options[@Id='OptionsGamePage']"; - public UILauncherPrefabExtension25() - { - XmlDocument.LoadXml(@" - -"); - } + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); - public override XmlDocument GetPrefabExtension() => XmlDocument; + public UILauncherPrefabExtension25() + { + XmlDocument.LoadXml(""" + +"""); } - internal sealed class UILauncherPrefabExtension26 : PrefabExtensionSetAttributesPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::ButtonWidget[@Id='PlaySingleplayerButton']"; + public override XmlDocument GetPrefabExtension() => XmlDocument; +} - public override List Attributes => new() - { - new Attribute("IsHidden", ""), - new Attribute("IsVisible", "@ShowPlaySingleplayerButton"), - }; - } - internal sealed class UILauncherPrefabExtension33 : PrefabExtensionSetAttributesPatch +internal sealed class UILauncherPrefabExtension26 : PrefabExtensionSetAttributesPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::ButtonWidget[@Id='PlaySingleplayerButton']"; + + public override List Attributes => new() { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::ButtonWidget[@Id='ContinueSingleplayerButton']"; + new Attribute("IsHidden", ""), + new Attribute("IsVisible", "@ShowPlaySingleplayerButton"), + }; +} +internal sealed class UILauncherPrefabExtension33 : PrefabExtensionSetAttributesPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::ButtonWidget[@Id='ContinueSingleplayerButton']"; - public override List Attributes => new() - { - new Attribute("IsHidden", ""), - new Attribute("IsVisible", "@ShowContinueSingleplayerButton"), - }; - } + public override List Attributes => new() + { + new Attribute("IsHidden", ""), + new Attribute("IsVisible", "@ShowContinueSingleplayerButton"), + }; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Mods.cs b/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Mods.cs index 4c217ef..36996b6 100644 --- a/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Mods.cs +++ b/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Mods.cs @@ -3,41 +3,40 @@ using System.Collections.Generic; using System.Xml; -namespace Bannerlord.LauncherEx.PrefabExtensions +namespace Bannerlord.LauncherEx.PrefabExtensions; + +/// +/// ModsPage - uses 'ShowMods' instead of 'IsMultiplayer' +/// +internal sealed class UILauncherPrefabExtension9 : PrefabExtensionSetAttributesPatch { - /// - /// ModsPage - uses 'ShowMods' instead of 'IsMultiplayer' - /// - internal sealed class UILauncherPrefabExtension9 : PrefabExtensionSetAttributesPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::TabToggleWidget[2]"; - - public override List Attributes => new() - { - new Attribute("IsHidden", ""), - new Attribute("IsVisible", "@ShowMods"), - new Attribute("IsSelected", "@IsModsDataSelected"), - }; - } + public static string Movie => "UILauncher"; + public static string XPath => "descendant::TabToggleWidget[2]"; - /// - /// Replaces Launcher.Mods with our own static implementation, since we add a lot of custom stuff anyway - /// - internal sealed class UILauncherPrefabExtension13 : PrefabExtensionReplacePatch + public override List Attributes => new() { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::Launcher.Mods"; + new Attribute("IsHidden", ""), + new Attribute("IsVisible", "@ShowMods"), + new Attribute("IsSelected", "@IsModsDataSelected"), + }; +} - private XmlDocument XmlDocument { get; } = new(); +/// +/// Replaces Launcher.Mods with our own static implementation, since we add a lot of custom stuff anyway +/// +internal sealed class UILauncherPrefabExtension13 : PrefabExtensionReplacePatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::Launcher.Mods"; - public UILauncherPrefabExtension13() - { - XmlDocument.LoadXml(@" - -"); - } + private XmlDocument XmlDocument { get; } = new(); - public override XmlDocument GetPrefabExtension() => XmlDocument; + public UILauncherPrefabExtension13() + { + XmlDocument.LoadXml(""" + +"""); } + + public override XmlDocument GetPrefabExtension() => XmlDocument; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.News.cs b/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.News.cs index 8427b2f..184400e 100644 --- a/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.News.cs +++ b/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.News.cs @@ -2,32 +2,31 @@ using System.Collections.Generic; -namespace Bannerlord.LauncherEx.PrefabExtensions -{ - /// - /// ModsPage - uses 'ShowNews' instead of 'IsMultiplayer' - /// - internal sealed class UILauncherPrefabExtension8 : PrefabExtensionSetAttributesPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::TabToggleWidget[1]"; +namespace Bannerlord.LauncherEx.PrefabExtensions; - public override List Attributes => new() - { - new Attribute("IsHidden", ""), - new Attribute("IsVisible", "@ShowNews"), - }; - } +/// +/// ModsPage - uses 'ShowNews' instead of 'IsMultiplayer' +/// +internal sealed class UILauncherPrefabExtension8 : PrefabExtensionSetAttributesPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::TabToggleWidget[1]"; - /// - /// News tab can be disabled - /// - internal sealed class UILauncherPrefabExtension12 : PrefabExtensionSetAttributePatch + public override List Attributes => new() { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::Launcher.News"; + new Attribute("IsHidden", ""), + new Attribute("IsVisible", "@ShowNews"), + }; +} + +/// +/// News tab can be disabled +/// +internal sealed class UILauncherPrefabExtension12 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::Launcher.News"; - public override string Attribute => "IsDisabled"; - public override string Value => "@IsDisabled2"; - } + public override string Attribute => "IsDisabled"; + public override string Value => "@IsDisabled2"; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Options.cs b/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Options.cs index e4f462f..5f203ea 100644 --- a/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Options.cs +++ b/src/Bannerlord.LauncherEx/PrefabExtensions/UILauncherPrefabExtension.Options.cs @@ -2,197 +2,196 @@ using System.Xml; -namespace Bannerlord.LauncherEx.PrefabExtensions +namespace Bannerlord.LauncherEx.PrefabExtensions; + +/// +/// Adds Options button on very top +/// +internal sealed class UILauncherPrefabExtension3 : PrefabExtensionInsertAsSiblingPatch { - /// - /// Adds Options button on very top - /// - internal sealed class UILauncherPrefabExtension3 : PrefabExtensionInsertAsSiblingPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "/Prefab/Window/LauncherDragWindowAreaWidget/Children/Widget/Children/Widget/Children/Widget[2]/Children/Widget[2]/Children/Widget[1]/Children/ListPanel/Children/ButtonWidget[1]"; + public static string Movie => "UILauncher"; + public static string XPath => "/Prefab/Window/LauncherDragWindowAreaWidget/Children/Widget/Children/Widget/Children/Widget[2]/Children/Widget[2]/Children/Widget[1]/Children/ListPanel/Children/ButtonWidget[1]"; - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); - public UILauncherPrefabExtension3() - { - XmlDocument.LoadXml(@" - + public UILauncherPrefabExtension3() + { + XmlDocument.LoadXml(""" + - + -"); - } - - public override XmlDocument GetPrefabExtension() => XmlDocument; +"""); } - /// - /// Replaces original Divider with a left one - /// - internal sealed class UILauncherPrefabExtension4 : PrefabExtensionSetAttributePatch - { - public static string Movie => "UILauncher"; - public static string XPath => "/Prefab/Window/LauncherDragWindowAreaWidget/Children/Widget/Children/Widget/Children/Widget[2]/Children/Widget[2]/Children/Widget[1]/Children/Widget[1]"; + public override XmlDocument GetPrefabExtension() => XmlDocument; +} - public override string Attribute => "MarginLeft"; - public override string Value => "125"; - } +/// +/// Replaces original Divider with a left one +/// +internal sealed class UILauncherPrefabExtension4 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "/Prefab/Window/LauncherDragWindowAreaWidget/Children/Widget/Children/Widget/Children/Widget[2]/Children/Widget[2]/Children/Widget[1]/Children/Widget[1]"; - /// - /// Appends a second Divider to the right - /// - internal sealed class UILauncherPrefabExtension5 : PrefabExtensionInsertAsSiblingPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "/Prefab/Window/LauncherDragWindowAreaWidget/Children/Widget/Children/Widget/Children/Widget[2]/Children/Widget[2]/Children/Widget[1]/Children/Widget[1]"; + public override string Attribute => "MarginLeft"; + public override string Value => "125"; +} - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); +/// +/// Appends a second Divider to the right +/// +internal sealed class UILauncherPrefabExtension5 : PrefabExtensionInsertAsSiblingPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "/Prefab/Window/LauncherDragWindowAreaWidget/Children/Widget/Children/Widget/Children/Widget[2]/Children/Widget[2]/Children/Widget[1]/Children/Widget[1]"; - public UILauncherPrefabExtension5() - { - XmlDocument.LoadXml(@" - -"); - } + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); - public override XmlDocument GetPrefabExtension() => XmlDocument; + public UILauncherPrefabExtension5() + { + XmlDocument.LoadXml(""" + +"""); } - /// - /// Adds Launcher Tab on lower tab screen - /// - internal sealed class UILauncherPrefabExtension6 : PrefabExtensionInsertAsSiblingPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::TabToggleWidget[2]"; + public override XmlDocument GetPrefabExtension() => XmlDocument; +} + +/// +/// Adds Launcher Tab on lower tab screen +/// +internal sealed class UILauncherPrefabExtension6 : PrefabExtensionInsertAsSiblingPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::TabToggleWidget[2]"; - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); - public UILauncherPrefabExtension6() - { - XmlDocument.LoadXml(@" - + public UILauncherPrefabExtension6() + { + XmlDocument.LoadXml(""" + - + -"); - } - - public override XmlDocument GetPrefabExtension() => XmlDocument; +"""); } - internal sealed class UILauncherPrefabExtension20 : PrefabExtensionInsertAsSiblingPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::TabToggleWidget[3]"; - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); + public override XmlDocument GetPrefabExtension() => XmlDocument; +} +internal sealed class UILauncherPrefabExtension20 : PrefabExtensionInsertAsSiblingPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::TabToggleWidget[3]"; + + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); - public UILauncherPrefabExtension20() - { - XmlDocument.LoadXml(@" - + public UILauncherPrefabExtension20() + { + XmlDocument.LoadXml(""" + - + -"); - } - - public override XmlDocument GetPrefabExtension() => XmlDocument; +"""); } - internal sealed class UILauncherPrefabExtension21 : PrefabExtensionInsertAsSiblingPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::TabToggleWidget[4]"; - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); + public override XmlDocument GetPrefabExtension() => XmlDocument; +} +internal sealed class UILauncherPrefabExtension21 : PrefabExtensionInsertAsSiblingPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::TabToggleWidget[4]"; - public UILauncherPrefabExtension21() - { - XmlDocument.LoadXml(@" - + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); + + public UILauncherPrefabExtension21() + { + XmlDocument.LoadXml(""" + - + -"); - } - - public override XmlDocument GetPrefabExtension() => XmlDocument; +"""); } - /// - /// Adds the Options Tab View - /// - internal sealed class UILauncherPrefabExtension7 : PrefabExtensionInsertAsSiblingPatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::Launcher.Mods"; + public override XmlDocument GetPrefabExtension() => XmlDocument; +} - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); +/// +/// Adds the Options Tab View +/// +internal sealed class UILauncherPrefabExtension7 : PrefabExtensionInsertAsSiblingPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::Launcher.Mods"; - public UILauncherPrefabExtension7() - { - XmlDocument.LoadXml(@" - -"); - } + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); - public override XmlDocument GetPrefabExtension() => XmlDocument; - } - internal sealed class UILauncherPrefabExtension22 : PrefabExtensionInsertAsSiblingPatch + public UILauncherPrefabExtension7() { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::Launcher.Options[@Id='OptionsLauncherPage']"; + XmlDocument.LoadXml(""" + +"""); + } - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); + public override XmlDocument GetPrefabExtension() => XmlDocument; +} +internal sealed class UILauncherPrefabExtension22 : PrefabExtensionInsertAsSiblingPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::Launcher.Options[@Id='OptionsLauncherPage']"; - public UILauncherPrefabExtension22() - { - XmlDocument.LoadXml(@" - -"); - } + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); - public override XmlDocument GetPrefabExtension() => XmlDocument; - } - internal sealed class UILauncherPrefabExtension23 : PrefabExtensionInsertAsSiblingPatch + public UILauncherPrefabExtension22() { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::Launcher.Options[@Id='OptionsGamePage']"; + XmlDocument.LoadXml(""" + +"""); + } - public override InsertType Type => InsertType.Append; - private XmlDocument XmlDocument { get; } = new(); + public override XmlDocument GetPrefabExtension() => XmlDocument; +} +internal sealed class UILauncherPrefabExtension23 : PrefabExtensionInsertAsSiblingPatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::Launcher.Options[@Id='OptionsGamePage']"; - public UILauncherPrefabExtension23() - { - XmlDocument.LoadXml(@" - -"); - } + public override InsertType Type => InsertType.Append; + private XmlDocument XmlDocument { get; } = new(); - public override XmlDocument GetPrefabExtension() => XmlDocument; + public UILauncherPrefabExtension23() + { + XmlDocument.LoadXml(""" + +"""); } - /// - /// Changing to Option screen will change image - /// - internal sealed class UILauncherPrefabExtension10 : PrefabExtensionSetAttributePatch - { - public static string Movie => "UILauncher"; - public static string XPath => "descendant::LauncherRandomImageWidget"; + public override XmlDocument GetPrefabExtension() => XmlDocument; +} - public override string Attribute => "ChangeTrigger"; - public override string Value => "@RandomImageSwitch"; - } +/// +/// Changing to Option screen will change image +/// +internal sealed class UILauncherPrefabExtension10 : PrefabExtensionSetAttributePatch +{ + public static string Movie => "UILauncher"; + public static string XPath => "descendant::LauncherRandomImageWidget"; + + public override string Attribute => "ChangeTrigger"; + public override string Value => "@RandomImageSwitch"; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/ResourceManagers/BrushFactoryManager.cs b/src/Bannerlord.LauncherEx/ResourceManagers/BrushFactoryManager.cs index a7125ca..2645d34 100644 --- a/src/Bannerlord.LauncherEx/ResourceManagers/BrushFactoryManager.cs +++ b/src/Bannerlord.LauncherEx/ResourceManagers/BrushFactoryManager.cs @@ -8,115 +8,114 @@ using TaleWorlds.GauntletUI; -namespace Bannerlord.LauncherEx.ResourceManagers +namespace Bannerlord.LauncherEx.ResourceManagers; + +internal static class BrushFactoryManager { - internal static class BrushFactoryManager - { - private static readonly Dictionary CustomBrushes = new(); - private static readonly List>> DeferredInitialization = new(); + private static readonly Dictionary CustomBrushes = new(); + private static readonly List>> DeferredInitialization = new(); - private delegate Brush LoadBrushFromDelegate(BrushFactory instance, XmlNode brushNode); - private static readonly LoadBrushFromDelegate? LoadBrushFrom = - AccessTools2.GetDelegate(typeof(BrushFactory), "LoadBrushFrom"); + private delegate Brush LoadBrushFromDelegate(BrushFactory instance, XmlNode brushNode); + private static readonly LoadBrushFromDelegate? LoadBrushFrom = + AccessTools2.GetDelegate(typeof(BrushFactory), "LoadBrushFrom"); - private static Harmony? _harmony; - private static WeakReference BrushFactoryReference { get; } = new(null); - public static void SetBrushFactory(BrushFactory brushFactory) - { - BrushFactoryReference.SetTarget(brushFactory); + private static Harmony? _harmony; + private static WeakReference BrushFactoryReference { get; } = new(null); + public static void SetBrushFactory(BrushFactory brushFactory) + { + BrushFactoryReference.SetTarget(brushFactory); - foreach (var brush in DeferredInitialization.SelectMany(func => func())) - { - CustomBrushes[brush.Name] = brush; - } + foreach (var brush in DeferredInitialization.SelectMany(func => func())) + { + CustomBrushes[brush.Name] = brush; } + } - public static IEnumerable Create(XmlDocument xmlDocument) - { - if (!BrushFactoryReference.TryGetTarget(out var brushFactory) || brushFactory is null) - yield break; + public static IEnumerable Create(XmlDocument xmlDocument) + { + if (!BrushFactoryReference.TryGetTarget(out var brushFactory) || brushFactory is null) + yield break; - foreach (var brushNode in xmlDocument.SelectSingleNode("Brushes")!.ChildNodes.OfType()) + foreach (var brushNode in xmlDocument.SelectSingleNode("Brushes")!.ChildNodes.OfType()) + { + var brush = LoadBrushFrom?.Invoke(brushFactory, brushNode); + if (brush is not null) { - var brush = LoadBrushFrom?.Invoke(brushFactory, brushNode); - if (brush is not null) - { - yield return brush; - } + yield return brush; } } - public static void Register(Func> func) => DeferredInitialization.Add(func); - public static void CreateAndRegister(XmlDocument xmlDocument) => Register(() => Create(xmlDocument)); - - public static void Clear() - { - CustomBrushes.Clear(); - DeferredInitialization.Clear(); - BrushFactoryReference.SetTarget(null); - } - - internal static bool Enable(Harmony harmony) - { - _harmony = harmony; + } + public static void Register(Func> func) => DeferredInitialization.Add(func); + public static void CreateAndRegister(XmlDocument xmlDocument) => Register(() => Create(xmlDocument)); - var res1 = harmony.TryPatch( - AccessTools2.PropertyGetter(typeof(BrushFactory), "Brushes"), - postfix: AccessTools2.DeclaredMethod(typeof(BrushFactoryManager), nameof(BrushesPostfix))); - if (!res1) return false; + public static void Clear() + { + CustomBrushes.Clear(); + DeferredInitialization.Clear(); + BrushFactoryReference.SetTarget(null); + } - var res2 = harmony.TryPatch( - AccessTools2.DeclaredMethod(typeof(BrushFactory), "CheckForUpdates"), - prefix: AccessTools2.DeclaredMethod(typeof(BrushFactoryManager), nameof(CheckForUpdatesPrefix))); - if (!res2) return false; + internal static bool Enable(Harmony harmony) + { + _harmony = harmony; - var res3 = harmony.TryPatch( - AccessTools2.DeclaredMethod(typeof(BrushFactory), "GetBrush"), - AccessTools2.DeclaredMethod(typeof(BrushFactoryManager), nameof(GetBrushPrefix))); - if (!res3) return false; + var res1 = harmony.TryPatch( + AccessTools2.PropertyGetter(typeof(BrushFactory), "Brushes"), + postfix: AccessTools2.DeclaredMethod(typeof(BrushFactoryManager), nameof(BrushesPostfix))); + if (!res1) return false; - var res4 = harmony.TryPatch( - AccessTools2.Method(typeof(BrushFactory), "LoadBrushes"), - AccessTools2.DeclaredMethod(typeof(BrushFactoryManager), nameof(LoadBrushesPostfix))); - if (!res4) return false; + var res2 = harmony.TryPatch( + AccessTools2.DeclaredMethod(typeof(BrushFactory), "CheckForUpdates"), + prefix: AccessTools2.DeclaredMethod(typeof(BrushFactoryManager), nameof(CheckForUpdatesPrefix))); + if (!res2) return false; - return true; - } + var res3 = harmony.TryPatch( + AccessTools2.DeclaredMethod(typeof(BrushFactory), "GetBrush"), + AccessTools2.DeclaredMethod(typeof(BrushFactoryManager), nameof(GetBrushPrefix))); + if (!res3) return false; - private static void BrushesPostfix(ref IEnumerable __result) - { - __result = __result.Concat(CustomBrushes.Values); - } + var res4 = harmony.TryPatch( + AccessTools2.Method(typeof(BrushFactory), "LoadBrushes"), + AccessTools2.DeclaredMethod(typeof(BrushFactoryManager), nameof(LoadBrushesPostfix))); + if (!res4) return false; - // We need to disable BrushFactory's hot reload - private static bool CheckForUpdatesPrefix() - { - return false; - } + return true; + } - private static bool GetBrushPrefix(string name, Dictionary ____brushes, ref Brush __result) - { - if (____brushes.ContainsKey(name) || !CustomBrushes.ContainsKey(name)) - return true; + private static void BrushesPostfix(ref IEnumerable __result) + { + __result = __result.Concat(CustomBrushes.Values); + } - if (CustomBrushes[name] is { } brush) - { - __result = brush; - return false; - } + // We need to disable BrushFactory's hot reload + private static bool CheckForUpdatesPrefix() + { + return false; + } + private static bool GetBrushPrefix(string name, Dictionary ____brushes, ref Brush __result) + { + if (____brushes.ContainsKey(name) || !CustomBrushes.ContainsKey(name)) return true; + + if (CustomBrushes[name] is { } brush) + { + __result = brush; + return false; } + return true; + } - private static void LoadBrushesPostfix(ref BrushFactory __instance) - { - SetBrushFactory(__instance); - _harmony?.Unpatch( - AccessTools2.Method(typeof(BrushFactory), "LoadBrushes"), - AccessTools2.DeclaredMethod(typeof(BrushFactoryManager), nameof(LoadBrushesPostfix))); - } + private static void LoadBrushesPostfix(ref BrushFactory __instance) + { + SetBrushFactory(__instance); - private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; + _harmony?.Unpatch( + AccessTools2.Method(typeof(BrushFactory), "LoadBrushes"), + AccessTools2.DeclaredMethod(typeof(BrushFactoryManager), nameof(LoadBrushesPostfix))); } + + private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/ResourceManagers/FontFactoryManager.cs b/src/Bannerlord.LauncherEx/ResourceManagers/FontFactoryManager.cs index 3b45043..581c50a 100644 --- a/src/Bannerlord.LauncherEx/ResourceManagers/FontFactoryManager.cs +++ b/src/Bannerlord.LauncherEx/ResourceManagers/FontFactoryManager.cs @@ -1,8 +1,8 @@ -using Bannerlord.LauncherManager.Localization; +using Bannerlord.LauncherManager.Localization; using HarmonyLib; -using HarmonyLib.BUTR.Extensions; - +using HarmonyLib.BUTR.Extensions; + using System.Collections.Generic; using System.IO; @@ -10,94 +10,93 @@ using TaleWorlds.Library; using TaleWorlds.TwoDimension; -namespace Bannerlord.LauncherEx.ResourceManagers +namespace Bannerlord.LauncherEx.ResourceManagers; + +internal static class FontFactoryManager { - internal static class FontFactoryManager + private static Harmony? _harmony; + + internal static bool Enable(Harmony harmony) { - private static Harmony? _harmony; + _harmony = harmony; - internal static bool Enable(Harmony harmony) - { - _harmony = harmony; + var res1 = harmony.TryPatch( + AccessTools2.Method(typeof(FontFactory), "LoadAllFonts"), + postfix: AccessTools2.DeclaredMethod(typeof(FontFactoryManager), nameof(LoadAllFontsPostfix))); + if (!res1) return false; - var res1 = harmony.TryPatch( - AccessTools2.Method(typeof(FontFactory), "LoadAllFonts"), - postfix: AccessTools2.DeclaredMethod(typeof(FontFactoryManager), nameof(LoadAllFontsPostfix))); - if (!res1) return false; + var res2 = harmony.TryPatch( + AccessTools2.Method(typeof(FontFactory), "GetMappedFontForLocalization"), + prefix: AccessTools2.DeclaredMethod(typeof(FontFactoryManager), nameof(GetMappedFontForLocalizationPrefix))); + if (!res2) return false; - var res2 = harmony.TryPatch( - AccessTools2.Method(typeof(FontFactory), "GetMappedFontForLocalization"), - prefix: AccessTools2.DeclaredMethod(typeof(FontFactoryManager), nameof(GetMappedFontForLocalizationPrefix))); - if (!res2) return false; + return true; + } - return true; - } + private delegate void SetSpriteNamesDelegate(SpriteData instance, Dictionary value); + private static readonly SetSpriteNamesDelegate? SetSpriteNames = + AccessTools2.GetPropertySetterDelegate(typeof(SpriteData), "SpriteNames"); - private delegate void SetSpriteNamesDelegate(SpriteData instance, Dictionary value); - private static readonly SetSpriteNamesDelegate? SetSpriteNames = - AccessTools2.GetPropertySetterDelegate(typeof(SpriteData), "SpriteNames"); + private delegate void AddFontDefinitionDelegate(FontFactory instance, string fontPath, string fontName, SpriteData spriteData); + private static readonly AddFontDefinitionDelegate? AddFontDefinition = + AccessTools2.GetDelegate(typeof(FontFactory), "AddFontDefinition"); - private delegate void AddFontDefinitionDelegate(FontFactory instance, string fontPath, string fontName, SpriteData spriteData); - private static readonly AddFontDefinitionDelegate? AddFontDefinition = - AccessTools2.GetDelegate(typeof(FontFactory), "AddFontDefinition"); + private delegate bool TryAddFontDefinitionDelegate(FontFactory instance, string fontPath, string fontName, SpriteData spriteData); + private static readonly TryAddFontDefinitionDelegate? TryAddFontDefinition = + AccessTools2.GetDelegate(typeof(FontFactory), "TryAddFontDefinition"); - private delegate bool TryAddFontDefinitionDelegate(FontFactory instance, string fontPath, string fontName, SpriteData spriteData); - private static readonly TryAddFontDefinitionDelegate? TryAddFontDefinition = - AccessTools2.GetDelegate(typeof(FontFactory), "TryAddFontDefinition"); + private static readonly AddFontDefinitionDelegate? AddFontDefinitionOptimistic = + AddFontDefinition ?? new((instance, fontPath, fontName, spriteData) => TryAddFontDefinition?.Invoke(instance, fontPath, fontName, spriteData)); - private static readonly AddFontDefinitionDelegate? AddFontDefinitionOptimistic = - AddFontDefinition ?? new((instance, fontPath, fontName, spriteData) => TryAddFontDefinition?.Invoke(instance, fontPath, fontName, spriteData)); + private static SpriteData WithData(this SpriteData spriteData, string spriteName) + { + SetSpriteNames!(spriteData, new Dictionary + { + { spriteName, SpriteDataManager.CreateGeneric(spriteName)! } + }); + return spriteData; + } - private static SpriteData WithData(this SpriteData spriteData, string spriteName) + private static void LoadAllFontsPostfix(ref FontFactory __instance) + { + if (AddFontDefinitionOptimistic is not AddFontDefinitionDelegate addFont) { - SetSpriteNames!(spriteData, new Dictionary - { - { spriteName, SpriteDataManager.CreateGeneric(spriteName)! } - }); - return spriteData; + return; } - private static void LoadAllFontsPostfix(ref FontFactory __instance) + switch (BUTRLocalizationManager.ActiveLanguage) { - if (AddFontDefinitionOptimistic is not AddFontDefinitionDelegate addFont) - { - return; - } - - switch (BUTRLocalizationManager.ActiveLanguage) - { - case BUTRLocalizationManager.ChineseTraditional: - case BUTRLocalizationManager.ChineseSimple: - addFont(__instance, Path.Combine(BasePath.Name, "GUI", "GauntletUI", "Fonts", "simkai") + "/", "simkai", new SpriteData("simkai").WithData("simkai")); - break; - case BUTRLocalizationManager.Japanese: - addFont(__instance, Path.Combine(BasePath.Name, "GUI", "GauntletUI", "Fonts", "SourceHanSansJP") + "/", "SourceHanSansJP", new SpriteData("SourceHanSansJP").WithData("SourceHanSansJP")); - break; - case BUTRLocalizationManager.Korean: - addFont(__instance, Path.Combine(BasePath.Name, "GUI", "GauntletUI", "Fonts", "NanumGothicKR") + "/", "NanumGothicKR", new SpriteData("NanumGothicKR").WithData("NanumGothicKR")); - break; - } - + case BUTRLocalizationManager.ChineseTraditional: + case BUTRLocalizationManager.ChineseSimple: + addFont(__instance, Path.Combine(BasePath.Name, "GUI", "GauntletUI", "Fonts", "simkai") + "/", "simkai", new SpriteData("simkai").WithData("simkai")); + break; + case BUTRLocalizationManager.Japanese: + addFont(__instance, Path.Combine(BasePath.Name, "GUI", "GauntletUI", "Fonts", "SourceHanSansJP") + "/", "SourceHanSansJP", new SpriteData("SourceHanSansJP").WithData("SourceHanSansJP")); + break; + case BUTRLocalizationManager.Korean: + addFont(__instance, Path.Combine(BasePath.Name, "GUI", "GauntletUI", "Fonts", "NanumGothicKR") + "/", "NanumGothicKR", new SpriteData("NanumGothicKR").WithData("NanumGothicKR")); + break; } - private static bool GetMappedFontForLocalizationPrefix(ref FontFactory __instance, ref Font __result) + + } + private static bool GetMappedFontForLocalizationPrefix(ref FontFactory __instance, ref Font __result) + { + switch (BUTRLocalizationManager.ActiveLanguage) { - switch (BUTRLocalizationManager.ActiveLanguage) - { - case BUTRLocalizationManager.ChineseTraditional: - case BUTRLocalizationManager.ChineseSimple: - __result = __instance.GetFont("simkai"); - return false; - case BUTRLocalizationManager.Japanese: - __result = __instance.GetFont("SourceHanSansJP"); - return false; - case BUTRLocalizationManager.Korean: - __result = __instance.GetFont("NanumGothicKR"); - return false; - } - - return true; + case BUTRLocalizationManager.ChineseTraditional: + case BUTRLocalizationManager.ChineseSimple: + __result = __instance.GetFont("simkai"); + return false; + case BUTRLocalizationManager.Japanese: + __result = __instance.GetFont("SourceHanSansJP"); + return false; + case BUTRLocalizationManager.Korean: + __result = __instance.GetFont("NanumGothicKR"); + return false; } - private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; + return true; } + + private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/ResourceManagers/GraphicsContextManager.cs b/src/Bannerlord.LauncherEx/ResourceManagers/GraphicsContextManager.cs index 4f1887a..aaab672 100644 --- a/src/Bannerlord.LauncherEx/ResourceManagers/GraphicsContextManager.cs +++ b/src/Bannerlord.LauncherEx/ResourceManagers/GraphicsContextManager.cs @@ -10,78 +10,77 @@ using TaleWorlds.TwoDimension.Standalone; -namespace Bannerlord.LauncherEx.ResourceManagers +namespace Bannerlord.LauncherEx.ResourceManagers; + +internal static class GraphicsContextManager { - internal static class GraphicsContextManager - { - public static WeakReference Instance { get; private set; } = default!; + public static WeakReference Instance { get; private set; } = default!; - private static readonly Dictionary Textures = new(); - private static readonly Dictionary> DeferredInitialization = new(); + private static readonly Dictionary Textures = new(); + private static readonly Dictionary> DeferredInitialization = new(); - public static OpenGLTexture Create(string name, Stream stream) - { - var texture = new OpenGLTexture(); - if (texture.LoadFromStream(name, stream)) - return texture; + public static OpenGLTexture Create(string name, Stream stream) + { + var texture = new OpenGLTexture(); + if (texture.LoadFromStream(name, stream)) + return texture; - var path = Path.GetTempFileName(); - using (var fs = File.OpenWrite(path)) - stream.CopyTo(fs); - var openGLTexture = OpenGLTexture.FromFile(path); - File.Delete(path); + var path = Path.GetTempFileName(); + using (var fs = File.OpenWrite(path)) + stream.CopyTo(fs); + var openGLTexture = OpenGLTexture.FromFile(path); + File.Delete(path); - return openGLTexture; - } - private static OpenGLTexture CreateAssetTexture(string name, TPac.Texture assetTexture) - { - var texture = new OpenGLTexture(); - texture.LoadFromAssetTexture(name, assetTexture); - return texture; - } - public static void Register(string name, Func func) => DeferredInitialization.Add(name, func); - public static void CreateAndRegister(string name, Stream stream) => Register(name, () => Create(name, stream)); - public static void CreateAssetTextureAndRegister(string name, TPac.Texture texture) => Register(name, () => CreateAssetTexture(name, texture)); + return openGLTexture; + } + private static OpenGLTexture CreateAssetTexture(string name, TPac.Texture assetTexture) + { + var texture = new OpenGLTexture(); + texture.LoadFromAssetTexture(name, assetTexture); + return texture; + } + public static void Register(string name, Func func) => DeferredInitialization.Add(name, func); + public static void CreateAndRegister(string name, Stream stream) => Register(name, () => Create(name, stream)); + public static void CreateAssetTextureAndRegister(string name, TPac.Texture texture) => Register(name, () => CreateAssetTexture(name, texture)); - public static void Clear() - { - Textures.Clear(); - DeferredInitialization.Clear(); - Instance.SetTarget(null); - } + public static void Clear() + { + Textures.Clear(); + DeferredInitialization.Clear(); + Instance.SetTarget(null); + } - internal static bool Enable(Harmony harmony) - { - var res1 = harmony.TryPatch( - AccessTools2.DeclaredMethod(typeof(GraphicsContext), "GetTexture"), - prefix: AccessTools2.DeclaredMethod(typeof(GraphicsContextManager), nameof(GetTexturePrefix))); - if (!res1) return false; + internal static bool Enable(Harmony harmony) + { + var res1 = harmony.TryPatch( + AccessTools2.DeclaredMethod(typeof(GraphicsContext), "GetTexture"), + prefix: AccessTools2.DeclaredMethod(typeof(GraphicsContextManager), nameof(GetTexturePrefix))); + if (!res1) return false; - var res2 = harmony.TryPatch( - AccessTools2.DeclaredMethod(typeof(GraphicsContext), "CreateContext"), - postfix: AccessTools2.DeclaredMethod(typeof(GraphicsContextManager), nameof(CreateContextPostfix))); - if (!res2) return false; + var res2 = harmony.TryPatch( + AccessTools2.DeclaredMethod(typeof(GraphicsContext), "CreateContext"), + postfix: AccessTools2.DeclaredMethod(typeof(GraphicsContextManager), nameof(CreateContextPostfix))); + if (!res2) return false; + return true; + } + + private static bool GetTexturePrefix(string textureName, ref OpenGLTexture? __result) + { + if (!Textures.TryGetValue(textureName, out __result)) return true; - } + return false; + } - private static bool GetTexturePrefix(string textureName, ref OpenGLTexture? __result) - { - if (!Textures.TryGetValue(textureName, out __result)) - return true; - return false; - } + private static void CreateContextPostfix(GraphicsContext __instance) + { + Instance = new(__instance); - private static void CreateContextPostfix(GraphicsContext __instance) + foreach (var (name, func) in DeferredInitialization) { - Instance = new(__instance); - - foreach (var (name, func) in DeferredInitialization) - { - Textures[name] = func(); - } + Textures[name] = func(); } - - private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; } + + private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/ResourceManagers/SpriteDataManager.cs b/src/Bannerlord.LauncherEx/ResourceManagers/SpriteDataManager.cs index 2566559..3e6de0a 100644 --- a/src/Bannerlord.LauncherEx/ResourceManagers/SpriteDataManager.cs +++ b/src/Bannerlord.LauncherEx/ResourceManagers/SpriteDataManager.cs @@ -8,233 +8,232 @@ using TaleWorlds.TwoDimension; -namespace Bannerlord.LauncherEx.ResourceManagers +namespace Bannerlord.LauncherEx.ResourceManagers; + +internal static class SpriteDataManager { - internal static class SpriteDataManager + // Replaces the Sprite Sheet mechanism with a single texture + private sealed class SpriteGenericFromTexture : SpriteGeneric { - // Replaces the Sprite Sheet mechanism with a single texture - private sealed class SpriteGenericFromTexture : SpriteGeneric - { - private delegate void SetIsLoadedDelegate(SpriteCategory instance, bool value); - private static readonly SetIsLoadedDelegate? SetIsLoaded = - AccessTools2.GetPropertySetterDelegate(typeof(SpriteCategory), "IsLoaded"); + private delegate void SetIsLoadedDelegate(SpriteCategory instance, bool value); + private static readonly SetIsLoadedDelegate? SetIsLoaded = + AccessTools2.GetPropertySetterDelegate(typeof(SpriteCategory), "IsLoaded"); - private static SpritePart GetSpritePart(string name, Texture texture) + private static SpritePart GetSpritePart(string name, Texture texture) + { + var data = new SpriteData(name); + var category = new SpriteCategory(name, data, 0) { - var data = new SpriteData(name); - var category = new SpriteCategory(name, data, 0) - { - SpriteSheets = + SpriteSheets = { texture }, - SpriteSheetCount = 1 - }; - SetIsLoaded?.Invoke(category, true); + SpriteSheetCount = 1 + }; + SetIsLoaded?.Invoke(category, true); - return new SpritePart(name, category, texture.Width, texture.Height) - { - SheetID = 1, - }; - } - public SpriteGenericFromTexture(string name, Texture texture) : base(name, GetSpritePart(name, texture)) { } + return new SpritePart(name, category, texture.Width, texture.Height) + { + SheetID = 1, + }; } + public SpriteGenericFromTexture(string name, Texture texture) : base(name, GetSpritePart(name, texture)) { } + } - private sealed class SpriteFromTexture : Sprite - { - private static readonly AccessTools.StructFieldRef? FieldMapX = AccessTools2.StructFieldRefAccess("MapX"); - private static readonly AccessTools.StructFieldRef? FieldMapY = AccessTools2.StructFieldRefAccess("MapY"); - private static readonly AccessTools.StructFieldRef? FieldScale = AccessTools2.StructFieldRefAccess("Scale"); - private static readonly AccessTools.StructFieldRef? FieldWidth = AccessTools2.StructFieldRefAccess("Width"); - private static readonly AccessTools.StructFieldRef? FieldHeight = AccessTools2.StructFieldRefAccess("Height"); - private static readonly AccessTools.StructFieldRef? FieldHorizontalFlip = AccessTools2.StructFieldRefAccess("HorizontalFlip"); - private static readonly AccessTools.StructFieldRef? FieldVerticalFlip = AccessTools2.StructFieldRefAccess("VerticalFlip"); + private sealed class SpriteFromTexture : Sprite + { + private static readonly AccessTools.StructFieldRef? FieldMapX = AccessTools2.StructFieldRefAccess("MapX"); + private static readonly AccessTools.StructFieldRef? FieldMapY = AccessTools2.StructFieldRefAccess("MapY"); + private static readonly AccessTools.StructFieldRef? FieldScale = AccessTools2.StructFieldRefAccess("Scale"); + private static readonly AccessTools.StructFieldRef? FieldWidth = AccessTools2.StructFieldRefAccess("Width"); + private static readonly AccessTools.StructFieldRef? FieldHeight = AccessTools2.StructFieldRefAccess("Height"); + private static readonly AccessTools.StructFieldRef? FieldHorizontalFlip = AccessTools2.StructFieldRefAccess("HorizontalFlip"); + private static readonly AccessTools.StructFieldRef? FieldVerticalFlip = AccessTools2.StructFieldRefAccess("VerticalFlip"); - private static readonly Type? Vector2 = Type.GetType("System.Numerics.Vector2, System.Numerics.Vectors"); - private static readonly ConstructorInfo? Vector2Constructor = AccessTools2.Constructor(Vector2!, new[] { typeof(float), typeof(float) }); - private static readonly MethodInfo? CreateQuad = AccessTools2.Method("TaleWorlds.TwoDimension.DrawObject2D:CreateQuad"); + private static readonly Type? Vector2 = Type.GetType("System.Numerics.Vector2, System.Numerics.Vectors"); + private static readonly ConstructorInfo? Vector2Constructor = AccessTools2.Constructor(Vector2!, new[] { typeof(float), typeof(float) }); + private static readonly MethodInfo? CreateQuad = AccessTools2.Method("TaleWorlds.TwoDimension.DrawObject2D:CreateQuad"); - public override Texture Texture { get; } + public override Texture Texture { get; } - private readonly float[] _vertices; - private readonly float[] _uvs; - private readonly uint[] _indices; + private readonly float[] _vertices; + private readonly float[] _uvs; + private readonly uint[] _indices; - public SpriteFromTexture(Texture texture) : this("Sprite", texture) { } - public SpriteFromTexture(string name, Texture texture) : base(name, texture.Width, texture.Height) - { - Texture = texture; - _vertices = new float[8]; - _uvs = new float[8]; - _indices = new uint[6]; - _indices[0] = 0U; - _indices[1] = 1U; - _indices[2] = 2U; - _indices[3] = 0U; - _indices[4] = 2U; - _indices[5] = 3U; - } + public SpriteFromTexture(Texture texture) : this("Sprite", texture) { } + public SpriteFromTexture(string name, Texture texture) : base(name, texture.Width, texture.Height) + { + Texture = texture; + _vertices = new float[8]; + _uvs = new float[8]; + _indices = new uint[6]; + _indices[0] = 0U; + _indices[1] = 1U; + _indices[2] = 2U; + _indices[3] = 0U; + _indices[4] = 2U; + _indices[5] = 3U; + } - public override float GetScaleToUse(float width, float height, float scale) => scale; + public override float GetScaleToUse(float width, float height, float scale) => scale; - protected override DrawObject2D GetArrays(SpriteDrawData spriteDrawData) + protected override DrawObject2D GetArrays(SpriteDrawData spriteDrawData) + { + if (CachedDrawObject is not null && CachedDrawData == spriteDrawData) + return CachedDrawObject; + + if (FieldMapX is null || FieldMapY is null || FieldWidth is null || FieldHeight is null || FieldHorizontalFlip is null || FieldVerticalFlip is null) + return null!; + + var mapX = FieldMapX(ref spriteDrawData); + var mapY = FieldMapY(ref spriteDrawData); + var width = FieldWidth(ref spriteDrawData); + var height = FieldHeight(ref spriteDrawData); + var horizontalFlip = FieldHorizontalFlip(ref spriteDrawData); + var verticalFlip = FieldVerticalFlip(ref spriteDrawData); + + //var vec2 = Vector2Constructor.Invoke(new object[] { width, height }); + //var quad1 = CreateQuad.Invoke(null, new object[]{ vec2 }) as DrawObject2D; + //return quad1; + //var quad = DrawObject2D.CreateQuad(new Vector2(width, height)); + //return quad; + + if (mapX == 0f && mapY == 0f) { - if (CachedDrawObject is not null && CachedDrawData == spriteDrawData) - return CachedDrawObject; - - if (FieldMapX is null || FieldMapY is null || FieldWidth is null || FieldHeight is null || FieldHorizontalFlip is null || FieldVerticalFlip is null) - return null!; - - var mapX = FieldMapX(ref spriteDrawData); - var mapY = FieldMapY(ref spriteDrawData); - var width = FieldWidth(ref spriteDrawData); - var height = FieldHeight(ref spriteDrawData); - var horizontalFlip = FieldHorizontalFlip(ref spriteDrawData); - var verticalFlip = FieldVerticalFlip(ref spriteDrawData); - - //var vec2 = Vector2Constructor.Invoke(new object[] { width, height }); - //var quad1 = CreateQuad.Invoke(null, new object[]{ vec2 }) as DrawObject2D; - //return quad1; - //var quad = DrawObject2D.CreateQuad(new Vector2(width, height)); - //return quad; - - if (mapX == 0f && mapY == 0f) - { - PopulateVertices(Texture, mapX, mapY, _vertices, 0, 1f, width, height); - PopulateTextureCoordinates(_uvs, 0, horizontalFlip, verticalFlip); - var drawObject2D = new DrawObject2D(MeshTopology.Triangles, _vertices.ToArray(), _uvs.ToArray(), _indices.ToArray(), 6) - { - DrawObjectType = DrawObjectType.Quad, - Width = width, - Height = height, - MinU = 0f, - MaxU = 1f, - MinV = 0f, - MaxV = 1f - }; - if (horizontalFlip) - { - drawObject2D.MinU = 1f; - drawObject2D.MaxU = 0f; - } - if (verticalFlip) - { - drawObject2D.MinV = 1f; - drawObject2D.MaxV = 0f; - } - - CachedDrawData = spriteDrawData; - CachedDrawObject = drawObject2D; - return drawObject2D; - } - PopulateVertices(Texture, mapX, mapY, _vertices, 0, 1f, width, height); PopulateTextureCoordinates(_uvs, 0, horizontalFlip, verticalFlip); - var drawObject2D2 = new DrawObject2D(MeshTopology.Triangles, _vertices.ToArray(), _uvs.ToArray(), _indices.ToArray(), 6) + var drawObject2D = new DrawObject2D(MeshTopology.Triangles, _vertices.ToArray(), _uvs.ToArray(), _indices.ToArray(), 6) { - DrawObjectType = DrawObjectType.Mesh + DrawObjectType = DrawObjectType.Quad, + Width = width, + Height = height, + MinU = 0f, + MaxU = 1f, + MinV = 0f, + MaxV = 1f }; - - CachedDrawData = spriteDrawData; - CachedDrawObject = drawObject2D2; - return drawObject2D2; - } - - private static void PopulateVertices(Texture texture, float screenX, float screenY, float[] outVertices, int verticesStartIndex, float scale, float customWidth, float customHeight) - { - var widthProp = customWidth / texture.Width; - var heightProp = customHeight / texture.Height; - var widthScaled = texture.Width * scale * widthProp; - var heightScaled = texture.Height * scale * heightProp; - - outVertices[verticesStartIndex] = screenX + 0f; - outVertices[verticesStartIndex + 1] = screenY + 0f; - outVertices[verticesStartIndex + 2] = screenX + 0f; - outVertices[verticesStartIndex + 3] = screenY + heightScaled; - outVertices[verticesStartIndex + 4] = screenX + widthScaled; - outVertices[verticesStartIndex + 5] = screenY + heightScaled; - outVertices[verticesStartIndex + 6] = screenX + widthScaled; - outVertices[verticesStartIndex + 7] = screenY + 0f; - } - private static void PopulateTextureCoordinates(float[] outUVs, int uvsStartIndex, bool horizontalFlip, bool verticalFlip) - { - var minU = 0f; - var maxU = 1f; if (horizontalFlip) { - minU = 1f; - maxU = 0f; + drawObject2D.MinU = 1f; + drawObject2D.MaxU = 0f; } - - var minV = 0f; - var maxV = 1f; if (verticalFlip) { - minV = 1f; - maxV = 0f; + drawObject2D.MinV = 1f; + drawObject2D.MaxV = 0f; } - outUVs[uvsStartIndex] = minU; - outUVs[uvsStartIndex + 1] = minV; - outUVs[uvsStartIndex + 2] = minU; - outUVs[uvsStartIndex + 3] = maxV; - outUVs[uvsStartIndex + 4] = maxU; - outUVs[uvsStartIndex + 5] = maxV; - outUVs[uvsStartIndex + 6] = maxU; - outUVs[uvsStartIndex + 7] = minV; + CachedDrawData = spriteDrawData; + CachedDrawObject = drawObject2D; + return drawObject2D; } - } - - private static readonly Dictionary SpriteNames = new(); - private static readonly List> DeferredInitialization = new(); + PopulateVertices(Texture, mapX, mapY, _vertices, 0, 1f, width, height); + PopulateTextureCoordinates(_uvs, 0, horizontalFlip, verticalFlip); + var drawObject2D2 = new DrawObject2D(MeshTopology.Triangles, _vertices.ToArray(), _uvs.ToArray(), _indices.ToArray(), 6) + { + DrawObjectType = DrawObjectType.Mesh + }; - public static Sprite? Create(string name) => GraphicsContextManager.Instance.TryGetTarget(out var gc) && gc is not null - ? new SpriteFromTexture(name, new Texture(gc.GetTexture(name))) : null; - public static Sprite? CreateGeneric(string name) => GraphicsContextManager.Instance.TryGetTarget(out var gc) && gc is not null - ? new SpriteGenericFromTexture(name, new Texture(gc.GetTexture(name))) : null; - public static void Register(Func func) => DeferredInitialization.Add(func); - public static void CreateAndRegister(string name) => Register(() => Create(name)!); - public static void CreateGenericAndRegister(string name) => Register(() => CreateGeneric(name)!); + CachedDrawData = spriteDrawData; + CachedDrawObject = drawObject2D2; + return drawObject2D2; + } - public static void Clear() + private static void PopulateVertices(Texture texture, float screenX, float screenY, float[] outVertices, int verticesStartIndex, float scale, float customWidth, float customHeight) { - SpriteNames.Clear(); - DeferredInitialization.Clear(); + var widthProp = customWidth / texture.Width; + var heightProp = customHeight / texture.Height; + var widthScaled = texture.Width * scale * widthProp; + var heightScaled = texture.Height * scale * heightProp; + + outVertices[verticesStartIndex] = screenX + 0f; + outVertices[verticesStartIndex + 1] = screenY + 0f; + outVertices[verticesStartIndex + 2] = screenX + 0f; + outVertices[verticesStartIndex + 3] = screenY + heightScaled; + outVertices[verticesStartIndex + 4] = screenX + widthScaled; + outVertices[verticesStartIndex + 5] = screenY + heightScaled; + outVertices[verticesStartIndex + 6] = screenX + widthScaled; + outVertices[verticesStartIndex + 7] = screenY + 0f; } - - internal static bool Enable(Harmony harmony) + private static void PopulateTextureCoordinates(float[] outUVs, int uvsStartIndex, bool horizontalFlip, bool verticalFlip) { - var res1 = harmony.TryPatch( - AccessTools2.Method(typeof(SpriteData), "GetSprite"), - prefix: AccessTools2.DeclaredMethod(typeof(SpriteDataManager), nameof(GetSpritePrefix))); - if (!res1) return false; + var minU = 0f; + var maxU = 1f; + if (horizontalFlip) + { + minU = 1f; + maxU = 0f; + } - var res2 = harmony.TryPatch( - AccessTools2.Method(typeof(SpriteData), "Load"), - postfix: AccessTools2.DeclaredMethod(typeof(SpriteDataManager), nameof(LoadPostfix))); - if (!res2) return false; + var minV = 0f; + var maxV = 1f; + if (verticalFlip) + { + minV = 1f; + maxV = 0f; + } - return true; + outUVs[uvsStartIndex] = minU; + outUVs[uvsStartIndex + 1] = minV; + outUVs[uvsStartIndex + 2] = minU; + outUVs[uvsStartIndex + 3] = maxV; + outUVs[uvsStartIndex + 4] = maxU; + outUVs[uvsStartIndex + 5] = maxV; + outUVs[uvsStartIndex + 6] = maxU; + outUVs[uvsStartIndex + 7] = minV; } + } - private static bool GetSpritePrefix(string name, ref Sprite? __result) - { - if (!SpriteNames.TryGetValue(name, out __result)) - return true; - return false; - } - private static void LoadPostfix() + private static readonly Dictionary SpriteNames = new(); + private static readonly List> DeferredInitialization = new(); + + public static Sprite? Create(string name) => GraphicsContextManager.Instance.TryGetTarget(out var gc) && gc is not null + ? new SpriteFromTexture(name, new Texture(gc.GetTexture(name))) : null; + public static Sprite? CreateGeneric(string name) => GraphicsContextManager.Instance.TryGetTarget(out var gc) && gc is not null + ? new SpriteGenericFromTexture(name, new Texture(gc.GetTexture(name))) : null; + public static void Register(Func func) => DeferredInitialization.Add(func); + public static void CreateAndRegister(string name) => Register(() => Create(name)!); + public static void CreateGenericAndRegister(string name) => Register(() => CreateGeneric(name)!); + + public static void Clear() + { + SpriteNames.Clear(); + DeferredInitialization.Clear(); + } + + internal static bool Enable(Harmony harmony) + { + var res1 = harmony.TryPatch( + AccessTools2.Method(typeof(SpriteData), "GetSprite"), + prefix: AccessTools2.DeclaredMethod(typeof(SpriteDataManager), nameof(GetSpritePrefix))); + if (!res1) return false; + + var res2 = harmony.TryPatch( + AccessTools2.Method(typeof(SpriteData), "Load"), + postfix: AccessTools2.DeclaredMethod(typeof(SpriteDataManager), nameof(LoadPostfix))); + if (!res2) return false; + + return true; + } + + private static bool GetSpritePrefix(string name, ref Sprite? __result) + { + if (!SpriteNames.TryGetValue(name, out __result)) + return true; + return false; + } + + private static void LoadPostfix() + { + foreach (var func in DeferredInitialization) { - foreach (var func in DeferredInitialization) - { - var sprite = func(); - SpriteNames[sprite.Name] = sprite; - } + var sprite = func(); + SpriteNames[sprite.Name] = sprite; } - - private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; } + + private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/ResourceManagers/WidgetFactoryManager.cs b/src/Bannerlord.LauncherEx/ResourceManagers/WidgetFactoryManager.cs index d539eab..b0c8a9a 100644 --- a/src/Bannerlord.LauncherEx/ResourceManagers/WidgetFactoryManager.cs +++ b/src/Bannerlord.LauncherEx/ResourceManagers/WidgetFactoryManager.cs @@ -14,178 +14,177 @@ using TaleWorlds.GauntletUI.BaseTypes; using TaleWorlds.GauntletUI.PrefabSystem; -namespace Bannerlord.LauncherEx.ResourceManagers +namespace Bannerlord.LauncherEx.ResourceManagers; + +/// +/// https://github.com/Aragas/Bannerlord.MBOptionScreen/blob/dev/src/MCM.UI/Patches/WidgetFactoryManager.cs +/// +internal static class WidgetFactoryManager { - /// - /// https://github.com/Aragas/Bannerlord.MBOptionScreen/blob/dev/src/MCM.UI/Patches/WidgetFactoryManager.cs - /// - internal static class WidgetFactoryManager + private delegate void ReloadDelegate(); + private static readonly ReloadDelegate? Reload = + AccessTools2.GetDeclaredDelegate(typeof(WidgetInfo), "Reload"); + + private static readonly AccessTools.FieldRef? _liveCustomTypes = + AccessTools2.FieldRefAccess("_liveCustomTypes"); + + private delegate Widget WidgetConstructor(UIContext uiContext); + private static readonly ConcurrentDictionary WidgetConstructors = new(); + private static readonly Dictionary> CustomTypes = new(); + private static readonly Dictionary BuiltinTypes = new(); + private static readonly Dictionary LiveCustomTypes = new(); + private static readonly Dictionary LiveInstanceTracker = new(); + + private static Harmony? _harmony; + private static WeakReference WidgetFactoryReference { get; } = new(null); + public static void SetWidgetFactory(WidgetFactory widgetFactory) { - private delegate void ReloadDelegate(); - private static readonly ReloadDelegate? Reload = - AccessTools2.GetDeclaredDelegate(typeof(WidgetInfo), "Reload"); - - private static readonly AccessTools.FieldRef? _liveCustomTypes = - AccessTools2.FieldRefAccess("_liveCustomTypes"); - - private delegate Widget WidgetConstructor(UIContext uiContext); - private static readonly ConcurrentDictionary WidgetConstructors = new(); - private static readonly Dictionary> CustomTypes = new(); - private static readonly Dictionary BuiltinTypes = new(); - private static readonly Dictionary LiveCustomTypes = new(); - private static readonly Dictionary LiveInstanceTracker = new(); - - private static Harmony? _harmony; - private static WeakReference WidgetFactoryReference { get; } = new(null); - public static void SetWidgetFactory(WidgetFactory widgetFactory) - { - WidgetFactoryReference.SetTarget(widgetFactory); - } + WidgetFactoryReference.SetTarget(widgetFactory); + } - public static WidgetPrefab? Create(string name, XmlDocument doc) - { - if (!WidgetFactoryReference.TryGetTarget(out var widgetFactory) || widgetFactory is null) - return null; - - return WidgetPrefabPatch.LoadFromDocument( - widgetFactory.PrefabExtensionContext, - widgetFactory.WidgetAttributeContext, - name, - doc); - } - public static void Register(Type widgetType) - { - if (Reload is null) return; + public static WidgetPrefab? Create(string name, XmlDocument doc) + { + if (!WidgetFactoryReference.TryGetTarget(out var widgetFactory) || widgetFactory is null) + return null; + + return WidgetPrefabPatch.LoadFromDocument( + widgetFactory.PrefabExtensionContext, + widgetFactory.WidgetAttributeContext, + name, + doc); + } + public static void Register(Type widgetType) + { + if (Reload is null) return; - BuiltinTypes[widgetType.Name] = widgetType; - Reload(); - } - public static void Register(string name, Func create) => CustomTypes.Add(name, create); - public static void CreateAndRegister(string name, XmlDocument xmlDocument) => Register(name, () => Create($"{name}.xml", xmlDocument)); + BuiltinTypes[widgetType.Name] = widgetType; + Reload(); + } + public static void Register(string name, Func create) => CustomTypes.Add(name, create); + public static void CreateAndRegister(string name, XmlDocument xmlDocument) => Register(name, () => Create($"{name}.xml", xmlDocument)); - public static void Clear() - { - WidgetConstructors.Clear(); - CustomTypes.Clear(); - BuiltinTypes.Clear(); - LiveCustomTypes.Clear(); - LiveInstanceTracker.Clear(); - WidgetFactoryReference.SetTarget(null); - } + public static void Clear() + { + WidgetConstructors.Clear(); + CustomTypes.Clear(); + BuiltinTypes.Clear(); + LiveCustomTypes.Clear(); + LiveInstanceTracker.Clear(); + WidgetFactoryReference.SetTarget(null); + } - public static bool Enable(Harmony harmony) - { - _harmony = harmony; - - var res1 = harmony.TryPatch( - AccessTools2.Method(typeof(WidgetFactory), "GetCustomType"), - prefix: AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(GetCustomTypePrefix))); - if (!res1) return false; - - var res2 = harmony.TryPatch( - AccessTools2.Method(typeof(WidgetFactory), "GetWidgetTypes"), - prefix: AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(GetWidgetTypesPostfix))); - if (!res2) return false; - - var res3 = harmony.TryPatch( - AccessTools2.Method(typeof(WidgetFactory), "IsCustomType"), - prefix: AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(IsCustomTypePrefix))); - if (!res3) return false; - - var res4 = harmony.TryPatch( - AccessTools2.Method(typeof(WidgetFactory), "OnUnload"), - prefix: AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(OnUnloadPrefix))); - if (!res4) return false; - - var res5 = harmony.TryPatch( - AccessTools2.Method(typeof(WidgetFactory), "Initialize"), - prefix: AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(InitializePostfix))); - if (!res5) return false; - - var res6 = harmony.TryPatch( - AccessTools2.Method(typeof(WidgetFactory), "CreateBuiltinWidget"), - prefix: AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(CreateBuiltinWidgetPrefix))); - if (!res6) return false; + public static bool Enable(Harmony harmony) + { + _harmony = harmony; + + var res1 = harmony.TryPatch( + AccessTools2.Method(typeof(WidgetFactory), "GetCustomType"), + prefix: AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(GetCustomTypePrefix))); + if (!res1) return false; + + var res2 = harmony.TryPatch( + AccessTools2.Method(typeof(WidgetFactory), "GetWidgetTypes"), + prefix: AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(GetWidgetTypesPostfix))); + if (!res2) return false; + + var res3 = harmony.TryPatch( + AccessTools2.Method(typeof(WidgetFactory), "IsCustomType"), + prefix: AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(IsCustomTypePrefix))); + if (!res3) return false; + + var res4 = harmony.TryPatch( + AccessTools2.Method(typeof(WidgetFactory), "OnUnload"), + prefix: AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(OnUnloadPrefix))); + if (!res4) return false; + + var res5 = harmony.TryPatch( + AccessTools2.Method(typeof(WidgetFactory), "Initialize"), + prefix: AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(InitializePostfix))); + if (!res5) return false; + + var res6 = harmony.TryPatch( + AccessTools2.Method(typeof(WidgetFactory), "CreateBuiltinWidget"), + prefix: AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(CreateBuiltinWidgetPrefix))); + if (!res6) return false; + + return true; + } + + private static void GetWidgetTypesPostfix(ref IEnumerable __result) + { + __result = __result.Concat(BuiltinTypes.Keys).Concat(CustomTypes.Keys); + } + private static bool CreateBuiltinWidgetPrefix(UIContext context, string typeName, ref object? __result) + { + if (!BuiltinTypes.TryGetValue(typeName, out var type)) return true; - } - private static void GetWidgetTypesPostfix(ref IEnumerable __result) - { - __result = __result.Concat(BuiltinTypes.Keys).Concat(CustomTypes.Keys); - } + var ctor = WidgetConstructors.GetOrAdd(type, static x => AccessTools2.GetDeclaredConstructorDelegate(x, new[] { typeof(UIContext) })); + if (ctor is null) + return true; - private static bool CreateBuiltinWidgetPrefix(UIContext context, string typeName, ref object? __result) - { - if (!BuiltinTypes.TryGetValue(typeName, out var type)) - return true; + __result = ctor(context); + return false; + } - var ctor = WidgetConstructors.GetOrAdd(type, static x => AccessTools2.GetDeclaredConstructorDelegate(x, new[] { typeof(UIContext) })); - if (ctor is null) - return true; + private static bool IsCustomTypePrefix(string typeName, ref bool __result) + { + if (!CustomTypes.ContainsKey(typeName)) + return true; - __result = ctor(context); - return false; - } + __result = true; + return false; + } - private static bool IsCustomTypePrefix(string typeName, ref bool __result) - { - if (!CustomTypes.ContainsKey(typeName)) - return true; + private static bool GetCustomTypePrefix(WidgetFactory __instance, string typeName, ref WidgetPrefab __result) + { + if (_liveCustomTypes?.Invoke(__instance) is { } ____liveCustomTypes && ____liveCustomTypes.Contains(typeName) || !CustomTypes.ContainsKey(typeName)) + return true; - __result = true; + if (LiveCustomTypes.TryGetValue(typeName, out var liveWidgetPrefab)) + { + LiveInstanceTracker[typeName]++; + __result = liveWidgetPrefab; return false; } - private static bool GetCustomTypePrefix(WidgetFactory __instance, string typeName, ref WidgetPrefab __result) + if (CustomTypes[typeName]?.Invoke() is { } widgetPrefab) { - if (_liveCustomTypes?.Invoke(__instance) is { } ____liveCustomTypes && ____liveCustomTypes.Contains(typeName) || !CustomTypes.ContainsKey(typeName)) - return true; - - if (LiveCustomTypes.TryGetValue(typeName, out var liveWidgetPrefab)) - { - LiveInstanceTracker[typeName]++; - __result = liveWidgetPrefab; - return false; - } + LiveCustomTypes.Add(typeName, widgetPrefab); + LiveInstanceTracker[typeName] = 1; - if (CustomTypes[typeName]?.Invoke() is { } widgetPrefab) - { - LiveCustomTypes.Add(typeName, widgetPrefab); - LiveInstanceTracker[typeName] = 1; - - __result = widgetPrefab; - return false; - } - - return true; + __result = widgetPrefab; + return false; } - private static bool OnUnloadPrefix(string typeName) + return true; + } + + private static bool OnUnloadPrefix(string typeName) + { + if (LiveCustomTypes.ContainsKey(typeName)) { - if (LiveCustomTypes.ContainsKey(typeName)) + LiveInstanceTracker[typeName]--; + if (LiveInstanceTracker[typeName] == 0) { - LiveInstanceTracker[typeName]--; - if (LiveInstanceTracker[typeName] == 0) - { - LiveCustomTypes.Remove(typeName); - LiveInstanceTracker.Remove(typeName); - } - return false; + LiveCustomTypes.Remove(typeName); + LiveInstanceTracker.Remove(typeName); } - - return true; + return false; } - private static void InitializePostfix(ref WidgetFactory __instance) - { - SetWidgetFactory(__instance); + return true; + } - _harmony?.Unpatch( - AccessTools2.Method(typeof(WidgetFactory), "Initialize"), - AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(InitializePostfix))); - } + private static void InitializePostfix(ref WidgetFactory __instance) + { + SetWidgetFactory(__instance); - private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; + _harmony?.Unpatch( + AccessTools2.Method(typeof(WidgetFactory), "Initialize"), + AccessTools2.DeclaredMethod(typeof(WidgetFactoryManager), nameof(InitializePostfix))); } + + private static IEnumerable BlankTranspiler(IEnumerable instructions) => instructions; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Resources/Brushes/Launcher.xml b/src/Bannerlord.LauncherEx/Resources/Brushes/Launcher.xml index d6d8b06..862c42a 100644 --- a/src/Bannerlord.LauncherEx/Resources/Brushes/Launcher.xml +++ b/src/Bannerlord.LauncherEx/Resources/Brushes/Launcher.xml @@ -56,148 +56,162 @@ - + - + - + - + - + + + + + + + + + + + - + - + - + - + - + - + diff --git a/src/Bannerlord.LauncherEx/Resources/Localization/EN/strings.xml b/src/Bannerlord.LauncherEx/Resources/Localization/EN/strings.xml index c6eba1b..9cc9d2c 100644 --- a/src/Bannerlord.LauncherEx/Resources/Localization/EN/strings.xml +++ b/src/Bannerlord.LauncherEx/Resources/Localization/EN/strings.xml @@ -119,5 +119,13 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Resources/Localization/JP/strings.xml b/src/Bannerlord.LauncherEx/Resources/Localization/JP/strings.xml index aa2cba2..8c58708 100644 --- a/src/Bannerlord.LauncherEx/Resources/Localization/JP/strings.xml +++ b/src/Bannerlord.LauncherEx/Resources/Localization/JP/strings.xml @@ -98,7 +98,7 @@ - + @@ -109,15 +109,15 @@ - + - - - + + + diff --git a/src/Bannerlord.LauncherEx/Resources/Prefabs/Launcher.Mods.ModuleTuple.xml b/src/Bannerlord.LauncherEx/Resources/Prefabs/Launcher.Mods.ModuleTuple.xml index 47fa96f..f1db73e 100644 --- a/src/Bannerlord.LauncherEx/Resources/Prefabs/Launcher.Mods.ModuleTuple.xml +++ b/src/Bannerlord.LauncherEx/Resources/Prefabs/Launcher.Mods.ModuleTuple.xml @@ -1,24 +1,18 @@ - - - - - - - + - + @@ -27,38 +21,78 @@ - - - + + - - + - + + - + + + + + + + + + + - - + + + - + + + + + - - - + + + - + - + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Bannerlord.LauncherEx/Resources/Prefabs/Launcher.Mods.xml b/src/Bannerlord.LauncherEx/Resources/Prefabs/Launcher.Mods.xml index 54a3ced..12235b0 100644 --- a/src/Bannerlord.LauncherEx/Resources/Prefabs/Launcher.Mods.xml +++ b/src/Bannerlord.LauncherEx/Resources/Prefabs/Launcher.Mods.xml @@ -1,12 +1,16 @@ - + - + + + + + @@ -14,17 +18,69 @@ - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Bannerlord.LauncherEx/TPac/AbstractExternalLoader.cs b/src/Bannerlord.LauncherEx/TPac/AbstractExternalLoader.cs index c56a9c4..916fcd8 100644 --- a/src/Bannerlord.LauncherEx/TPac/AbstractExternalLoader.cs +++ b/src/Bannerlord.LauncherEx/TPac/AbstractExternalLoader.cs @@ -2,34 +2,33 @@ using System.Collections.Generic; using System.IO; -namespace Bannerlord.LauncherEx.TPac +namespace Bannerlord.LauncherEx.TPac; + +internal abstract class AbstractExternalLoader { - internal abstract class AbstractExternalLoader - { - protected static readonly IDictionary EMPTY_USERDATA = new Dictionary(); + protected static readonly IDictionary EMPTY_USERDATA = new Dictionary(); - protected FileInfo? _file; + protected FileInfo? _file; - protected internal ulong _offset; + protected internal ulong _offset; - protected internal ulong _actualSize; + protected internal ulong _actualSize; - protected internal ulong _storageSize; + protected internal ulong _storageSize; - protected internal StorageFormat _storageFormat; + protected internal StorageFormat _storageFormat; - public Guid TypeGuid { internal set; get; } + public Guid TypeGuid { internal set; get; } - public Guid OwnerGuid { set; get; } + public Guid OwnerGuid { set; get; } - protected readonly Lazy> _userdata = new(); + protected readonly Lazy> _userdata = new(); - public IDictionary UserData => _userdata.Value; + public IDictionary UserData => _userdata.Value; - public enum StorageFormat : byte - { - Uncompressed = 0, - LZ4HC = 1 - } + public enum StorageFormat : byte + { + Uncompressed = 0, + LZ4HC = 1 } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/TPac/AssetItem.cs b/src/Bannerlord.LauncherEx/TPac/AssetItem.cs index b5afa8d..8a244c0 100644 --- a/src/Bannerlord.LauncherEx/TPac/AssetItem.cs +++ b/src/Bannerlord.LauncherEx/TPac/AssetItem.cs @@ -1,31 +1,30 @@ using System; using System.IO; -namespace Bannerlord.LauncherEx.TPac -{ - internal class AssetItem - { - public Guid Type { get; private set; } +namespace Bannerlord.LauncherEx.TPac; - public uint Version { get; protected internal set; } +internal class AssetItem +{ + public Guid Type { get; private set; } - public Guid Guid { get; set; } + public uint Version { get; protected internal set; } - public string Name { get; set; } + public Guid Guid { get; set; } - public AssetItem(Guid type) - { - Type = type; - Guid = Guid.Empty; - Name = string.Empty; - } + public string Name { get; set; } - public virtual void ReadMetadata(BinaryReader stream, int totalSize) - { - stream.BaseStream.Seek(totalSize, SeekOrigin.Current); - //_temp_metadata = stream.ReadBytes(totalSize); - } + public AssetItem(Guid type) + { + Type = type; + Guid = Guid.Empty; + Name = string.Empty; + } - public virtual void ConsumeDataSegments(AbstractExternalLoader[] externalData) { } + public virtual void ReadMetadata(BinaryReader stream, int totalSize) + { + stream.BaseStream.Seek(totalSize, SeekOrigin.Current); + //_temp_metadata = stream.ReadBytes(totalSize); } + + public virtual void ConsumeDataSegments(AbstractExternalLoader[] externalData) { } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/TPac/AssetPackage.cs b/src/Bannerlord.LauncherEx/TPac/AssetPackage.cs index 6b14288..a40c213 100644 --- a/src/Bannerlord.LauncherEx/TPac/AssetPackage.cs +++ b/src/Bannerlord.LauncherEx/TPac/AssetPackage.cs @@ -4,103 +4,102 @@ using System.Linq; using System.Runtime.CompilerServices; -namespace Bannerlord.LauncherEx.TPac +namespace Bannerlord.LauncherEx.TPac; + +internal class AssetPackage { - internal class AssetPackage - { - public const uint TPAC_MAGIC_NUMBER = 0x43415054; + public const uint TPAC_MAGIC_NUMBER = 0x43415054; - public bool HeaderLoaded { get; private set; } = false; + public bool HeaderLoaded { get; private set; } = false; - public FileInfo File { get; } + public FileInfo File { get; } - public List Items { get; } = new(); + public List Items { get; } = new(); - public AssetPackage(string filePath) - { - File = new FileInfo(filePath); - } + public AssetPackage(string filePath) + { + File = new FileInfo(filePath); + } - public Texture? GetTexture(string id) + public Texture? GetTexture(string id) + { + Load(); + return Items.OfType().FirstOrDefault(x => x.Name == id); + } + + public void Load() + { + if (!HeaderLoaded) { - Load(); - return Items.OfType().FirstOrDefault(x => x.Name == id); + using var stream = File.OpenBinaryReader(); + Load(stream); } + } - public void Load() + protected virtual void Load(BinaryReader stream) + { + if (!stream.BaseStream.CanSeek) + throw new IOException("The base stream must support random access (seek)."); + HeaderLoaded = true; + + if (stream.ReadUInt32() != TPAC_MAGIC_NUMBER) + throw new IOException($"Not a Tpac file: {File.FullName}"); + var version = stream.ReadUInt32(); + switch (version) { - if (!HeaderLoaded) - { - using var stream = File.OpenBinaryReader(); - Load(stream); - } + case 1: // 1.0.0~1.4.2 + case 2: // since 1.4.3 + break; + default: + throw new Exception("Unsupported Tpac version: " + version); } - protected virtual void Load(BinaryReader stream) + stream.ReadGuid(); + + var resourceNum = stream.ReadUInt32(); + var dataOffset = stream.ReadUInt32(); + stream.ReadUInt32(); // skip reserve + + for (var i = 0; i < resourceNum; i++) { - if (!stream.BaseStream.CanSeek) - throw new IOException("The base stream must support random access (seek)."); - HeaderLoaded = true; - - if (stream.ReadUInt32() != TPAC_MAGIC_NUMBER) - throw new IOException($"Not a Tpac file: {File.FullName}"); - var version = stream.ReadUInt32(); - switch (version) + var typeGuid = stream.ReadGuid(); + TypedAssetFactory.CreateTypedAsset(typeGuid, out var assetItem); + assetItem.Guid = stream.ReadGuid(); + + uint assetVersion = 0; + if (version > 1) assetVersion = stream.ReadUInt32(); + assetItem.Version = assetVersion; + assetItem.Name = stream.ReadSizedString(); + + var metadataSize = stream.ReadUInt64(); + assetItem.ReadMetadata(stream, (int) metadataSize); + var unknownMetadataChecknum = stream.ReadInt64(); + + var dataSegmentNum = stream.ReadInt32(); + var segments = new AbstractExternalLoader[dataSegmentNum]; + for (var j = 0; j < dataSegmentNum; j++) { - case 1: // 1.0.0~1.4.2 - case 2: // since 1.4.3 - break; - default: - throw new Exception("Unsupported Tpac version: " + version); + var segOffset = stream.ReadUInt64(); + var segActualSize = stream.ReadUInt64(); + var segStorageSize = stream.ReadUInt64(); + var segGuid = stream.ReadGuid(); + var segTypeGuid = stream.ReadGuid(); + TypedDataFactory.CreateTypedLoader(segTypeGuid, File, out var result); + result._offset = segOffset; + result._actualSize = segActualSize; + result._storageSize = segStorageSize; + result.OwnerGuid = segGuid; + stream.ReadUInt64(); + stream.ReadUInt32(); + result._storageFormat = (AbstractExternalLoader.StorageFormat) stream.ReadByte(); + segments[j] = result; } - stream.ReadGuid(); - - var resourceNum = stream.ReadUInt32(); - var dataOffset = stream.ReadUInt32(); - stream.ReadUInt32(); // skip reserve + assetItem.ConsumeDataSegments(segments); - for (var i = 0; i < resourceNum; i++) - { - var typeGuid = stream.ReadGuid(); - TypedAssetFactory.CreateTypedAsset(typeGuid, out var assetItem); - assetItem.Guid = stream.ReadGuid(); - - uint assetVersion = 0; - if (version > 1) assetVersion = stream.ReadUInt32(); - assetItem.Version = assetVersion; - assetItem.Name = stream.ReadSizedString(); - - var metadataSize = stream.ReadUInt64(); - assetItem.ReadMetadata(stream, (int) metadataSize); - var unknownMetadataChecknum = stream.ReadInt64(); - - var dataSegmentNum = stream.ReadInt32(); - var segments = new AbstractExternalLoader[dataSegmentNum]; - for (var j = 0; j < dataSegmentNum; j++) - { - var segOffset = stream.ReadUInt64(); - var segActualSize = stream.ReadUInt64(); - var segStorageSize = stream.ReadUInt64(); - var segGuid = stream.ReadGuid(); - var segTypeGuid = stream.ReadGuid(); - TypedDataFactory.CreateTypedLoader(segTypeGuid, File, out var result); - result._offset = segOffset; - result._actualSize = segActualSize; - result._storageSize = segStorageSize; - result.OwnerGuid = segGuid; - stream.ReadUInt64(); - stream.ReadUInt32(); - result._storageFormat = (AbstractExternalLoader.StorageFormat) stream.ReadByte(); - segments[j] = result; - } - - assetItem.ConsumeDataSegments(segments); - - var depNum = stream.ReadInt32(); - stream.BaseStream.Seek(depNum * 3 * Unsafe.SizeOf(), SeekOrigin.Current); - Items.Add(assetItem); - } + var depNum = stream.ReadInt32(); + stream.BaseStream.Seek(depNum * 3 * Unsafe.SizeOf(), SeekOrigin.Current); + Items.Add(assetItem); } } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/TPac/ExternalData.cs b/src/Bannerlord.LauncherEx/TPac/ExternalData.cs index efedce9..10a8c86 100644 --- a/src/Bannerlord.LauncherEx/TPac/ExternalData.cs +++ b/src/Bannerlord.LauncherEx/TPac/ExternalData.cs @@ -2,26 +2,25 @@ using System.Collections.Generic; using System.IO; -namespace Bannerlord.LauncherEx.TPac +namespace Bannerlord.LauncherEx.TPac; + +internal class ExternalData { - internal class ExternalData - { - public Guid TypeGuid { get; protected internal set; } + public Guid TypeGuid { get; protected internal set; } - public ExternalData() - { - TypeGuid = Guid.Empty; - } + public ExternalData() + { + TypeGuid = Guid.Empty; + } - public ExternalData(Guid typeGuid) - { - TypeGuid = typeGuid; - } + public ExternalData(Guid typeGuid) + { + TypeGuid = typeGuid; + } - public virtual void ReadData(BinaryReader stream, IDictionary userdata, int totalSize) - { - stream.BaseStream.Seek(totalSize, SeekOrigin.Current); - //_unknownRawData = stream.ReadBytes(totalSize); - } + public virtual void ReadData(BinaryReader stream, IDictionary userdata, int totalSize) + { + stream.BaseStream.Seek(totalSize, SeekOrigin.Current); + //_unknownRawData = stream.ReadBytes(totalSize); } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/TPac/ExternalLoader.cs b/src/Bannerlord.LauncherEx/TPac/ExternalLoader.cs index 13acf46..be569d1 100644 --- a/src/Bannerlord.LauncherEx/TPac/ExternalLoader.cs +++ b/src/Bannerlord.LauncherEx/TPac/ExternalLoader.cs @@ -3,91 +3,90 @@ using System; using System.IO; -namespace Bannerlord.LauncherEx.TPac +namespace Bannerlord.LauncherEx.TPac; + +internal sealed class ExternalLoader : AbstractExternalLoader where T : ExternalData, new() { - internal sealed class ExternalLoader : AbstractExternalLoader where T : ExternalData, new() + private const int IMMEDIATELY_GC_THRESHOLD = 128 * 1024 * 1024 - 1; + + internal ExternalLoader(FileInfo file) { - private const int IMMEDIATELY_GC_THRESHOLD = 128 * 1024 * 1024 - 1; + _file = file; + if (!_file.Exists) throw new FileNotFoundException("Cannot find file: " + _file.FullName); + OwnerGuid = Guid.Empty; + } + + public ExternalLoader() : this(new T()) { } - internal ExternalLoader(FileInfo file) + public ExternalLoader(T data) + { + _file = null; + //OwnerGuid = Guid.NewGuid(); // sz: don't assign a random guid for it makes no sense + OwnerGuid = Guid.Empty; + if (data.TypeGuid == Guid.Empty) { - _file = file; - if (!_file.Exists) throw new FileNotFoundException("Cannot find file: " + _file.FullName); - OwnerGuid = Guid.Empty; + throw new ArgumentException("The data which were assigned to the loader must have a type guid."); } - public ExternalLoader() : this(new T()) { } + TypeGuid = data.TypeGuid; + } - public ExternalLoader(T data) - { - _file = null; - //OwnerGuid = Guid.NewGuid(); // sz: don't assign a random guid for it makes no sense - OwnerGuid = Guid.Empty; - if (data.TypeGuid == Guid.Empty) - { - throw new ArgumentException("The data which were assigned to the loader must have a type guid."); - } + private T ReadData() + { + if (_file is null) throw new InvalidOperationException("Can't read data from file"); + using var stream = _file.OpenBinaryReader(); + return ReadData(stream); + } - TypeGuid = data.TypeGuid; + private T ReadData(BinaryReader fullStream) + { + var rawData = GetRawData(fullStream); + var data = new T(); + using (var stream = rawData.CreateBinaryReader()) + { + data.ReadData(stream, _userdata.IsValueCreated ? _userdata.Value : EMPTY_USERDATA, (int) _actualSize); } - private T ReadData() + if (rawData.Length > IMMEDIATELY_GC_THRESHOLD) { - if (_file is null) throw new InvalidOperationException("Can't read data from file"); - using var stream = _file.OpenBinaryReader(); - return ReadData(stream); + rawData = null; + GC.Collect(); } - private T ReadData(BinaryReader fullStream) + data.TypeGuid = TypeGuid; + + return data; + } + + private byte[] GetRawData(BinaryReader stream) + { + byte[]? rawData; + if (!stream.BaseStream.CanSeek) + throw new IOException("The base stream must support random access (seek)"); + stream.BaseStream.Seek((long) _offset, SeekOrigin.Begin); + switch (_storageFormat) { - var rawData = GetRawData(fullStream); - var data = new T(); - using (var stream = rawData.CreateBinaryReader()) + case StorageFormat.Uncompressed: { - data.ReadData(stream, _userdata.IsValueCreated ? _userdata.Value : EMPTY_USERDATA, (int) _actualSize); + rawData = stream.ReadBytes((int) _storageSize); + break; } - - if (rawData.Length > IMMEDIATELY_GC_THRESHOLD) + case StorageFormat.LZ4HC: { - rawData = null; - GC.Collect(); + rawData = stream.ReadBytes((int) _storageSize); + rawData = LZ4Decompress(rawData, (int) _actualSize); + if (rawData.Length > IMMEDIATELY_GC_THRESHOLD) + GC.Collect(); + break; } - - data.TypeGuid = TypeGuid; - - return data; + default: + throw new ArgumentException("Unsupported data storage format: " + _storageFormat); } - private byte[] GetRawData(BinaryReader stream) - { - byte[]? rawData; - if (!stream.BaseStream.CanSeek) - throw new IOException("The base stream must support random access (seek)"); - stream.BaseStream.Seek((long) _offset, SeekOrigin.Begin); - switch (_storageFormat) - { - case StorageFormat.Uncompressed: - { - rawData = stream.ReadBytes((int) _storageSize); - break; - } - case StorageFormat.LZ4HC: - { - rawData = stream.ReadBytes((int) _storageSize); - rawData = LZ4Decompress(rawData, (int) _actualSize); - if (rawData.Length > IMMEDIATELY_GC_THRESHOLD) - GC.Collect(); - break; - } - default: - throw new ArgumentException("Unsupported data storage format: " + _storageFormat); - } - - return rawData; - } + return rawData; + } - public T GetData() => ReadData(); + public T GetData() => ReadData(); - private static byte[] LZ4Decompress(byte[] input, int outLength) => LZ4Codec.Decode(input, 0, input.Length, outLength); - } + private static byte[] LZ4Decompress(byte[] input, int outLength) => LZ4Codec.Decode(input, 0, input.Length, outLength); } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/TPac/Texture.cs b/src/Bannerlord.LauncherEx/TPac/Texture.cs index 94262f6..d2a1317 100644 --- a/src/Bannerlord.LauncherEx/TPac/Texture.cs +++ b/src/Bannerlord.LauncherEx/TPac/Texture.cs @@ -2,124 +2,123 @@ using System.Collections.Generic; using System.IO; -namespace Bannerlord.LauncherEx.TPac +namespace Bannerlord.LauncherEx.TPac; + +internal class Texture : AssetItem { - internal class Texture : AssetItem + public static readonly Guid TYPE_GUID = Guid.Parse("c974cbcb-5f1c-49f6-9a32-2b5b6c92c2e8"); + + + public List Flags { set; get; } = new(); + /* + dont_degrade 0x2 + dont_delay_loading 0x80 + is_envmap 0x1000000 + is_specularmap 0x800000 + is_bumpmap 0x400000 + for_terrain 0x200000 + for_colorgrade 0x100000 + for_skybox_background 0x4000000 + for_skybox_cloud 0x8000000 + for_skybox_sun 0x10000000 + dont_compress 0x20 + ignore_alpha 0x80000 + dont_resize_in_atlas 0x100 + */ + + public string Source { set; get; } + public uint Width { set; get; } + public uint Height { set; get; } + public byte MipmapCount { set; get; } + public ushort ArrayCount { set; get; } + public TextureFormat Format { set; get; } + + private string? _rawFormat; + + public List SystemFlags { set; get; } = new(); + /* + has_alpha 0x8000 + is_cubemap 0x2000 + */ + + public ExternalLoader? TexturePixels { set; get; } + + public bool HasPixelData => TexturePixels is not null; + + public Texture() : base(TYPE_GUID) + { + Source = string.Empty; + } + + public override void ReadMetadata(BinaryReader stream, int totalSize) { - public static readonly Guid TYPE_GUID = Guid.Parse("c974cbcb-5f1c-49f6-9a32-2b5b6c92c2e8"); - - - public List Flags { set; get; } = new(); - /* - dont_degrade 0x2 - dont_delay_loading 0x80 - is_envmap 0x1000000 - is_specularmap 0x800000 - is_bumpmap 0x400000 - for_terrain 0x200000 - for_colorgrade 0x100000 - for_skybox_background 0x4000000 - for_skybox_cloud 0x8000000 - for_skybox_sun 0x10000000 - dont_compress 0x20 - ignore_alpha 0x80000 - dont_resize_in_atlas 0x100 - */ - - public string Source { set; get; } - public uint Width { set; get; } - public uint Height { set; get; } - public byte MipmapCount { set; get; } - public ushort ArrayCount { set; get; } - public TextureFormat Format { set; get; } - - private string? _rawFormat; - - public List SystemFlags { set; get; } = new(); - /* - has_alpha 0x8000 - is_cubemap 0x2000 - */ - - public ExternalLoader? TexturePixels { set; get; } - - public bool HasPixelData => TexturePixels is not null; - - public Texture() : base(TYPE_GUID) + var pos = stream.BaseStream.Position; + var version = stream.ReadUInt32(); + stream.ReadGuid(); + var UnknownUint1 = stream.ReadUInt32(); + Source = stream.ReadSizedString(); + var UnknownUlong = stream.ReadUInt64(); + var UnknownBool = stream.ReadBoolean(); + var UnknownUint2 = stream.ReadUInt32(); + Flags = stream.ReadStringList(); + var UnknownUint3 = stream.ReadUInt32(); + + var UnknownByte = stream.ReadByte(); + Width = stream.ReadUInt32(); + Height = stream.ReadUInt32(); + var UnknownUint4 = stream.ReadUInt32(); + MipmapCount = stream.ReadByte(); + ArrayCount = stream.ReadUInt16(); + _rawFormat = stream.ReadSizedString(); + Format = Enum.TryParse(_rawFormat, true, out TextureFormat format) ? format : TextureFormat.UNKNOWN; + + var UnknownUint5 = stream.ReadUInt32(); + SystemFlags = stream.ReadStringList(); + + if (UnknownUint3 > 0) { - Source = string.Empty; + var UnknownUint6 = stream.ReadUInt32(); + var UnknownUint7 = stream.ReadUInt32(); } - public override void ReadMetadata(BinaryReader stream, int totalSize) + // dirty hack for 1.5.0 + // TW introduced a new field for the metadata of texture since 1.5.0 + // but they didn't bump the version of metadata + if (version >= 1 || totalSize - (stream.BaseStream.Position - pos) == 4) { - var pos = stream.BaseStream.Position; - var version = stream.ReadUInt32(); - stream.ReadGuid(); - var UnknownUint1 = stream.ReadUInt32(); - Source = stream.ReadSizedString(); - var UnknownUlong = stream.ReadUInt64(); - var UnknownBool = stream.ReadBoolean(); - var UnknownUint2 = stream.ReadUInt32(); - Flags = stream.ReadStringList(); - var UnknownUint3 = stream.ReadUInt32(); - - var UnknownByte = stream.ReadByte(); - Width = stream.ReadUInt32(); - Height = stream.ReadUInt32(); - var UnknownUint4 = stream.ReadUInt32(); - MipmapCount = stream.ReadByte(); - ArrayCount = stream.ReadUInt16(); - _rawFormat = stream.ReadSizedString(); - Format = Enum.TryParse(_rawFormat, true, out TextureFormat format) ? format : TextureFormat.UNKNOWN; - - var UnknownUint5 = stream.ReadUInt32(); - SystemFlags = stream.ReadStringList(); - - if (UnknownUint3 > 0) + var numPair = stream.ReadUInt32(); + for (var i = 0; i < numPair; i++) { - var UnknownUint6 = stream.ReadUInt32(); - var UnknownUint7 = stream.ReadUInt32(); - } - - // dirty hack for 1.5.0 - // TW introduced a new field for the metadata of texture since 1.5.0 - // but they didn't bump the version of metadata - if (version >= 1 || totalSize - (stream.BaseStream.Position - pos) == 4) - { - var numPair = stream.ReadUInt32(); - for (var i = 0; i < numPair; i++) - { - stream.ReadGuid(); - stream.ReadGuid(); - } + stream.ReadGuid(); + stream.ReadGuid(); } + } - if (version >= 2) - { - var UnknownUlong2 = stream.ReadUInt64(); - } + if (version >= 2) + { + var UnknownUlong2 = stream.ReadUInt64(); } + } - public override void ConsumeDataSegments(AbstractExternalLoader[] externalData) + public override void ConsumeDataSegments(AbstractExternalLoader[] externalData) + { + foreach (var externalLoader in externalData) { - foreach (var externalLoader in externalData) + if (externalLoader is ExternalLoader pixelData) { - if (externalLoader is ExternalLoader pixelData) - { - var ud = pixelData.UserData; - ud[TexturePixelData.KEY_WIDTH] = (int) Width; - ud[TexturePixelData.KEY_HEIGHT] = (int) Height; - ud[TexturePixelData.KEY_ARRAY] = (int) ArrayCount; - ud[TexturePixelData.KEY_MIPMAP] = (int) MipmapCount; - ud[TexturePixelData.KEY_FORMAT] = Format; - TexturePixels = pixelData; - } - /*else if (externalLoader is ExternalLoader importSetting) - { - }*/ + var ud = pixelData.UserData; + ud[TexturePixelData.KEY_WIDTH] = (int) Width; + ud[TexturePixelData.KEY_HEIGHT] = (int) Height; + ud[TexturePixelData.KEY_ARRAY] = (int) ArrayCount; + ud[TexturePixelData.KEY_MIPMAP] = (int) MipmapCount; + ud[TexturePixelData.KEY_FORMAT] = Format; + TexturePixels = pixelData; } - - base.ConsumeDataSegments(externalData); + /*else if (externalLoader is ExternalLoader importSetting) + { + }*/ } + + base.ConsumeDataSegments(externalData); } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/TPac/TextureFormat.cs b/src/Bannerlord.LauncherEx/TPac/TextureFormat.cs index ca29014..5575873 100644 --- a/src/Bannerlord.LauncherEx/TPac/TextureFormat.cs +++ b/src/Bannerlord.LauncherEx/TPac/TextureFormat.cs @@ -1,59 +1,58 @@ -namespace Bannerlord.LauncherEx.TPac +namespace Bannerlord.LauncherEx.TPac; + +internal enum TextureFormat { - internal enum TextureFormat - { - UNKNOWN, - B8G8R8A8_UNORM, - B8G8R8X8_UNORM, - R16G16_UNORM, - R16G16F, - R32_UINT, - A8_UNORM, - R8G8B8A8_UNORM, - R8G8B8A8_UINT, - R16G16B16A16_UNORM, - L8_UNORM, - R8_UNORM, - R16_UNORM, - R16F, - DXT1, - DXT2, - DXT3, - DXT4, - DXT5, - BC4, - BC5, - BC6H_UF16, - BC7, - D24_UNORM_S8_UINT, - D24_UNORM_X8_UINT, - D16_UNORM, - D32F, - L16_UNORM, - INDEX16, - INDEX32, - R16G16B16A16F, - R32F, - R32G32B32F, - R32G32B32A32F, - DF24, - ATOC, - A2M0, - A2M1, - R11G11B10F, - R16G16B16, - R8G8B8, - B8G8R8, - R32G32B32A32_UINT, - R8_UINT, - R16_UINT, - R24G8_TYPELESS, - R32G32B32_UINT, - D32_S8X24_UINT, - R16G16_UINT, - R8G8_UNORM, - R32G32F, - R32G32_UINT, - R16G16B16A16_UINT - } + UNKNOWN, + B8G8R8A8_UNORM, + B8G8R8X8_UNORM, + R16G16_UNORM, + R16G16F, + R32_UINT, + A8_UNORM, + R8G8B8A8_UNORM, + R8G8B8A8_UINT, + R16G16B16A16_UNORM, + L8_UNORM, + R8_UNORM, + R16_UNORM, + R16F, + DXT1, + DXT2, + DXT3, + DXT4, + DXT5, + BC4, + BC5, + BC6H_UF16, + BC7, + D24_UNORM_S8_UINT, + D24_UNORM_X8_UINT, + D16_UNORM, + D32F, + L16_UNORM, + INDEX16, + INDEX32, + R16G16B16A16F, + R32F, + R32G32B32F, + R32G32B32A32F, + DF24, + ATOC, + A2M0, + A2M1, + R11G11B10F, + R16G16B16, + R8G8B8, + B8G8R8, + R32G32B32A32_UINT, + R8_UINT, + R16_UINT, + R24G8_TYPELESS, + R32G32B32_UINT, + D32_S8X24_UINT, + R16G16_UINT, + R8G8_UNORM, + R32G32F, + R32G32_UINT, + R16G16B16A16_UINT } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/TPac/TexturePixelData.cs b/src/Bannerlord.LauncherEx/TPac/TexturePixelData.cs index 8ce8ef4..f7b0870 100644 --- a/src/Bannerlord.LauncherEx/TPac/TexturePixelData.cs +++ b/src/Bannerlord.LauncherEx/TPac/TexturePixelData.cs @@ -2,92 +2,91 @@ using System.Collections.Generic; using System.IO; -namespace Bannerlord.LauncherEx.TPac +namespace Bannerlord.LauncherEx.TPac; + +internal class TexturePixelData : ExternalData { - internal class TexturePixelData : ExternalData - { - public static readonly Guid TYPE_GUID = Guid.Parse("70ee4e2c-79e4-4b2d-8d54-d53ecd2a559c"); + public static readonly Guid TYPE_GUID = Guid.Parse("70ee4e2c-79e4-4b2d-8d54-d53ecd2a559c"); - public const string KEY_WIDTH = "width"; + public const string KEY_WIDTH = "width"; - public const string KEY_HEIGHT = "height"; + public const string KEY_HEIGHT = "height"; - public const string KEY_ARRAY = "array"; + public const string KEY_ARRAY = "array"; - public const string KEY_MIPMAP = "mipmap"; + public const string KEY_MIPMAP = "mipmap"; - public const string KEY_FORMAT = "format"; + public const string KEY_FORMAT = "format"; - /*public const string KEY_PIXELSIZE = "pixelSize"; - public const string KEY_ALIGN = "align";*/ + /*public const string KEY_PIXELSIZE = "pixelSize"; + public const string KEY_ALIGN = "align";*/ - public byte[][][] RawImage { set; get; } = Array.Empty(); + public byte[][][] RawImage { set; get; } = Array.Empty(); - public byte[] PrimaryRawImage => RawImage[0][0]; + public byte[] PrimaryRawImage => RawImage[0][0]; - public TexturePixelData() : base(TYPE_GUID) { } + public TexturePixelData() : base(TYPE_GUID) { } - public override void ReadData(BinaryReader stream, IDictionary userdata, int totalSize) + public override void ReadData(BinaryReader stream, IDictionary userdata, int totalSize) + { + int array = 1, mipmap = 1; + if (userdata.TryGetValue(KEY_ARRAY, out var arrayObj)) { - int array = 1, mipmap = 1; - if (userdata.TryGetValue(KEY_ARRAY, out var arrayObj)) - { - array = (int) arrayObj; - } + array = (int) arrayObj; + } - if (userdata.TryGetValue(KEY_MIPMAP, out var mipmapObj)) - { - mipmap = (int) mipmapObj; - } + if (userdata.TryGetValue(KEY_MIPMAP, out var mipmapObj)) + { + mipmap = (int) mipmapObj; + } - if (!userdata.TryGetValue(KEY_WIDTH, out var widthObj) || !userdata.TryGetValue(KEY_HEIGHT, out var heightObj) || !userdata.TryGetValue(KEY_FORMAT, out var formatObj)) - { - throw new ArgumentException("No enough user data for width, height and format"); - } + if (!userdata.TryGetValue(KEY_WIDTH, out var widthObj) || !userdata.TryGetValue(KEY_HEIGHT, out var heightObj) || !userdata.TryGetValue(KEY_FORMAT, out var formatObj)) + { + throw new ArgumentException("No enough user data for width, height and format"); + } - var width = (int) widthObj; - var height = (int) heightObj; - var format = (TextureFormat) formatObj; - var aligh = format.GetAlignSize(); - var pixelSize = format.GetBitsPerPixel(); + var width = (int) widthObj; + var height = (int) heightObj; + var format = (TextureFormat) formatObj; + var aligh = format.GetAlignSize(); + var pixelSize = format.GetBitsPerPixel(); - //var raw = stream.ReadBytes(totalSize); + //var raw = stream.ReadBytes(totalSize); - var raw = new byte[array][][]; - for (var a = 0; a < array; a++) + var raw = new byte[array][][]; + for (var a = 0; a < array; a++) + { + raw[a] = new byte[mipmap][]; + var imageWidth = width; + var imageHeight = height; + for (var m = 0; m < mipmap; m++) { - raw[a] = new byte[mipmap][]; - var imageWidth = width; - var imageHeight = height; - for (var m = 0; m < mipmap; m++) + var alignedWidth = imageWidth; + var alignedHeight = imageHeight; + if (alignedWidth % aligh != 0) { - var alignedWidth = imageWidth; - var alignedHeight = imageHeight; - if (alignedWidth % aligh != 0) - { - alignedWidth += aligh - (alignedWidth % aligh); - } - - if (alignedHeight % aligh != 0) - { - alignedHeight += aligh - (alignedHeight % aligh); - } - - // prevent from overflow for mega textures or underflow for very small mipmap - // should be alignedWidth * alignedHeight * pixelSize / 8 - var readSize = alignedWidth * alignedHeight; - // if readSize & 7 != 0, then it cannot be divided exactly - if (readSize >= 8 && (readSize & 7) == 0) - readSize = readSize / 8 * pixelSize; - else - readSize = readSize * pixelSize / 8; - raw[a][m] = stream.ReadBytes(readSize); - imageWidth = Math.Max(imageWidth >> 1, 1); - imageHeight = Math.Max(imageHeight >> 1, 1); + alignedWidth += aligh - (alignedWidth % aligh); } - } - RawImage = raw; + if (alignedHeight % aligh != 0) + { + alignedHeight += aligh - (alignedHeight % aligh); + } + + // prevent from overflow for mega textures or underflow for very small mipmap + // should be alignedWidth * alignedHeight * pixelSize / 8 + var readSize = alignedWidth * alignedHeight; + // if readSize & 7 != 0, then it cannot be divided exactly + if (readSize >= 8 && (readSize & 7) == 0) + readSize = readSize / 8 * pixelSize; + else + readSize = readSize * pixelSize / 8; + raw[a][m] = stream.ReadBytes(readSize); + imageWidth = Math.Max(imageWidth >> 1, 1); + imageHeight = Math.Max(imageHeight >> 1, 1); + } } + + RawImage = raw; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/TPac/TextureUtils.cs b/src/Bannerlord.LauncherEx/TPac/TextureUtils.cs index abc556c..4a11f6b 100644 --- a/src/Bannerlord.LauncherEx/TPac/TextureUtils.cs +++ b/src/Bannerlord.LauncherEx/TPac/TextureUtils.cs @@ -3,1145 +3,1144 @@ using System.Drawing.Imaging; using System.Runtime.InteropServices; -namespace Bannerlord.LauncherEx.TPac +namespace Bannerlord.LauncherEx.TPac; + +internal static class TextureUtil { - internal static class TextureUtil + public static bool IsPremultiplied(this TextureFormat format) { - public static bool IsPremultiplied(this TextureFormat format) - { - return format is TextureFormat.DXT2 or TextureFormat.DXT4; - } + return format is TextureFormat.DXT2 or TextureFormat.DXT4; + } - public static bool IsSupported(this TextureFormat format) => format switch - { - // in fact R16G16B16 can't be exported, too - TextureFormat.DF24 => false, - TextureFormat.ATOC => false, - TextureFormat.A2M0 => false, - TextureFormat.A2M1 => false, - TextureFormat.BC6H_UF16 => false, - TextureFormat.BC7 => false, - TextureFormat.INDEX16 => false, - TextureFormat.INDEX32 => false, - _ => true - }; - - public static int GetAlignSize(this TextureFormat format) => format switch - { - TextureFormat.DXT1 => 4, - TextureFormat.DXT2 => 4, - TextureFormat.DXT3 => 4, - TextureFormat.DXT4 => 4, - TextureFormat.DXT5 => 4, - TextureFormat.BC4 => 4, - TextureFormat.BC5 => 4, - TextureFormat.BC6H_UF16 => 4, - TextureFormat.BC7 => 4, - _ => 1 - }; - - public static int GetBitsPerPixel(this TextureFormat format) => format switch - { - TextureFormat.R32G32B32A32F => 128, - TextureFormat.R32G32B32A32_UINT => 128, - TextureFormat.R32G32B32F => 96, - TextureFormat.R32G32B32_UINT => 96, - TextureFormat.R16G16B16A16_UNORM => 64, - TextureFormat.R16G16B16A16F => 64, - TextureFormat.D32_S8X24_UINT => 64, - TextureFormat.R32G32F => 64, - TextureFormat.R32G32_UINT => 64, - TextureFormat.R16G16B16A16_UINT => 64, - TextureFormat.R16G16B16 => 48, - TextureFormat.B8G8R8A8_UNORM => 32, - TextureFormat.B8G8R8X8_UNORM => 32, - TextureFormat.R16G16_UNORM => 32, - TextureFormat.R16G16F => 32, - TextureFormat.R32_UINT => 32, - TextureFormat.R8G8B8A8_UNORM => 32, - TextureFormat.R8G8B8A8_UINT => 32, - TextureFormat.D24_UNORM_S8_UINT => 32, - TextureFormat.D24_UNORM_X8_UINT => 32, - TextureFormat.D32F => 32, - TextureFormat.R32F => 32, - TextureFormat.R11G11B10F => 32, - TextureFormat.R24G8_TYPELESS => 32, - TextureFormat.R16G16_UINT => 32, - TextureFormat.R8G8B8 => 24, - TextureFormat.B8G8R8 => 24, - TextureFormat.R16_UNORM => 16, - TextureFormat.R16F => 16, - TextureFormat.D16_UNORM => 16, - TextureFormat.L16_UNORM => 16, - TextureFormat.R16_UINT => 16, - TextureFormat.R8G8_UNORM => 16, - TextureFormat.A8_UNORM => 8, - TextureFormat.L8_UNORM => 8, - TextureFormat.R8_UNORM => 8, - TextureFormat.DXT2 => 8, - TextureFormat.DXT3 => 8, - TextureFormat.DXT4 => 8, - TextureFormat.DXT5 => 8, - TextureFormat.BC5 => 8, - TextureFormat.BC6H_UF16 => 8, - TextureFormat.BC7 => 8, - TextureFormat.R8_UINT => 8, - TextureFormat.DXT1 => 4, - TextureFormat.BC4 => 4, - _ => throw new Exception("Unsupported format:" + format.ToString()) - }; - - private static byte FloatToByte(float v) => (byte) TaleWorlds.Library.MathF.Min(Math.Max(TaleWorlds.Library.MathF.Round(v * 255f), 0), 255); - - private static void DecodeTextureDataToWriter(byte[] data, int width, int height, TextureFormat format, PipelineWriter writer, bool silentlyFail = false) + public static bool IsSupported(this TextureFormat format) => format switch + { + // in fact R16G16B16 can't be exported, too + TextureFormat.DF24 => false, + TextureFormat.ATOC => false, + TextureFormat.A2M0 => false, + TextureFormat.A2M1 => false, + TextureFormat.BC6H_UF16 => false, + TextureFormat.BC7 => false, + TextureFormat.INDEX16 => false, + TextureFormat.INDEX32 => false, + _ => true + }; + + public static int GetAlignSize(this TextureFormat format) => format switch + { + TextureFormat.DXT1 => 4, + TextureFormat.DXT2 => 4, + TextureFormat.DXT3 => 4, + TextureFormat.DXT4 => 4, + TextureFormat.DXT5 => 4, + TextureFormat.BC4 => 4, + TextureFormat.BC5 => 4, + TextureFormat.BC6H_UF16 => 4, + TextureFormat.BC7 => 4, + _ => 1 + }; + + public static int GetBitsPerPixel(this TextureFormat format) => format switch + { + TextureFormat.R32G32B32A32F => 128, + TextureFormat.R32G32B32A32_UINT => 128, + TextureFormat.R32G32B32F => 96, + TextureFormat.R32G32B32_UINT => 96, + TextureFormat.R16G16B16A16_UNORM => 64, + TextureFormat.R16G16B16A16F => 64, + TextureFormat.D32_S8X24_UINT => 64, + TextureFormat.R32G32F => 64, + TextureFormat.R32G32_UINT => 64, + TextureFormat.R16G16B16A16_UINT => 64, + TextureFormat.R16G16B16 => 48, + TextureFormat.B8G8R8A8_UNORM => 32, + TextureFormat.B8G8R8X8_UNORM => 32, + TextureFormat.R16G16_UNORM => 32, + TextureFormat.R16G16F => 32, + TextureFormat.R32_UINT => 32, + TextureFormat.R8G8B8A8_UNORM => 32, + TextureFormat.R8G8B8A8_UINT => 32, + TextureFormat.D24_UNORM_S8_UINT => 32, + TextureFormat.D24_UNORM_X8_UINT => 32, + TextureFormat.D32F => 32, + TextureFormat.R32F => 32, + TextureFormat.R11G11B10F => 32, + TextureFormat.R24G8_TYPELESS => 32, + TextureFormat.R16G16_UINT => 32, + TextureFormat.R8G8B8 => 24, + TextureFormat.B8G8R8 => 24, + TextureFormat.R16_UNORM => 16, + TextureFormat.R16F => 16, + TextureFormat.D16_UNORM => 16, + TextureFormat.L16_UNORM => 16, + TextureFormat.R16_UINT => 16, + TextureFormat.R8G8_UNORM => 16, + TextureFormat.A8_UNORM => 8, + TextureFormat.L8_UNORM => 8, + TextureFormat.R8_UNORM => 8, + TextureFormat.DXT2 => 8, + TextureFormat.DXT3 => 8, + TextureFormat.DXT4 => 8, + TextureFormat.DXT5 => 8, + TextureFormat.BC5 => 8, + TextureFormat.BC6H_UF16 => 8, + TextureFormat.BC7 => 8, + TextureFormat.R8_UINT => 8, + TextureFormat.DXT1 => 4, + TextureFormat.BC4 => 4, + _ => throw new Exception("Unsupported format:" + format.ToString()) + }; + + private static byte FloatToByte(float v) => (byte) TaleWorlds.Library.MathF.Min(Math.Max(TaleWorlds.Library.MathF.Round(v * 255f), 0), 255); + + private static void DecodeTextureDataToWriter(byte[] data, int width, int height, TextureFormat format, PipelineWriter writer, bool silentlyFail = false) + { + if (!format.IsSupported()) { - if (!format.IsSupported()) + if (!silentlyFail) + throw new FormatException("Unsupported format: " + format); + var byteBuffer = new byte[width * 4]; + for (var x = 0; x < width; x++) { - if (!silentlyFail) - throw new FormatException("Unsupported format: " + format); - var byteBuffer = new byte[width * 4]; - for (var x = 0; x < width; x++) - { - var i = x * 4; - byteBuffer[i] = 160; - byteBuffer[i + 1] = 160; - byteBuffer[i + 2] = 160; - byteBuffer[i + 3] = 255; - } - - for (var y = 0; y < height; y++) - { - writer.WriteLine(byteBuffer, true); - } + var i = x * 4; + byteBuffer[i] = 160; + byteBuffer[i + 1] = 160; + byteBuffer[i + 2] = 160; + byteBuffer[i + 3] = 255; } - else + + for (var y = 0; y < height; y++) { - PipelineReader reader = format switch - { - TextureFormat.DXT1 => new DXT1Reader(data, format, width, height), - TextureFormat.DXT2 => new DXT3Reader(data, format, width, height), - TextureFormat.DXT3 => new DXT3Reader(data, format, width, height), - TextureFormat.DXT4 => new DXT5Reader(data, format, width, height), - TextureFormat.DXT5 => new DXT5Reader(data, format, width, height), - TextureFormat.BC4 => new BC4Reader(data, format, width, height), - TextureFormat.BC5 => new BC5Reader(data, format, width, height), - _ => new DefaultReader(data, format, width, height) - }; - - reader.Read(writer); + writer.WriteLine(byteBuffer, true); } } - - public static Bitmap DecodeTextureDataToBitmap(byte[] data, int width, int height, TextureFormat format) + else { - var bitmap = new Bitmap(width, height, format.IsPremultiplied() ? PixelFormat.Format32bppPArgb : PixelFormat.Format32bppArgb); - BitmapData? bitmapData = null; - try + PipelineReader reader = format switch { - bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat); - PipelineWriter writer = new ARGB32Writer(bitmapData.Scan0, bitmapData.Width, bitmapData.Height, bitmapData.Stride); - DecodeTextureDataToWriter(data, width, height, format, writer); - } - finally - { - if (bitmapData is not null) - { - bitmap.UnlockBits(bitmapData); - } - } - return bitmap; + TextureFormat.DXT1 => new DXT1Reader(data, format, width, height), + TextureFormat.DXT2 => new DXT3Reader(data, format, width, height), + TextureFormat.DXT3 => new DXT3Reader(data, format, width, height), + TextureFormat.DXT4 => new DXT5Reader(data, format, width, height), + TextureFormat.DXT5 => new DXT5Reader(data, format, width, height), + TextureFormat.BC4 => new BC4Reader(data, format, width, height), + TextureFormat.BC5 => new BC5Reader(data, format, width, height), + _ => new DefaultReader(data, format, width, height) + }; + + reader.Read(writer); } + } - private abstract class PipelineReader + public static Bitmap DecodeTextureDataToBitmap(byte[] data, int width, int height, TextureFormat format) + { + var bitmap = new Bitmap(width, height, format.IsPremultiplied() ? PixelFormat.Format32bppPArgb : PixelFormat.Format32bppArgb); + BitmapData? bitmapData = null; + try { - protected readonly byte[] dataSource; - protected readonly int width, height; - protected readonly TextureFormat format; - - protected PipelineReader(byte[] dataSource, TextureFormat format, int width, int height) + bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat); + PipelineWriter writer = new ARGB32Writer(bitmapData.Scan0, bitmapData.Width, bitmapData.Height, bitmapData.Stride); + DecodeTextureDataToWriter(data, width, height, format, writer); + } + finally + { + if (bitmapData is not null) { - this.dataSource = dataSource; - this.format = format; - this.width = width; - this.height = height; + bitmap.UnlockBits(bitmapData); } - - public abstract void Read(PipelineWriter output); } + return bitmap; + } + + private abstract class PipelineReader + { + protected readonly byte[] dataSource; + protected readonly int width, height; + protected readonly TextureFormat format; - private sealed class SimpleBinaryStream + protected PipelineReader(byte[] dataSource, TextureFormat format, int width, int height) { - private readonly byte[] data; - private int pointer; + this.dataSource = dataSource; + this.format = format; + this.width = width; + this.height = height; + } - public SimpleBinaryStream(byte[] data) - { - this.data = data; - } + public abstract void Read(PipelineWriter output); + } - public byte ReadByte() - { - return data[pointer++]; - } + private sealed class SimpleBinaryStream + { + private readonly byte[] data; + private int pointer; - public ushort ReadUInt16() - { - var b0 = data[pointer]; - var b1 = data[pointer + 1]; - pointer += 2; - return (ushort) (b0 | b1 << 8); - } + public SimpleBinaryStream(byte[] data) + { + this.data = data; + } - public uint ReadUInt24() - { - var b0 = data[pointer]; - var b1 = data[pointer + 1]; - var b2 = data[pointer + 2]; - pointer += 3; - return (uint) (b0 | b1 << 8 | b2 << 16); - } + public byte ReadByte() + { + return data[pointer++]; + } - public uint ReadUInt32() - { - var b0 = data[pointer]; - var b1 = data[pointer + 1]; - var b2 = data[pointer + 2]; - var b3 = data[pointer + 3]; - pointer += 4; - return (uint) (b0 | b1 << 8 | b2 << 16 | b3 << 24); - } + public ushort ReadUInt16() + { + var b0 = data[pointer]; + var b1 = data[pointer + 1]; + pointer += 2; + return (ushort) (b0 | b1 << 8); + } - public ulong ReadUInt64() - { - var numLSB = (uint) (data[pointer] | data[pointer + 1] << 8 | data[pointer + 2] << 16 | data[pointer + 3] << 24); - var numMSB = (uint) (data[pointer + 4] | data[pointer + 5] << 8 | data[pointer + 6] << 16 | data[pointer + 7] << 24); - pointer += 8; - return (ulong) numMSB << 32 | numLSB; - } + public uint ReadUInt24() + { + var b0 = data[pointer]; + var b1 = data[pointer + 1]; + var b2 = data[pointer + 2]; + pointer += 3; + return (uint) (b0 | b1 << 8 | b2 << 16); + } - public long ReadInt64() - { - var numLSB = (uint) (data[pointer] | data[pointer + 1] << 8 | data[pointer + 2] << 16 | data[pointer + 3] << 24); - var numMSB = (uint) (data[pointer + 4] | data[pointer + 5] << 8 | data[pointer + 6] << 16 | data[pointer + 7] << 24); - pointer += 8; - return (long) ((ulong) numMSB << 32 | numLSB); - } + public uint ReadUInt32() + { + var b0 = data[pointer]; + var b1 = data[pointer + 1]; + var b2 = data[pointer + 2]; + var b3 = data[pointer + 3]; + pointer += 4; + return (uint) (b0 | b1 << 8 | b2 << 16 | b3 << 24); + } - public float ReadSingle() - { - var f = BitConverter.ToSingle(data, pointer); - pointer += 4; - return f; - } + public ulong ReadUInt64() + { + var numLSB = (uint) (data[pointer] | data[pointer + 1] << 8 | data[pointer + 2] << 16 | data[pointer + 3] << 24); + var numMSB = (uint) (data[pointer + 4] | data[pointer + 5] << 8 | data[pointer + 6] << 16 | data[pointer + 7] << 24); + pointer += 8; + return (ulong) numMSB << 32 | numLSB; } - private class DefaultReader : PipelineReader + public long ReadInt64() { - private int bytePerPixel; + var numLSB = (uint) (data[pointer] | data[pointer + 1] << 8 | data[pointer + 2] << 16 | data[pointer + 3] << 24); + var numMSB = (uint) (data[pointer + 4] | data[pointer + 5] << 8 | data[pointer + 6] << 16 | data[pointer + 7] << 24); + pointer += 8; + return (long) ((ulong) numMSB << 32 | numLSB); + } - public DefaultReader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) - { - bytePerPixel = format.GetBitsPerPixel() / 8; - } + public float ReadSingle() + { + var f = BitConverter.ToSingle(data, pointer); + pointer += 4; + return f; + } + } - public override void Read(PipelineWriter output) + private class DefaultReader : PipelineReader + { + private int bytePerPixel; + + public DefaultReader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) + { + bytePerPixel = format.GetBitsPerPixel() / 8; + } + + public override void Read(PipelineWriter output) + { + var stream = new SimpleBinaryStream(dataSource); + switch (format) { - var stream = new SimpleBinaryStream(dataSource); - switch (format) - { - case TextureFormat.A8_UNORM: - case TextureFormat.L8_UNORM: - case TextureFormat.R8_UNORM: - case TextureFormat.R8_UINT: - case TextureFormat.R8G8_UNORM: - case TextureFormat.R8G8B8: - case TextureFormat.B8G8R8: - case TextureFormat.B8G8R8A8_UNORM: - case TextureFormat.B8G8R8X8_UNORM: - case TextureFormat.R8G8B8A8_UNORM: - case TextureFormat.R8G8B8A8_UINT: - var byteBuffer = new byte[width * 4]; - for (var y = 0; y < height; y++) - { - ReadLine8bpp(stream, output, byteBuffer); - Array.Clear(byteBuffer, 0, byteBuffer.Length); - } + case TextureFormat.A8_UNORM: + case TextureFormat.L8_UNORM: + case TextureFormat.R8_UNORM: + case TextureFormat.R8_UINT: + case TextureFormat.R8G8_UNORM: + case TextureFormat.R8G8B8: + case TextureFormat.B8G8R8: + case TextureFormat.B8G8R8A8_UNORM: + case TextureFormat.B8G8R8X8_UNORM: + case TextureFormat.R8G8B8A8_UNORM: + case TextureFormat.R8G8B8A8_UINT: + var byteBuffer = new byte[width * 4]; + for (var y = 0; y < height; y++) + { + ReadLine8bpp(stream, output, byteBuffer); + Array.Clear(byteBuffer, 0, byteBuffer.Length); + } - byteBuffer = null; - break; - case TextureFormat.R16_UINT: - case TextureFormat.R16_UNORM: - case TextureFormat.D16_UNORM: - case TextureFormat.L16_UNORM: - case TextureFormat.R16G16_UNORM: - case TextureFormat.R16G16_UINT: - case TextureFormat.R16G16B16: - case TextureFormat.R16G16B16A16_UINT: - var ushortBuffer = new ushort[width * 4]; - for (var y = 0; y < height; y++) - { - ReadLine16bpp(stream, output, ushortBuffer); - Array.Clear(ushortBuffer, 0, ushortBuffer.Length); - } + byteBuffer = null; + break; + case TextureFormat.R16_UINT: + case TextureFormat.R16_UNORM: + case TextureFormat.D16_UNORM: + case TextureFormat.L16_UNORM: + case TextureFormat.R16G16_UNORM: + case TextureFormat.R16G16_UINT: + case TextureFormat.R16G16B16: + case TextureFormat.R16G16B16A16_UINT: + var ushortBuffer = new ushort[width * 4]; + for (var y = 0; y < height; y++) + { + ReadLine16bpp(stream, output, ushortBuffer); + Array.Clear(ushortBuffer, 0, ushortBuffer.Length); + } - ushortBuffer = null; - break; - case TextureFormat.R32_UINT: - case TextureFormat.R32G32_UINT: - case TextureFormat.R32G32B32_UINT: - case TextureFormat.R32G32B32A32_UINT: - case TextureFormat.R24G8_TYPELESS: - var uintBuffer = new uint[width * 4]; - for (var y = 0; y < height; y++) - { - ReadLine32bpp(stream, output, uintBuffer); - Array.Clear(uintBuffer, 0, uintBuffer.Length); - } + ushortBuffer = null; + break; + case TextureFormat.R32_UINT: + case TextureFormat.R32G32_UINT: + case TextureFormat.R32G32B32_UINT: + case TextureFormat.R32G32B32A32_UINT: + case TextureFormat.R24G8_TYPELESS: + var uintBuffer = new uint[width * 4]; + for (var y = 0; y < height; y++) + { + ReadLine32bpp(stream, output, uintBuffer); + Array.Clear(uintBuffer, 0, uintBuffer.Length); + } - uintBuffer = null; - break; - case TextureFormat.D24_UNORM_S8_UINT: - case TextureFormat.D24_UNORM_X8_UINT: - case TextureFormat.D32_S8X24_UINT: - case TextureFormat.D32F: - var depthBuffer = new float[width]; - var stencilBuffer = new byte[width]; - for (var y = 0; y < height; y++) - { - ReadLineDepth(stream, output, depthBuffer, stencilBuffer); - } + uintBuffer = null; + break; + case TextureFormat.D24_UNORM_S8_UINT: + case TextureFormat.D24_UNORM_X8_UINT: + case TextureFormat.D32_S8X24_UINT: + case TextureFormat.D32F: + var depthBuffer = new float[width]; + var stencilBuffer = new byte[width]; + for (var y = 0; y < height; y++) + { + ReadLineDepth(stream, output, depthBuffer, stencilBuffer); + } - depthBuffer = null; - stencilBuffer = null; - break; - case TextureFormat.R11G11B10F: - case TextureFormat.R16F: - case TextureFormat.R16G16F: - case TextureFormat.R16G16B16A16F: - case TextureFormat.R32F: - case TextureFormat.R32G32F: - case TextureFormat.R32G32B32F: - case TextureFormat.R32G32B32A32F: - var floatBuffer = new float[width * 4]; - for (var y = 0; y < height; y++) - { - ReadLineFloat(stream, output, floatBuffer); - Array.Clear(floatBuffer, 0, floatBuffer.Length); - } + depthBuffer = null; + stencilBuffer = null; + break; + case TextureFormat.R11G11B10F: + case TextureFormat.R16F: + case TextureFormat.R16G16F: + case TextureFormat.R16G16B16A16F: + case TextureFormat.R32F: + case TextureFormat.R32G32F: + case TextureFormat.R32G32B32F: + case TextureFormat.R32G32B32A32F: + var floatBuffer = new float[width * 4]; + for (var y = 0; y < height; y++) + { + ReadLineFloat(stream, output, floatBuffer); + Array.Clear(floatBuffer, 0, floatBuffer.Length); + } - floatBuffer = null; - break; - default: - throw new Exception("Unsupported format:" + format.ToString()); - } + floatBuffer = null; + break; + default: + throw new Exception("Unsupported format:" + format.ToString()); } + } - private void ReadLine8bpp(SimpleBinaryStream input, PipelineWriter output, byte[] buffer) + private void ReadLine8bpp(SimpleBinaryStream input, PipelineWriter output, byte[] buffer) + { + var normalized = true; + switch (format) { - var normalized = true; - switch (format) - { - case TextureFormat.A8_UNORM: - for (var i = 0; i < width; i++) - buffer[i * 4 + 3] = input.ReadByte(); - break; - case TextureFormat.L8_UNORM: - case TextureFormat.R8_UNORM: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadByte(); - buffer[j + 3] = byte.MaxValue; - } - - break; - case TextureFormat.R8_UINT: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadByte(); - buffer[j + 3] = byte.MaxValue; - } + case TextureFormat.A8_UNORM: + for (var i = 0; i < width; i++) + buffer[i * 4 + 3] = input.ReadByte(); + break; + case TextureFormat.L8_UNORM: + case TextureFormat.R8_UNORM: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadByte(); + buffer[j + 3] = byte.MaxValue; + } - normalized = false; - break; - case TextureFormat.R8G8_UNORM: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadByte(); - buffer[j + 1] = input.ReadByte(); - buffer[j + 3] = byte.MaxValue; - } + break; + case TextureFormat.R8_UINT: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadByte(); + buffer[j + 3] = byte.MaxValue; + } - break; - case TextureFormat.R8G8B8: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadByte(); - buffer[j + 1] = input.ReadByte(); - buffer[j + 2] = input.ReadByte(); - buffer[j + 3] = byte.MaxValue; - } + normalized = false; + break; + case TextureFormat.R8G8_UNORM: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadByte(); + buffer[j + 1] = input.ReadByte(); + buffer[j + 3] = byte.MaxValue; + } - break; - case TextureFormat.B8G8R8: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j + 2] = input.ReadByte(); - buffer[j + 1] = input.ReadByte(); - buffer[j] = input.ReadByte(); - buffer[j + 3] = byte.MaxValue; - } + break; + case TextureFormat.R8G8B8: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadByte(); + buffer[j + 1] = input.ReadByte(); + buffer[j + 2] = input.ReadByte(); + buffer[j + 3] = byte.MaxValue; + } - break; - case TextureFormat.B8G8R8A8_UNORM: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j + 2] = input.ReadByte(); - buffer[j + 1] = input.ReadByte(); - buffer[j] = input.ReadByte(); - buffer[j + 3] = input.ReadByte(); - } + break; + case TextureFormat.B8G8R8: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j + 2] = input.ReadByte(); + buffer[j + 1] = input.ReadByte(); + buffer[j] = input.ReadByte(); + buffer[j + 3] = byte.MaxValue; + } - break; - case TextureFormat.B8G8R8X8_UNORM: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j + 2] = input.ReadByte(); - buffer[j + 1] = input.ReadByte(); - buffer[j] = input.ReadByte(); - input.ReadByte(); - buffer[j + 3] = byte.MaxValue; - } + break; + case TextureFormat.B8G8R8A8_UNORM: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j + 2] = input.ReadByte(); + buffer[j + 1] = input.ReadByte(); + buffer[j] = input.ReadByte(); + buffer[j + 3] = input.ReadByte(); + } - break; - case TextureFormat.R8G8B8A8_UNORM: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadByte(); - buffer[j + 1] = input.ReadByte(); - buffer[j + 2] = input.ReadByte(); - buffer[j + 3] = input.ReadByte(); - } + break; + case TextureFormat.B8G8R8X8_UNORM: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j + 2] = input.ReadByte(); + buffer[j + 1] = input.ReadByte(); + buffer[j] = input.ReadByte(); + input.ReadByte(); + buffer[j + 3] = byte.MaxValue; + } - break; - case TextureFormat.R8G8B8A8_UINT: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadByte(); - buffer[j + 1] = input.ReadByte(); - buffer[j + 2] = input.ReadByte(); - buffer[j + 3] = input.ReadByte(); - } + break; + case TextureFormat.R8G8B8A8_UNORM: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadByte(); + buffer[j + 1] = input.ReadByte(); + buffer[j + 2] = input.ReadByte(); + buffer[j + 3] = input.ReadByte(); + } - normalized = false; - break; - } + break; + case TextureFormat.R8G8B8A8_UINT: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadByte(); + buffer[j + 1] = input.ReadByte(); + buffer[j + 2] = input.ReadByte(); + buffer[j + 3] = input.ReadByte(); + } - output.WriteLine(buffer, normalized); + normalized = false; + break; } - private void ReadLine16bpp(SimpleBinaryStream input, PipelineWriter output, ushort[] buffer) - { - var normalized = true; - switch (format) - { - case TextureFormat.R16_UINT: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadUInt16(); - buffer[j + 3] = ushort.MaxValue; - } + output.WriteLine(buffer, normalized); + } - normalized = false; - break; - case TextureFormat.R16_UNORM: - case TextureFormat.D16_UNORM: - case TextureFormat.L16_UNORM: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadUInt16(); - buffer[j + 3] = ushort.MaxValue; - } + private void ReadLine16bpp(SimpleBinaryStream input, PipelineWriter output, ushort[] buffer) + { + var normalized = true; + switch (format) + { + case TextureFormat.R16_UINT: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadUInt16(); + buffer[j + 3] = ushort.MaxValue; + } - break; - case TextureFormat.R16G16_UNORM: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadUInt16(); - buffer[j + 1] = input.ReadUInt16(); - buffer[j + 3] = ushort.MaxValue; - } + normalized = false; + break; + case TextureFormat.R16_UNORM: + case TextureFormat.D16_UNORM: + case TextureFormat.L16_UNORM: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadUInt16(); + buffer[j + 3] = ushort.MaxValue; + } - break; - case TextureFormat.R16G16_UINT: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadUInt16(); - buffer[j + 1] = input.ReadUInt16(); - buffer[j + 3] = ushort.MaxValue; - } + break; + case TextureFormat.R16G16_UNORM: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadUInt16(); + buffer[j + 1] = input.ReadUInt16(); + buffer[j + 3] = ushort.MaxValue; + } - normalized = false; - break; - case TextureFormat.R16G16B16: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadUInt16(); - buffer[j + 1] = input.ReadUInt16(); - buffer[j + 2] = input.ReadUInt16(); - buffer[j + 3] = ushort.MaxValue; - } + break; + case TextureFormat.R16G16_UINT: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadUInt16(); + buffer[j + 1] = input.ReadUInt16(); + buffer[j + 3] = ushort.MaxValue; + } - break; - case TextureFormat.R16G16B16A16_UINT: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadUInt16(); - buffer[j + 1] = input.ReadUInt16(); - buffer[j + 2] = input.ReadUInt16(); - buffer[j + 3] = input.ReadUInt16(); - } + normalized = false; + break; + case TextureFormat.R16G16B16: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadUInt16(); + buffer[j + 1] = input.ReadUInt16(); + buffer[j + 2] = input.ReadUInt16(); + buffer[j + 3] = ushort.MaxValue; + } - break; - } + break; + case TextureFormat.R16G16B16A16_UINT: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadUInt16(); + buffer[j + 1] = input.ReadUInt16(); + buffer[j + 2] = input.ReadUInt16(); + buffer[j + 3] = input.ReadUInt16(); + } - output.WriteLine(buffer, normalized); + break; } - private void ReadLine32bpp(SimpleBinaryStream input, PipelineWriter output, uint[] buffer) - { - var normalized = false; - switch (format) - { - case TextureFormat.R32_UINT: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadUInt32(); - buffer[j + 3] = uint.MaxValue; - } + output.WriteLine(buffer, normalized); + } - break; - case TextureFormat.R32G32_UINT: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadUInt32(); - buffer[j + 1] = input.ReadUInt32(); - buffer[j + 3] = uint.MaxValue; - } + private void ReadLine32bpp(SimpleBinaryStream input, PipelineWriter output, uint[] buffer) + { + var normalized = false; + switch (format) + { + case TextureFormat.R32_UINT: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadUInt32(); + buffer[j + 3] = uint.MaxValue; + } - break; - case TextureFormat.R32G32B32_UINT: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadUInt32(); - buffer[j + 1] = input.ReadUInt32(); - buffer[j + 2] = input.ReadUInt32(); - buffer[j + 3] = uint.MaxValue; - } + break; + case TextureFormat.R32G32_UINT: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadUInt32(); + buffer[j + 1] = input.ReadUInt32(); + buffer[j + 3] = uint.MaxValue; + } - break; - case TextureFormat.R32G32B32A32_UINT: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadUInt32(); - buffer[j + 1] = input.ReadUInt32(); - buffer[j + 2] = input.ReadUInt32(); - buffer[j + 3] = input.ReadUInt32(); - } + break; + case TextureFormat.R32G32B32_UINT: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadUInt32(); + buffer[j + 1] = input.ReadUInt32(); + buffer[j + 2] = input.ReadUInt32(); + buffer[j + 3] = uint.MaxValue; + } - break; - case TextureFormat.R24G8_TYPELESS: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadUInt24(); - buffer[j + 1] = input.ReadByte(); - buffer[j + 3] = uint.MaxValue; - } + break; + case TextureFormat.R32G32B32A32_UINT: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadUInt32(); + buffer[j + 1] = input.ReadUInt32(); + buffer[j + 2] = input.ReadUInt32(); + buffer[j + 3] = input.ReadUInt32(); + } - break; - } + break; + case TextureFormat.R24G8_TYPELESS: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadUInt24(); + buffer[j + 1] = input.ReadByte(); + buffer[j + 3] = uint.MaxValue; + } - output.WriteLine(buffer, normalized); + break; } - private void ReadLineDepth(SimpleBinaryStream input, PipelineWriter output, float[] depth, byte[] stencil) - { - switch (format) - { - case TextureFormat.D24_UNORM_S8_UINT: - for (var i = 0; i < width; i++) - { - depth[i] = Int24ToSingle(input.ReadUInt24()); - stencil[i] = input.ReadByte(); - } + output.WriteLine(buffer, normalized); + } - break; - case TextureFormat.D24_UNORM_X8_UINT: - for (var i = 0; i < width; i++) - { - depth[i] = Int24ToSingle(input.ReadUInt24()); - input.ReadByte(); - } + private void ReadLineDepth(SimpleBinaryStream input, PipelineWriter output, float[] depth, byte[] stencil) + { + switch (format) + { + case TextureFormat.D24_UNORM_S8_UINT: + for (var i = 0; i < width; i++) + { + depth[i] = Int24ToSingle(input.ReadUInt24()); + stencil[i] = input.ReadByte(); + } - break; - case TextureFormat.D32_S8X24_UINT: - for (var i = 0; i < width; i++) - { - depth[i] = Int32ToSingle(input.ReadUInt32()); - stencil[i] = input.ReadByte(); - input.ReadUInt24(); - } + break; + case TextureFormat.D24_UNORM_X8_UINT: + for (var i = 0; i < width; i++) + { + depth[i] = Int24ToSingle(input.ReadUInt24()); + input.ReadByte(); + } - break; - case TextureFormat.D32F: - for (var i = 0; i < width; i++) - { - depth[i] = input.ReadSingle(); - } + break; + case TextureFormat.D32_S8X24_UINT: + for (var i = 0; i < width; i++) + { + depth[i] = Int32ToSingle(input.ReadUInt32()); + stencil[i] = input.ReadByte(); + input.ReadUInt24(); + } - break; - } + break; + case TextureFormat.D32F: + for (var i = 0; i < width; i++) + { + depth[i] = input.ReadSingle(); + } - output.WriteLine(depth, stencil); + break; } - private void ReadLineFloat(SimpleBinaryStream input, PipelineWriter output, float[] buffer) - { - switch (format) - { - case TextureFormat.R11G11B10F: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - UnpackR11G11B10F(input.ReadUInt32(), out var r, out var g, out var b); - buffer[j] = r; - buffer[j + 1] = g; - buffer[j + 2] = b; - buffer[j + 3] = 1f; - } + output.WriteLine(depth, stencil); + } - break; - case TextureFormat.R16F: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = HalfToSingle(input.ReadUInt16()); - buffer[j + 3] = 1f; - } + private void ReadLineFloat(SimpleBinaryStream input, PipelineWriter output, float[] buffer) + { + switch (format) + { + case TextureFormat.R11G11B10F: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + UnpackR11G11B10F(input.ReadUInt32(), out var r, out var g, out var b); + buffer[j] = r; + buffer[j + 1] = g; + buffer[j + 2] = b; + buffer[j + 3] = 1f; + } - break; - case TextureFormat.R16G16F: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = HalfToSingle(input.ReadUInt16()); - buffer[j + 1] = HalfToSingle(input.ReadUInt16()); - buffer[j + 3] = 1f; - } + break; + case TextureFormat.R16F: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = HalfToSingle(input.ReadUInt16()); + buffer[j + 3] = 1f; + } - break; - case TextureFormat.R16G16B16A16F: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = HalfToSingle(input.ReadUInt16()); - buffer[j + 1] = HalfToSingle(input.ReadUInt16()); - buffer[j + 2] = HalfToSingle(input.ReadUInt16()); - buffer[j + 3] = HalfToSingle(input.ReadUInt16()); - } + break; + case TextureFormat.R16G16F: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = HalfToSingle(input.ReadUInt16()); + buffer[j + 1] = HalfToSingle(input.ReadUInt16()); + buffer[j + 3] = 1f; + } - break; - case TextureFormat.R32F: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadSingle(); - buffer[j + 3] = 1f; - } + break; + case TextureFormat.R16G16B16A16F: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = HalfToSingle(input.ReadUInt16()); + buffer[j + 1] = HalfToSingle(input.ReadUInt16()); + buffer[j + 2] = HalfToSingle(input.ReadUInt16()); + buffer[j + 3] = HalfToSingle(input.ReadUInt16()); + } - break; - case TextureFormat.R32G32F: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadSingle(); - buffer[j + 1] = input.ReadSingle(); - buffer[j + 3] = 1f; - } + break; + case TextureFormat.R32F: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadSingle(); + buffer[j + 3] = 1f; + } - break; - case TextureFormat.R32G32B32F: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadSingle(); - buffer[j + 1] = input.ReadSingle(); - buffer[j + 2] = input.ReadSingle(); - buffer[j + 3] = 1f; - } + break; + case TextureFormat.R32G32F: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadSingle(); + buffer[j + 1] = input.ReadSingle(); + buffer[j + 3] = 1f; + } - break; - case TextureFormat.R32G32B32A32F: - for (int i = 0, j = 0; i < width; i++, j = i * 4) - { - buffer[j] = input.ReadSingle(); - buffer[j + 1] = input.ReadSingle(); - buffer[j + 2] = input.ReadSingle(); - buffer[j + 3] = input.ReadSingle(); - } + break; + case TextureFormat.R32G32B32F: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadSingle(); + buffer[j + 1] = input.ReadSingle(); + buffer[j + 2] = input.ReadSingle(); + buffer[j + 3] = 1f; + } - break; - } + break; + case TextureFormat.R32G32B32A32F: + for (int i = 0, j = 0; i < width; i++, j = i * 4) + { + buffer[j] = input.ReadSingle(); + buffer[j + 1] = input.ReadSingle(); + buffer[j + 2] = input.ReadSingle(); + buffer[j + 3] = input.ReadSingle(); + } - output.WriteLine(buffer); + break; } - private static float Int24ToSingle(uint value) - { - return (float) (value / 16777215d); - } + output.WriteLine(buffer); + } - private static float Int32ToSingle(uint value) - { - return (float) (value / (double) UInt32.MaxValue); - } + private static float Int24ToSingle(uint value) + { + return (float) (value / 16777215d); + } - private static float HalfToSingle(ushort value) - { - return SystemHalf.Half.ToHalf(value); - } + private static float Int32ToSingle(uint value) + { + return (float) (value / (double) UInt32.MaxValue); + } - private static void UnpackR11G11B10F(uint value, out float r, out float g, out float b) - { - // https://github.com/anonymousguy198/swiftshader-compiled/blob/a6bc61d61d6fe9551d72f917629bf6bccfeafce0/src/Common/Half.hpp#L73 - var bits = (value & 0x7FFu) << 4; - r = HalfToSingle((ushort) bits); - bits = ((value >> 11) & 0x7FFu) << 4; - g = HalfToSingle((ushort) bits); - bits = ((value >> 22) & 0x3FFu) << 5; - b = HalfToSingle((ushort) bits); - } + private static float HalfToSingle(ushort value) + { + return SystemHalf.Half.ToHalf(value); } - private struct RGB565 + private static void UnpackR11G11B10F(uint value, out float r, out float g, out float b) { - internal readonly byte R; - internal readonly byte G; - internal readonly byte B; - internal readonly byte A; + // https://github.com/anonymousguy198/swiftshader-compiled/blob/a6bc61d61d6fe9551d72f917629bf6bccfeafce0/src/Common/Half.hpp#L73 + var bits = (value & 0x7FFu) << 4; + r = HalfToSingle((ushort) bits); + bits = ((value >> 11) & 0x7FFu) << 4; + g = HalfToSingle((ushort) bits); + bits = ((value >> 22) & 0x3FFu) << 5; + b = HalfToSingle((ushort) bits); + } + } - public RGB565(ushort value) : this(value, byte.MaxValue) - { - } + private struct RGB565 + { + internal readonly byte R; + internal readonly byte G; + internal readonly byte B; + internal readonly byte A; - public RGB565(ushort value, byte alpha) - { - var r = (value >> 11) & 0x1F; - var g = (value >> 5) & 0x3F; - var b = value & 0x1F; - r = r << 3 | r >> 2; - g = g << 2 | g >> 3; - b = b << 3 | b >> 2; - R = (byte) r; - G = (byte) g; - B = (byte) b; - A = alpha; - } + public RGB565(ushort value) : this(value, byte.MaxValue) + { + } - private RGB565(byte r, byte g, byte b, byte a) - { - R = r; - G = g; - B = b; - A = a; - } + public RGB565(ushort value, byte alpha) + { + var r = (value >> 11) & 0x1F; + var g = (value >> 5) & 0x3F; + var b = value & 0x1F; + r = r << 3 | r >> 2; + g = g << 2 | g >> 3; + b = b << 3 | b >> 2; + R = (byte) r; + G = (byte) g; + B = (byte) b; + A = alpha; + } - public void WriteToBytes(byte[] rgba8, int offset) - { - rgba8[offset] = R; - rgba8[offset + 1] = G; - rgba8[offset + 2] = B; - rgba8[offset + 3] = A; - } + private RGB565(byte r, byte g, byte b, byte a) + { + R = r; + G = g; + B = b; + A = a; + } - // return (left * 2 + right) / 3 (ignore alpha) - public static RGB565 MAD(RGB565 left, RGB565 right) - { - var r = (((left.R << 1) + right.R) / 3) & 0xFF; - var g = (((left.G << 1) + right.G) / 3) & 0xFF; - var b = (((left.B << 1) + right.B) / 3) & 0xFF; - return new RGB565((byte) r, (byte) g, (byte) b, left.A); - } + public void WriteToBytes(byte[] rgba8, int offset) + { + rgba8[offset] = R; + rgba8[offset + 1] = G; + rgba8[offset + 2] = B; + rgba8[offset + 3] = A; + } - // return (left + right) / 2 (ignore alpha) - public static RGB565 AVG(RGB565 left, RGB565 right) - { - var r = ((left.R + right.R) >> 1) & 0xFF; - var g = ((left.G + right.G) >> 1) & 0xFF; - var b = ((left.B + right.B) >> 1) & 0xFF; - return new RGB565((byte) r, (byte) g, (byte) b, left.A); - } + // return (left * 2 + right) / 3 (ignore alpha) + public static RGB565 MAD(RGB565 left, RGB565 right) + { + var r = (((left.R << 1) + right.R) / 3) & 0xFF; + var g = (((left.G << 1) + right.G) / 3) & 0xFF; + var b = (((left.B << 1) + right.B) / 3) & 0xFF; + return new RGB565((byte) r, (byte) g, (byte) b, left.A); } - private abstract class BlockReader : PipelineReader + // return (left + right) / 2 (ignore alpha) + public static RGB565 AVG(RGB565 left, RGB565 right) { - private int bytesPerBlock; + var r = ((left.R + right.R) >> 1) & 0xFF; + var g = ((left.G + right.G) >> 1) & 0xFF; + var b = ((left.B + right.B) >> 1) & 0xFF; + return new RGB565((byte) r, (byte) g, (byte) b, left.A); + } + } - public BlockReader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) - { - bytesPerBlock = (format.GetBitsPerPixel() * 16) / 8; - } + private abstract class BlockReader : PipelineReader + { + private int bytesPerBlock; + + public BlockReader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) + { + bytesPerBlock = (format.GetBitsPerPixel() * 16) / 8; + } - public override void Read(PipelineWriter output) + public override void Read(PipelineWriter output) + { + var blockWidth = Math.Max((width + 3) / 4, 1); + var blockHeight = Math.Max((height + 3) / 4, 1); + var cache0 = new byte[4][]; + for (var i = 0; i < 4; i++) + cache0[i] = new byte[blockWidth * 4 * 4]; + var cache1 = new byte[4][]; + for (var i = 0; i < 4; i++) + cache1[i] = new byte[blockWidth * 4 * 4]; + var isPingPong = false; + + var stream = new SimpleBinaryStream(dataSource); + for (var y = 0; y < blockHeight; y++) { - var blockWidth = Math.Max((width + 3) / 4, 1); - var blockHeight = Math.Max((height + 3) / 4, 1); - var cache0 = new byte[4][]; - for (var i = 0; i < 4; i++) - cache0[i] = new byte[blockWidth * 4 * 4]; - var cache1 = new byte[4][]; - for (var i = 0; i < 4; i++) - cache1[i] = new byte[blockWidth * 4 * 4]; - var isPingPong = false; - - var stream = new SimpleBinaryStream(dataSource); - for (var y = 0; y < blockHeight; y++) + if (isPingPong) { - if (isPingPong) - { - for (var x = 0; x < blockWidth; x++) - ReadBlock(stream, cache1, x); - } - else - { - for (var x = 0; x < blockWidth; x++) - ReadBlock(stream, cache0, x); - } + for (var x = 0; x < blockWidth; x++) + ReadBlock(stream, cache1, x); + } + else + { + for (var x = 0; x < blockWidth; x++) + ReadBlock(stream, cache0, x); + } - var pingPoing = isPingPong; - var baseY = y; - for (var y2 = 0; y2 < 4; y2++) + var pingPoing = isPingPong; + var baseY = y; + for (var y2 = 0; y2 < 4; y2++) + { + var currentY = baseY * 4 + y2; + if (currentY < height) { - var currentY = baseY * 4 + y2; - if (currentY < height) + if (pingPoing) + { + output.WriteLine(cache1[y2], false); + } + else { - if (pingPoing) - { - output.WriteLine(cache1[y2], false); - } - else - { - output.WriteLine(cache0[y2], false); - } + output.WriteLine(cache0[y2], false); } } - isPingPong = !isPingPong; } + isPingPong = !isPingPong; } + } - /*protected static PixelColor GetColorFromRGB565(ushort rgb565) - { - int b = rgb565 & 0x1F; - int g = (rgb565 & 0x7E0) >> 5; - int r = (rgb565 & 0xF800) >> 11; - b = b << 3 | b >> 2; - g = g << 2 | g >> 3; - r = r << 3 | r >> 2; - return new PixelColor(r / 255f, g / 255f, b / 255f, 1f); - }*/ - - protected abstract void ReadBlock(SimpleBinaryStream stream, byte[][] cache, int blockX); + /*protected static PixelColor GetColorFromRGB565(ushort rgb565) + { + int b = rgb565 & 0x1F; + int g = (rgb565 & 0x7E0) >> 5; + int r = (rgb565 & 0xF800) >> 11; + b = b << 3 | b >> 2; + g = g << 2 | g >> 3; + r = r << 3 | r >> 2; + return new PixelColor(r / 255f, g / 255f, b / 255f, 1f); + }*/ + + protected abstract void ReadBlock(SimpleBinaryStream stream, byte[][] cache, int blockX); + } + + private class DXT1Reader : BlockReader + { + public DXT1Reader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) + { } - private class DXT1Reader : BlockReader + protected override void ReadBlock(SimpleBinaryStream stream, byte[][] cache, int blockX) { - public DXT1Reader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) + var posX = blockX * 4; + + var baseColor = new RGB565[4]; + ushort color0, color1; + baseColor[0] = new RGB565(color0 = stream.ReadUInt16()); + baseColor[1] = new RGB565(color1 = stream.ReadUInt16()); + if (color0 > color1) { + // baseColor[2] = (baseColor[0] * 2 + baseColor[1]) / 3; + baseColor[2] = RGB565.MAD(baseColor[0], baseColor[1]); + baseColor[3] = RGB565.MAD(baseColor[1], baseColor[0]); } - - protected override void ReadBlock(SimpleBinaryStream stream, byte[][] cache, int blockX) + else { - var posX = blockX * 4; - - var baseColor = new RGB565[4]; - ushort color0, color1; - baseColor[0] = new RGB565(color0 = stream.ReadUInt16()); - baseColor[1] = new RGB565(color1 = stream.ReadUInt16()); - if (color0 > color1) - { - // baseColor[2] = (baseColor[0] * 2 + baseColor[1]) / 3; - baseColor[2] = RGB565.MAD(baseColor[0], baseColor[1]); - baseColor[3] = RGB565.MAD(baseColor[1], baseColor[0]); - } - else - { - baseColor[2] = RGB565.AVG(baseColor[0], baseColor[1]); - baseColor[3] = new RGB565(0, byte.MaxValue); - } + baseColor[2] = RGB565.AVG(baseColor[0], baseColor[1]); + baseColor[3] = new RGB565(0, byte.MaxValue); + } - for (var y = 0; y < 4; y++) - { - var writeLine = cache[y]; - var index = stream.ReadByte(); - baseColor[(index >> 0) & 0x3].WriteToBytes(writeLine, (posX + 0) * 4); - baseColor[(index >> 2) & 0x3].WriteToBytes(writeLine, (posX + 1) * 4); - baseColor[(index >> 4) & 0x3].WriteToBytes(writeLine, (posX + 2) * 4); - baseColor[(index >> 6) & 0x3].WriteToBytes(writeLine, (posX + 3) * 4); - } + for (var y = 0; y < 4; y++) + { + var writeLine = cache[y]; + var index = stream.ReadByte(); + baseColor[(index >> 0) & 0x3].WriteToBytes(writeLine, (posX + 0) * 4); + baseColor[(index >> 2) & 0x3].WriteToBytes(writeLine, (posX + 1) * 4); + baseColor[(index >> 4) & 0x3].WriteToBytes(writeLine, (posX + 2) * 4); + baseColor[(index >> 6) & 0x3].WriteToBytes(writeLine, (posX + 3) * 4); } } - private class DXT3Reader : DXT1Reader + } + private class DXT3Reader : DXT1Reader + { + public DXT3Reader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) { - public DXT3Reader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) - { - } + } - protected override void ReadBlock(SimpleBinaryStream stream, byte[][] cache, int blockX) + protected override void ReadBlock(SimpleBinaryStream stream, byte[][] cache, int blockX) + { + var alphaData = stream.ReadUInt64(); + base.ReadBlock(stream, cache, blockX); + var posX = blockX * 4; + var i = 0; + for (var y = 0; y < 4; y++) { - var alphaData = stream.ReadUInt64(); - base.ReadBlock(stream, cache, blockX); - var posX = blockX * 4; - var i = 0; - for (var y = 0; y < 4; y++) + var writeLine = cache[y]; + for (var x = 0; x < 4; x++) { - var writeLine = cache[y]; - for (var x = 0; x < 4; x++) - { - var alpha = (int) ((alphaData >> (i * 4)) & 0xF); - i++; - alpha = (alpha << 4 | alpha) & 0xFF; - writeLine[(posX + x) * 4 + 3] = (byte) alpha; - } + var alpha = (int) ((alphaData >> (i * 4)) & 0xF); + i++; + alpha = (alpha << 4 | alpha) & 0xFF; + writeLine[(posX + x) * 4 + 3] = (byte) alpha; } } } - private class DXT5Reader : DXT1Reader + } + private class DXT5Reader : DXT1Reader + { + public DXT5Reader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) { - public DXT5Reader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) - { - } + } - protected override void ReadBlock(SimpleBinaryStream stream, byte[][] cache, int blockX) + protected override void ReadBlock(SimpleBinaryStream stream, byte[][] cache, int blockX) + { + var alphaData = stream.ReadInt64(); + base.ReadBlock(stream, cache, blockX); + ReadBC3AlphaBlock(alphaData, cache, blockX * 4, (out int maskR, out int maskG, out int maskB, out int maskA, + out byte addR, out byte addG, out byte addB, out byte addA) => { - var alphaData = stream.ReadInt64(); - base.ReadBlock(stream, cache, blockX); - ReadBC3AlphaBlock(alphaData, cache, blockX * 4, (out int maskR, out int maskG, out int maskB, out int maskA, - out byte addR, out byte addG, out byte addB, out byte addA) => - { - maskR = maskG = maskB = 0; - maskA = byte.MaxValue; - addR = addG = addB = addA = 0; - }); - /*ReadBC3AlphaBlock(alphaData, cache, blockX * 4, (ref PixelColor pixel, float value) => - { - pixel.A = value; - });*/ - } + maskR = maskG = maskB = 0; + maskA = byte.MaxValue; + addR = addG = addB = addA = 0; + }); + /*ReadBC3AlphaBlock(alphaData, cache, blockX * 4, (ref PixelColor pixel, float value) => + { + pixel.A = value; + });*/ + } - public delegate void GetPixelMask(out int maskR, out int maskG, out int maskB, out int maskA, - out byte addR, out byte addG, out byte addB, out byte addA); + public delegate void GetPixelMask(out int maskR, out int maskG, out int maskB, out int maskA, + out byte addR, out byte addG, out byte addB, out byte addA); - public static void ReadBC3AlphaBlock(long block, byte[][] cache, int posX, GetPixelMask writeMask) + public static void ReadBC3AlphaBlock(long block, byte[][] cache, int posX, GetPixelMask writeMask) + { + writeMask(out var maskR, out var maskG, out var maskB, out var maskA, + out var addR, out var addG, out var addB, out var addA); + var alpha0 = (int) (block & 0xFF); + var alpha1 = (int) ((block >> 8) & 0xFF); + var isFirstGreater = alpha0 > alpha1; + block = block >> 16; + var alphaLookup = new byte[8]; + for (var j = 0; j < 8; j++) { - writeMask(out var maskR, out var maskG, out var maskB, out var maskA, - out var addR, out var addG, out var addB, out var addA); - var alpha0 = (int) (block & 0xFF); - var alpha1 = (int) ((block >> 8) & 0xFF); - var isFirstGreater = alpha0 > alpha1; - block = block >> 16; - var alphaLookup = new byte[8]; - for (var j = 0; j < 8; j++) - { - alphaLookup[j] = (byte) BC3GradientInterpolate(j, alpha0, alpha1, isFirstGreater); - } + alphaLookup[j] = (byte) BC3GradientInterpolate(j, alpha0, alpha1, isFirstGreater); + } - var i = 0; - for (var y = 0; y < 4; y++) + var i = 0; + for (var y = 0; y < 4; y++) + { + var writeLine = cache[y]; + for (var x = 0; x < 4; x++) { - var writeLine = cache[y]; - for (var x = 0; x < 4; x++) - { - var alphaIndex = (int) (block >> (i * 3)) & 0x7; - var value = alphaLookup[alphaIndex]; - i++; - var writePos = (posX + x) * 4; - writeLine[writePos + 0] = (byte) (((value & maskR) + (writeLine[writePos + 0] & ~maskR)) + addR); - writeLine[writePos + 1] = (byte) (((value & maskG) + (writeLine[writePos + 1] & ~maskG)) + addG); - writeLine[writePos + 2] = (byte) (((value & maskB) + (writeLine[writePos + 2] & ~maskB)) + addB); - writeLine[writePos + 3] = (byte) (((value & maskA) + (writeLine[writePos + 3] & ~maskA)) + addA); - } + var alphaIndex = (int) (block >> (i * 3)) & 0x7; + var value = alphaLookup[alphaIndex]; + i++; + var writePos = (posX + x) * 4; + writeLine[writePos + 0] = (byte) (((value & maskR) + (writeLine[writePos + 0] & ~maskR)) + addR); + writeLine[writePos + 1] = (byte) (((value & maskG) + (writeLine[writePos + 1] & ~maskG)) + addG); + writeLine[writePos + 2] = (byte) (((value & maskB) + (writeLine[writePos + 2] & ~maskB)) + addB); + writeLine[writePos + 3] = (byte) (((value & maskA) + (writeLine[writePos + 3] & ~maskA)) + addA); } } + } - public static int BC3GradientInterpolate(int index, int alpha0, int alpha1, bool isFirstGreater) + public static int BC3GradientInterpolate(int index, int alpha0, int alpha1, bool isFirstGreater) + { + if (isFirstGreater) { - if (isFirstGreater) + switch (index) { - switch (index) - { - case 0: - return alpha0; - case 1: - return alpha1; - case 2: - return (alpha0 * 6 + alpha1 * 1) / 7; - case 3: - return (alpha0 * 5 + alpha1 * 2) / 7; - case 4: - return (alpha0 * 4 + alpha1 * 3) / 7; - case 5: - return (alpha0 * 3 + alpha1 * 4) / 7; - case 6: - return (alpha0 * 2 + alpha1 * 5) / 7; - case 7: - return (alpha0 * 1 + alpha1 * 6) / 7; - } + case 0: + return alpha0; + case 1: + return alpha1; + case 2: + return (alpha0 * 6 + alpha1 * 1) / 7; + case 3: + return (alpha0 * 5 + alpha1 * 2) / 7; + case 4: + return (alpha0 * 4 + alpha1 * 3) / 7; + case 5: + return (alpha0 * 3 + alpha1 * 4) / 7; + case 6: + return (alpha0 * 2 + alpha1 * 5) / 7; + case 7: + return (alpha0 * 1 + alpha1 * 6) / 7; } - else + } + else + { + switch (index) { - switch (index) - { - case 0: - return alpha0; - case 1: - return alpha1; - case 2: - return (alpha0 * 4 + alpha1 * 1) / 5; - case 3: - return (alpha0 * 3 + alpha1 * 2) / 5; - case 4: - return (alpha0 * 2 + alpha1 * 3) / 5; - case 5: - return (alpha0 * 1 + alpha1 * 4) / 5; - case 6: - return 0; - case 7: - return ushort.MaxValue; - } + case 0: + return alpha0; + case 1: + return alpha1; + case 2: + return (alpha0 * 4 + alpha1 * 1) / 5; + case 3: + return (alpha0 * 3 + alpha1 * 2) / 5; + case 4: + return (alpha0 * 2 + alpha1 * 3) / 5; + case 5: + return (alpha0 * 1 + alpha1 * 4) / 5; + case 6: + return 0; + case 7: + return ushort.MaxValue; } - - throw new IndexOutOfRangeException("Bad alpha index: " + index); } + + throw new IndexOutOfRangeException("Bad alpha index: " + index); } - private class BC4Reader : BlockReader + } + private class BC4Reader : BlockReader + { + public BC4Reader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) { - public BC4Reader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) - { - } + } - protected override void ReadBlock(SimpleBinaryStream stream, byte[][] cache, int blockX) + protected override void ReadBlock(SimpleBinaryStream stream, byte[][] cache, int blockX) + { + DXT5Reader.ReadBC3AlphaBlock(stream.ReadInt64(), cache, blockX * 4, (out int maskR, out int maskG, out int maskB, out int maskA, + out byte addR, out byte addG, out byte addB, out byte addA) => { - DXT5Reader.ReadBC3AlphaBlock(stream.ReadInt64(), cache, blockX * 4, (out int maskR, out int maskG, out int maskB, out int maskA, - out byte addR, out byte addG, out byte addB, out byte addA) => - { - maskR = byte.MaxValue; - maskG = maskB = maskA = 0; - addR = addG = addB = 0; - addA = byte.MaxValue; - }); - /*DXT5Reader.ReadBC3AlphaBlock(stream.ReadInt64(), ref cache, blockX * 4, (ref PixelColor pixel, float value) => - { - pixel.R = value; - pixel.A = 1f; - });*/ - } + maskR = byte.MaxValue; + maskG = maskB = maskA = 0; + addR = addG = addB = 0; + addA = byte.MaxValue; + }); + /*DXT5Reader.ReadBC3AlphaBlock(stream.ReadInt64(), ref cache, blockX * 4, (ref PixelColor pixel, float value) => + { + pixel.R = value; + pixel.A = 1f; + });*/ + } + } + private class BC5Reader : BC4Reader + { + public BC5Reader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) + { } - private class BC5Reader : BC4Reader + + protected override void ReadBlock(SimpleBinaryStream stream, byte[][] cache, int blockX) { - public BC5Reader(byte[] dataSource, TextureFormat format, int width, int height) : base(dataSource, format, width, height) + base.ReadBlock(stream, cache, blockX); + DXT5Reader.ReadBC3AlphaBlock(stream.ReadInt64(), cache, blockX * 4, (out int maskR, out int maskG, out int maskB, out int maskA, + out byte addR, out byte addG, out byte addB, out byte addA) => { - } - - protected override void ReadBlock(SimpleBinaryStream stream, byte[][] cache, int blockX) + maskG = byte.MaxValue; + maskR = maskB = maskA = 0; + addR = addG = addB = addA = 0; + }); + /*DXT5Reader.ReadBC3AlphaBlock(stream.ReadInt64(), ref cache, blockX * 4, (ref PixelColor pixel, float value) => { - base.ReadBlock(stream, cache, blockX); - DXT5Reader.ReadBC3AlphaBlock(stream.ReadInt64(), cache, blockX * 4, (out int maskR, out int maskG, out int maskB, out int maskA, - out byte addR, out byte addG, out byte addB, out byte addA) => - { - maskG = byte.MaxValue; - maskR = maskB = maskA = 0; - addR = addG = addB = addA = 0; - }); - /*DXT5Reader.ReadBC3AlphaBlock(stream.ReadInt64(), ref cache, blockX * 4, (ref PixelColor pixel, float value) => - { - pixel.G = value; - });*/ - } + pixel.G = value; + });*/ } + } - private abstract class PipelineWriter - { - public abstract void WriteLine(byte[] rgba8, bool normalized); + private abstract class PipelineWriter + { + public abstract void WriteLine(byte[] rgba8, bool normalized); - public abstract void WriteLine(ushort[] rgba16, bool normalized); + public abstract void WriteLine(ushort[] rgba16, bool normalized); - public abstract void WriteLine(uint[] rgba32, bool normalized); + public abstract void WriteLine(uint[] rgba32, bool normalized); - public abstract void WriteLine(float[] depth, byte[] stencil); + public abstract void WriteLine(float[] depth, byte[] stencil); - public abstract void WriteLine(float[] rgba32f); - } - private sealed class ARGB32Writer : PipelineWriter - { - private readonly int lineLimiter; - private readonly int stride; - private readonly int[] buffer; - private IntPtr data; + public abstract void WriteLine(float[] rgba32f); + } + private sealed class ARGB32Writer : PipelineWriter + { + private readonly int lineLimiter; + private readonly int stride; + private readonly int[] buffer; + private IntPtr data; - public ARGB32Writer(IntPtr ptr, int width, int height, int stride) - { - data = ptr; - this.stride = stride; - lineLimiter = width; - buffer = new int[lineLimiter]; - } + public ARGB32Writer(IntPtr ptr, int width, int height, int stride) + { + data = ptr; + this.stride = stride; + lineLimiter = width; + buffer = new int[lineLimiter]; + } - public override void WriteLine(byte[] rgba8, bool normalized) + public override void WriteLine(byte[] rgba8, bool normalized) + { + for (var i = 0; i < lineLimiter; i++) { - for (var i = 0; i < lineLimiter; i++) - { - var num = i * 4; - buffer[i] = (int) rgba8[num + 2] | ((int) rgba8[num + 1] << 8) | ((int) rgba8[num] << 16) | ((int) rgba8[num + 3] << 24); - } - Marshal.Copy(buffer, 0, data, lineLimiter); - data = IntPtr.Add(data, stride); + var num = i * 4; + buffer[i] = (int) rgba8[num + 2] | ((int) rgba8[num + 1] << 8) | ((int) rgba8[num] << 16) | ((int) rgba8[num + 3] << 24); } + Marshal.Copy(buffer, 0, data, lineLimiter); + data = IntPtr.Add(data, stride); + } - public override void WriteLine(ushort[] rgba16, bool normalized) + public override void WriteLine(ushort[] rgba16, bool normalized) + { + for (int i = 0; i < lineLimiter; i++) { - for (int i = 0; i < lineLimiter; i++) - { - int num = i * 4; - buffer[i] = (rgba16[num + 2] >> 8) | (rgba16[num + 1] >> 8 << 8) | (rgba16[num] >> 8 << 16) | (rgba16[num + 3] >> 8 << 24); - } - Marshal.Copy(buffer, 0, data, lineLimiter); - data = IntPtr.Add(data, stride); + int num = i * 4; + buffer[i] = (rgba16[num + 2] >> 8) | (rgba16[num + 1] >> 8 << 8) | (rgba16[num] >> 8 << 16) | (rgba16[num + 3] >> 8 << 24); } + Marshal.Copy(buffer, 0, data, lineLimiter); + data = IntPtr.Add(data, stride); + } - public override void WriteLine(uint[] rgba32, bool normalized) + public override void WriteLine(uint[] rgba32, bool normalized) + { + for (int i = 0; i < lineLimiter; i++) { - for (int i = 0; i < lineLimiter; i++) - { - int num = i * 4; - buffer[i] = (int) ((rgba32[num + 2] >> 24) | (rgba32[num + 1] >> 24 << 8) | (rgba32[num] >> 24 << 16) | (rgba32[num + 3] >> 24 << 24)); - } - Marshal.Copy(buffer, 0, data, lineLimiter); - data = IntPtr.Add(data, stride); + int num = i * 4; + buffer[i] = (int) ((rgba32[num + 2] >> 24) | (rgba32[num + 1] >> 24 << 8) | (rgba32[num] >> 24 << 16) | (rgba32[num + 3] >> 24 << 24)); } + Marshal.Copy(buffer, 0, data, lineLimiter); + data = IntPtr.Add(data, stride); + } - public override void WriteLine(float[] depth, byte[] stencil) + public override void WriteLine(float[] depth, byte[] stencil) + { + for (int i = 0; i < lineLimiter; i++) { - for (int i = 0; i < lineLimiter; i++) - { - buffer[i] = 0 | ((int) stencil[i] << 8) | ((int) FloatToByte(depth[i]) << 16) | 255; - } - Marshal.Copy(buffer, 0, data, lineLimiter); - data = IntPtr.Add(data, stride); + buffer[i] = 0 | ((int) stencil[i] << 8) | ((int) FloatToByte(depth[i]) << 16) | 255; } + Marshal.Copy(buffer, 0, data, lineLimiter); + data = IntPtr.Add(data, stride); + } - public override void WriteLine(float[] rgba32f) + public override void WriteLine(float[] rgba32f) + { + for (int i = 0; i < lineLimiter; i++) { - for (int i = 0; i < lineLimiter; i++) - { - int num = i * 4; - buffer[i] = (int) FloatToByte(rgba32f[num + 2]) | ((int) FloatToByte(rgba32f[num + 1]) << 8) | ((int) FloatToByte(rgba32f[num]) << 16) | ((int) FloatToByte(rgba32f[num + 3]) << 24); - } - Marshal.Copy(buffer, 0, data, lineLimiter); - data = IntPtr.Add(data, stride); + int num = i * 4; + buffer[i] = (int) FloatToByte(rgba32f[num + 2]) | ((int) FloatToByte(rgba32f[num + 1]) << 8) | ((int) FloatToByte(rgba32f[num]) << 16) | ((int) FloatToByte(rgba32f[num + 3]) << 24); } + Marshal.Copy(buffer, 0, data, lineLimiter); + data = IntPtr.Add(data, stride); } } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/TPac/TypedAssetFactory.cs b/src/Bannerlord.LauncherEx/TPac/TypedAssetFactory.cs index 69be58d..a78ad99 100644 --- a/src/Bannerlord.LauncherEx/TPac/TypedAssetFactory.cs +++ b/src/Bannerlord.LauncherEx/TPac/TypedAssetFactory.cs @@ -2,50 +2,49 @@ using System.Collections.Generic; using System.Reflection; -namespace Bannerlord.LauncherEx.TPac +namespace Bannerlord.LauncherEx.TPac; + +internal static class TypedAssetFactory { - internal static class TypedAssetFactory - { - private static readonly Dictionary classMap = new(); - private static readonly Dictionary constructorMap = new(); + private static readonly Dictionary classMap = new(); + private static readonly Dictionary constructorMap = new(); - static TypedAssetFactory() - { - RegisterType(typeof(Texture)); - } + static TypedAssetFactory() + { + RegisterType(typeof(Texture)); + } - public static void RegisterType(Type typeClass) - { - var field = typeClass.GetField("TYPE_GUID", BindingFlags.Static | BindingFlags.Public) ?? throw new ArgumentException("Cannot find public static field \"TYPE_GUID\" from class " + typeClass.FullName); - if (field.FieldType != typeof(Guid)) throw new ArgumentException("\"TYPE_GUID\" must be Guid"); - var guid = (Guid) field.GetValue(null); - RegisterType(guid, typeClass); - } + public static void RegisterType(Type typeClass) + { + var field = typeClass.GetField("TYPE_GUID", BindingFlags.Static | BindingFlags.Public) ?? throw new ArgumentException("Cannot find public static field \"TYPE_GUID\" from class " + typeClass.FullName); + if (field.FieldType != typeof(Guid)) throw new ArgumentException("\"TYPE_GUID\" must be Guid"); + var guid = (Guid) field.GetValue(null); + RegisterType(guid, typeClass); + } - public static void RegisterType(Guid typeGuid, Type typeClass) - { - if (!typeof(AssetItem).IsAssignableFrom(typeClass)) - throw new ArgumentException("Registered type must extend from AssetItem"); + public static void RegisterType(Guid typeGuid, Type typeClass) + { + if (!typeof(AssetItem).IsAssignableFrom(typeClass)) + throw new ArgumentException("Registered type must extend from AssetItem"); - var constructor = typeClass.GetConstructor(Type.EmptyTypes); - if (constructor == null) - throw new ArgumentException("Registered type must have a param-less constructor"); + var constructor = typeClass.GetConstructor(Type.EmptyTypes); + if (constructor == null) + throw new ArgumentException("Registered type must have a param-less constructor"); - classMap[typeGuid] = typeClass; - constructorMap[typeGuid] = constructor; - } + classMap[typeGuid] = typeClass; + constructorMap[typeGuid] = constructor; + } - public static bool CreateTypedAsset(Guid typeGuid, out AssetItem result) + public static bool CreateTypedAsset(Guid typeGuid, out AssetItem result) + { + if (classMap.ContainsKey(typeGuid)) { - if (classMap.ContainsKey(typeGuid)) - { - var constructor = constructorMap[typeGuid]; - result = (AssetItem) constructor.Invoke(null); - return true; - } - - result = new AssetItem(typeGuid); - return false; + var constructor = constructorMap[typeGuid]; + result = (AssetItem) constructor.Invoke(null); + return true; } + + result = new AssetItem(typeGuid); + return false; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/TPac/TypedDataFactory.cs b/src/Bannerlord.LauncherEx/TPac/TypedDataFactory.cs index 43ee4fe..26d42d5 100644 --- a/src/Bannerlord.LauncherEx/TPac/TypedDataFactory.cs +++ b/src/Bannerlord.LauncherEx/TPac/TypedDataFactory.cs @@ -3,55 +3,54 @@ using System.IO; using System.Reflection; -namespace Bannerlord.LauncherEx.TPac +namespace Bannerlord.LauncherEx.TPac; + +internal static class TypedDataFactory { - internal static class TypedDataFactory - { - private static readonly Dictionary guidToLoaderTypeMap = new(); - private static readonly Dictionary guidToLoaderConstructorMap = new(); + private static readonly Dictionary guidToLoaderTypeMap = new(); + private static readonly Dictionary guidToLoaderConstructorMap = new(); - static TypedDataFactory() - { - RegisterType(typeof(TexturePixelData)); - } + static TypedDataFactory() + { + RegisterType(typeof(TexturePixelData)); + } - public static void RegisterType(Type typeClass) - { - var field = typeClass.GetField("TYPE_GUID", BindingFlags.Static | BindingFlags.Public) ?? throw new ArgumentException("Cannot find public static field \"TYPE_GUID\" from class " + typeClass.FullName); - if (field.FieldType != typeof(Guid)) throw new ArgumentException("\"TYPE_GUID\" must be Guid"); - var guid = (Guid) field.GetValue(null); - RegisterType(guid, typeClass); - } + public static void RegisterType(Type typeClass) + { + var field = typeClass.GetField("TYPE_GUID", BindingFlags.Static | BindingFlags.Public) ?? throw new ArgumentException("Cannot find public static field \"TYPE_GUID\" from class " + typeClass.FullName); + if (field.FieldType != typeof(Guid)) throw new ArgumentException("\"TYPE_GUID\" must be Guid"); + var guid = (Guid) field.GetValue(null); + RegisterType(guid, typeClass); + } - public static void RegisterType(Guid typeGuid, Type typeClass) - { - if (!typeof(ExternalData).IsAssignableFrom(typeClass)) - throw new ArgumentException("Registered type must extend from ExternalData"); + public static void RegisterType(Guid typeGuid, Type typeClass) + { + if (!typeof(ExternalData).IsAssignableFrom(typeClass)) + throw new ArgumentException("Registered type must extend from ExternalData"); - var constructor = typeClass.GetConstructor(Type.EmptyTypes); - if (constructor == null) throw new ArgumentException("Registered type must have a param-less constructor"); + var constructor = typeClass.GetConstructor(Type.EmptyTypes); + if (constructor == null) throw new ArgumentException("Registered type must have a param-less constructor"); - var loaderType = typeof(ExternalLoader<>).MakeGenericType(typeClass); - guidToLoaderTypeMap[typeGuid] = loaderType; - guidToLoaderConstructorMap[typeGuid] = loaderType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(FileInfo) }, null); - } + var loaderType = typeof(ExternalLoader<>).MakeGenericType(typeClass); + guidToLoaderTypeMap[typeGuid] = loaderType; + guidToLoaderConstructorMap[typeGuid] = loaderType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(FileInfo) }, null); + } - public static bool CreateTypedLoader(Guid typeGuid, FileInfo file, out AbstractExternalLoader result) + public static bool CreateTypedLoader(Guid typeGuid, FileInfo file, out AbstractExternalLoader result) + { + AbstractExternalLoader? loader = null; + var isFound = false; + if (guidToLoaderTypeMap.ContainsKey(typeGuid)) { - AbstractExternalLoader? loader = null; - var isFound = false; - if (guidToLoaderTypeMap.ContainsKey(typeGuid)) - { - var constructor = guidToLoaderConstructorMap[typeGuid]; - loader = (AbstractExternalLoader) constructor.Invoke(new object[] { file }); - isFound = true; - } - - loader ??= new ExternalLoader(file); - - loader.TypeGuid = typeGuid; - result = loader; - return isFound; + var constructor = guidToLoaderConstructorMap[typeGuid]; + loader = (AbstractExternalLoader) constructor.Invoke(new object[] { file }); + isFound = true; } + + loader ??= new ExternalLoader(file); + + loader.TypeGuid = typeGuid; + result = loader; + return isFound; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/TPac/Utils.cs b/src/Bannerlord.LauncherEx/TPac/Utils.cs index 32c976a..6bba3de 100644 --- a/src/Bannerlord.LauncherEx/TPac/Utils.cs +++ b/src/Bannerlord.LauncherEx/TPac/Utils.cs @@ -3,37 +3,36 @@ using System.IO; using System.Text; -namespace Bannerlord.LauncherEx.TPac +namespace Bannerlord.LauncherEx.TPac; + +internal static class Utils { - internal static class Utils + public static string ReadSizedString(this BinaryReader stream) { - public static string ReadSizedString(this BinaryReader stream) + var num = stream.ReadInt32(); + if (num == 0) { - var num = stream.ReadInt32(); - if (num == 0) - { - return string.Empty; - } - - var array = stream.ReadBytes(num); - return Encoding.UTF8.GetString(array); + return string.Empty; } - public static List ReadStringList(this BinaryReader stream) + var array = stream.ReadBytes(num); + return Encoding.UTF8.GetString(array); + } + + public static List ReadStringList(this BinaryReader stream) + { + var num = stream.ReadInt32(); + var list = new List(num); + for (var i = 0; i < num; i++) { - var num = stream.ReadInt32(); - var list = new List(num); - for (var i = 0; i < num; i++) - { - list.Add(stream.ReadSizedString()); - } - return list; + list.Add(stream.ReadSizedString()); } + return list; + } - public static Guid ReadGuid(this BinaryReader stream) => new Guid(stream.ReadBytes(16)); + public static Guid ReadGuid(this BinaryReader stream) => new Guid(stream.ReadBytes(16)); - public static BinaryReader OpenBinaryReader(this FileInfo file) => new(file.Open(FileMode.Open, FileAccess.Read, FileShare.Read)); + public static BinaryReader OpenBinaryReader(this FileInfo file) => new(file.Open(FileMode.Open, FileAccess.Read, FileShare.Read)); - public static BinaryReader CreateBinaryReader(this byte[] data) => new(new MemoryStream(data, false)); - } + public static BinaryReader CreateBinaryReader(this byte[] data) => new(new MemoryStream(data, false)); } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherMessageBoxVM.cs b/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherMessageBoxVM.cs index 35ee631..fc680a2 100644 --- a/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherMessageBoxVM.cs +++ b/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherMessageBoxVM.cs @@ -3,52 +3,51 @@ using System; -namespace Bannerlord.LauncherEx.ViewModels +namespace Bannerlord.LauncherEx.ViewModels; + +internal class BUTRLauncherMessageBoxVM : BUTRViewModel { - internal class BUTRLauncherMessageBoxVM : BUTRViewModel + [BUTRDataSourceProperty] + public string Title { get => _title; set => SetField(ref _title, value); } + private string _title = string.Empty; + + [BUTRDataSourceProperty] + public string Description { get => _description; set => SetField(ref _description, value); } + private string _description = string.Empty; + + [BUTRDataSourceProperty] + public bool IsEnabled { get => _isEnabled; set => SetField(ref _isEnabled, value); } + private bool _isEnabled; + + [BUTRDataSourceProperty] + public string CancelText => new BUTRTextObject("{=DzJmcvsP}Cancel").ToString(); + [BUTRDataSourceProperty] + public string ConfirmText => new BUTRTextObject("{=epTxGUqT}Confirm").ToString(); + + + private Action? _onConfirm; + private Action? _onCancel; + + public void Show(string title, string description, Action? onConfirm, Action? onCancel) + { + if (IsEnabled) return; + + Title = title; + Description = description; + _onConfirm = onConfirm; + _onCancel = onCancel; + IsEnabled = true; + } + + private void ExecuteConfirm() + { + _onConfirm?.Invoke(); + IsEnabled = false; + } + + private void ExecuteCancel() { - [BUTRDataSourceProperty] - public string Title { get => _title; set => SetField(ref _title, value); } - private string _title = string.Empty; - - [BUTRDataSourceProperty] - public string Description { get => _description; set => SetField(ref _description, value); } - private string _description = string.Empty; - - [BUTRDataSourceProperty] - public bool IsEnabled { get => _isEnabled; set => SetField(ref _isEnabled, value); } - private bool _isEnabled; - - [BUTRDataSourceProperty] - public string CancelText => new BUTRTextObject("{=DzJmcvsP}Cancel").ToString(); - [BUTRDataSourceProperty] - public string ConfirmText => new BUTRTextObject("{=epTxGUqT}Confirm").ToString(); - - - private Action? _onConfirm; - private Action? _onCancel; - - public void Show(string title, string description, Action? onConfirm, Action? onCancel) - { - if (IsEnabled) return; - - Title = title; - Description = description; - _onConfirm = onConfirm; - _onCancel = onCancel; - IsEnabled = true; - } - - private void ExecuteConfirm() - { - _onConfirm?.Invoke(); - IsEnabled = false; - } - - private void ExecuteCancel() - { - _onCancel?.Invoke(); - IsEnabled = false; - } + _onCancel?.Invoke(); + IsEnabled = false; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherModuleVM.cs b/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherModuleVM.cs index ff550da..7f10a54 100644 --- a/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherModuleVM.cs +++ b/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherModuleVM.cs @@ -1,4 +1,5 @@ -using Bannerlord.LauncherEx.Helpers; +using Bannerlord.BUTR.Shared.Helpers; +using Bannerlord.LauncherEx.Helpers; using Bannerlord.LauncherManager.Localization; using Bannerlord.LauncherManager.Models; using Bannerlord.LauncherManager.Utils; @@ -6,166 +7,217 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Linq; using TaleWorlds.MountAndBlade.Launcher.Library; -namespace Bannerlord.LauncherEx.ViewModels +namespace Bannerlord.LauncherEx.ViewModels; + +internal sealed class BUTRLauncherModuleVM : BUTRViewModel, IModuleViewModel { - internal sealed class BUTRLauncherModuleVM : BUTRViewModel, IModuleViewModel - { - private readonly Action _select; - private readonly Func> _validate; + private readonly Action _select; + private readonly Func> _validate; + private readonly Func> _getPossibleProviders; - public ModuleInfoExtendedWithPath ModuleInfoExtended { get; } + public ModuleInfoExtendedWithMetadata ModuleInfoExtended { get; } - public int Index { get; set; } + public int Index { get; set; } - [BUTRDataSourceProperty] - public string Name => ModuleInfoExtended.Name; + [BUTRDataSourceProperty] + public string Name => ModuleInfoExtended.Name; - [BUTRDataSourceProperty] - public string VersionText => ModuleInfoExtended.Version.ToString(); + [BUTRDataSourceProperty] + public string VersionText => ModuleInfoExtended.Version.ToString(); - [BUTRDataSourceProperty] - public bool IsOfficial => ModuleInfoExtended.IsOfficial; + [BUTRDataSourceProperty] + public bool IsOfficial => ModuleInfoExtended.IsOfficial; - [BUTRDataSourceProperty] - public bool IsDangerous { get => _isDangerous; set => SetField(ref _isDangerous, value); } - private bool _isDangerous; + [BUTRDataSourceProperty] + public bool IsDangerous { get => _isDangerous; set => SetField(ref _isDangerous, value); } + private bool _isDangerous; - [BUTRDataSourceProperty] - public LauncherHintVM? DangerousHint { get => _dangerousHint; set => SetField(ref _dangerousHint, value); } - private LauncherHintVM? _dangerousHint; + [BUTRDataSourceProperty] + public LauncherHintVM? DangerousHint { get => _dangerousHint; set => SetField(ref _dangerousHint, value); } + private LauncherHintVM? _dangerousHint; - [BUTRDataSourceProperty] - public LauncherHintVM? DependencyHint { get => _dependencyHint; set => SetField(ref _dependencyHint, value); } - private LauncherHintVM? _dependencyHint; + [BUTRDataSourceProperty] + public LauncherHintVM? DependencyHint { get => _dependencyHint; set => SetField(ref _dependencyHint, value); } + private LauncherHintVM? _dependencyHint; - [BUTRDataSourceProperty] - public bool AnyDependencyAvailable { get => _anyDependencyAvailable; set => SetField(ref _anyDependencyAvailable, value); } - private bool _anyDependencyAvailable; + [BUTRDataSourceProperty] + public bool AnyDependencyAvailable { get => _anyDependencyAvailable; set => SetField(ref _anyDependencyAvailable, value); } + private bool _anyDependencyAvailable; - [BUTRDataSourceProperty] - public bool IsSelected { get => _isSelected; set => SetField(ref _isSelected, value); } - private bool _isSelected; + [BUTRDataSourceProperty] + public bool IsSelected { get => _isSelected; set => SetField(ref _isSelected, value); } + private bool _isSelected; - [BUTRDataSourceProperty] - public bool IsDisabled + [BUTRDataSourceProperty] + public bool IsDisabled + { + get => _isDisabled; + set { - get => _isDisabled; - set + if (value != _isDisabled) { - if (value != _isDisabled) - { - _isDisabled = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(IsNotSelectable)); - } + _isDisabled = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(IsNotSelectable)); } } - private bool _isDisabled; + } + private bool _isDisabled; - [BUTRDataSourceProperty] - public bool IsExpanded { get => _isExpanded; set => SetField(ref _isExpanded, value); } - private bool _isExpanded; + [BUTRDataSourceProperty] + public bool IsExpanded { get => _isExpanded; set => SetField(ref _isExpanded, value); } + private bool _isExpanded; - [BUTRDataSourceProperty] - public string IssuesText + [BUTRDataSourceProperty] + public string IssuesText + { + get => _issuesText; + set { - get => _issuesText; - set + if (value != _issuesText) { - if (value != _issuesText) - { - _issuesText = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(IsNotSelectable)); - OnPropertyChanged(nameof(IsValid)); - } + _issuesText = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(IsNotSelectable)); + OnPropertyChanged(nameof(IsValid)); } } - private string _issuesText = string.Empty; + } + private string _issuesText = string.Empty; - [BUTRDataSourceProperty] - public bool IsNotSelectable => !IsValid || IsDisabled; + [BUTRDataSourceProperty] + public bool IsNotSelectable => !IsValid || IsDisabled; - [BUTRDataSourceProperty] - public bool IsValid => string.IsNullOrWhiteSpace(IssuesText); + [BUTRDataSourceProperty] + public bool IsValid => string.IsNullOrWhiteSpace(IssuesText); - [BUTRDataSourceProperty] - public bool IsVisible { get => _isVisible; set => SetField(ref _isVisible, value); } - private bool _isVisible = true; + [BUTRDataSourceProperty] + public bool IsVisible { get => _isVisible; set => SetField(ref _isVisible, value); } + private bool _isVisible = true; - public BUTRLauncherModuleVM(ModuleInfoExtendedWithPath moduleInfoExtended, Action select, Func> validate) - { - ModuleInfoExtended = moduleInfoExtended; - _select = select; - _validate = validate; - if (ModuleDependencyConstructor.GetDependencyHint(moduleInfoExtended) is { } str) - { - DependencyHint = new LauncherHintVM(str); - AnyDependencyAvailable = !string.IsNullOrEmpty(str); - } + [BUTRDataSourceProperty] + public bool UpdateInfoAvailable { get => _updateInfoAvailable; set => SetField(ref _updateInfoAvailable, value); } + private bool _updateInfoAvailable; - var dangerous = string.Empty; - if (ModuleChecker.IsInstalledInMainAndExternalModuleDirectory(moduleInfoExtended)) - { - dangerous += new BUTRTextObject("{=kfMQEOFS}The Module is installed in the game's /Modules folder and on Steam Workshop!{NL}The /Modules version will be used!").ToString(); - } - if (ModuleChecker.IsObfuscated(moduleInfoExtended)) - { - if (dangerous.Length != 0) dangerous += "\n"; - dangerous += new BUTRTextObject("{=aAYdk1zd}The DLL is obfuscated!{NL}There is no guarantee that the code is safe!{NL}The BUTR Team warns of consequences arising from running obfuscated code!").ToString(); - } - if (!string.IsNullOrEmpty(dangerous)) - { - IsDangerous = true; - DangerousHint = new LauncherHintVM(dangerous); - } - else - { - IsDangerous = false; - DangerousHint = new LauncherHintVM(dangerous); - } - } + [BUTRDataSourceProperty] + public double CompatibilityScore { get => _compatibilityScore; set => SetField(ref _compatibilityScore, value); } + private double _compatibilityScore = 1d; - public void Validate() - { - var validationIssues = _validate(this).ToList(); + [BUTRDataSourceProperty] + public string RecommendedVersion { get => _recommendedVersion; set => SetField(ref _recommendedVersion, value); } + private string _recommendedVersion = string.Empty; - IssuesText = validationIssues.Count > 0 - ? string.Join("\n", validationIssues) - : string.Empty; - } + [BUTRDataSourceProperty] + public LauncherHintVM? UpdateHint { get => _updateHint; set => SetField(ref _updateHint, value); } + private LauncherHintVM? _updateHint; - [BUTRDataSourceMethod] - public void ExecuteSelect() - { - if (IsNotSelectable) - return; + public BUTRLauncherModuleVM(ModuleInfoExtendedWithMetadata moduleInfoExtended, Action select, Func> validate, + Func> getPossibleProviders) + { + ModuleInfoExtended = moduleInfoExtended; + _select = select; + _validate = validate; + _getPossibleProviders = getPossibleProviders; - _select(this); + if (ModuleDependencyConstructor.GetDependencyHint(moduleInfoExtended) is { } str) + { + DependencyHint = new LauncherHintVM(str); + AnyDependencyAvailable = !string.IsNullOrEmpty(str); } - [BUTRDataSourceMethod] - public void ExecuteOpen() + Refresh(); + } + + public void Validate() + { + var validationIssues = _validate(this).ToList(); + + IssuesText = validationIssues.Count > 0 + ? string.Join("\n", validationIssues) + : string.Empty; + } + + public void Refresh() + { + var dangerous = string.Empty; + if (_getPossibleProviders(ModuleInfoExtended) is { } providers && providers.Any(x => x == ModuleProviderType.Steam)) { - if (Integrations.IsModOrganizer2) - { - var explorer = Path.Combine(Integrations.ModOrganizer2Path!, "explorer++", "Explorer++.exe"); - if (!File.Exists(explorer)) return; - Process.Start(explorer, $"\"{ModuleInfoExtended.Path}\""); - return; - } + dangerous += new BUTRTextObject("{=kfMQEOFS}The Module is installed in the game's /Modules folder and on Steam Workshop!{NL}The /Modules version will be used!").ToString(); + } + if (ModuleChecker.IsObfuscated(ModuleInfoExtended)) + { + if (dangerous.Length != 0) dangerous += "\n"; + dangerous += new BUTRTextObject("{=aAYdk1zd}The DLL is obfuscated!{NL}There is no guarantee that the code is safe!{NL}The BUTR Team warns of consequences arising from running obfuscated code!").ToString(); + } + if (!string.IsNullOrEmpty(dangerous)) + { + IsDangerous = true; + DangerousHint = new LauncherHintVM(dangerous); + } + else + { + IsDangerous = false; + DangerousHint = new LauncherHintVM(dangerous); + } + } - if (!Directory.Exists(ModuleInfoExtended.Path)) return; - Process.Start(ModuleInfoExtended.Path); + [BUTRDataSourceMethod] + public void ExecuteSelect() + { + if (IsNotSelectable) + return; + + _select(this); + } + + [BUTRDataSourceMethod] + public void ExecuteOpen() + { + if (Integrations.IsModOrganizer2) + { + var explorer = Path.Combine(Integrations.ModOrganizer2Path!, "explorer++", "Explorer++.exe"); + if (!File.Exists(explorer)) return; + Process.Start(explorer, $"\"{ModuleInfoExtended.Path}\""); + return; } - public override string ToString() => $"{ModuleInfoExtended}, IsSelected: {IsSelected}, IsValid: {IsValid}"; + if (!Directory.Exists(ModuleInfoExtended.Path)) return; + Process.Start(ModuleInfoExtended.Path); } + + public void SetUpdateInfo(double compatibilityScore, string? recommendedVersion) + { + UpdateInfoAvailable = true; + CompatibilityScore = compatibilityScore; + RecommendedVersion = recommendedVersion ?? string.Empty; + + var hasRecommendedVersion = !string.IsNullOrEmpty(recommendedVersion); + + UpdateHint = new LauncherHintVM(new BUTRTextObject(hasRecommendedVersion + ? "{=HdnFwgVB}Based on BUTR analytics:{NL}{NL}Suggesting to update to {RECOMMENDEDVERSION}.{NL}Compatibility Score {SCORE}%{NL}{NL}{RECOMMENDEDVERSION} has a better compatibility for game {GAMEVERSION} rather than {CURRENTVERSION}!" + : "{=HdnFwgVA}Based on BUTR analytics:{NL}{NL}Update is not requiured.{NL}Compatibility Score {SCORE}%{NL}{NL}{CURRENTVERSION} is one of the best version for game {GAMEVERSION}") + .SetTextVariable("SCORE", CompatibilityScore.ToString(CultureInfo.InvariantCulture)) + .SetTextVariable("CURRENTVERSION", VersionText) + .SetTextVariable("RECOMMENDEDVERSION", RecommendedVersion) + .SetTextVariable("GAMEVERSION", ApplicationVersionHelper.GameVersionStr()).ToString()); + } + + public void RemoveUpdateInfo() + { + UpdateInfoAvailable = false; + CompatibilityScore = 0d; + RecommendedVersion = string.Empty; + UpdateHint = null; + } + + + public override string ToString() => $"{ModuleInfoExtended}, IsSelected: {IsSelected}, IsValid: {IsValid}"; } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherOptionsVM.cs b/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherOptionsVM.cs index c2769d9..c27cc9d 100644 --- a/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherOptionsVM.cs +++ b/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherOptionsVM.cs @@ -12,422 +12,421 @@ using TaleWorlds.Library; -namespace Bannerlord.LauncherEx.ViewModels +namespace Bannerlord.LauncherEx.ViewModels; + +internal enum OptionsType { - internal enum OptionsType - { - Launcher, Game, Engine - } + Launcher, Game, Engine +} - internal sealed class BUTRLauncherOptionsVM : BUTRViewModel - { - private readonly BUTRLauncherManagerHandler _launcherManagerHandler = BUTRLauncherManagerHandler.Default; - private readonly OptionsType _optionsType; - private LauncherExData? _launcherExData; - private readonly Action _saveUserData; - private readonly Action _refreshOptions; +internal sealed class BUTRLauncherOptionsVM : BUTRViewModel +{ + private readonly BUTRLauncherManagerHandler _launcherManagerHandler = BUTRLauncherManagerHandler.Default; + private readonly OptionsType _optionsType; + private LauncherExData? _launcherExData; + private readonly Action _saveUserData; + private readonly Action _refreshOptions; - [BUTRDataSourceProperty] - public bool IsDisabled { get => _isDisabled; set => SetField(ref _isDisabled, value); } - private bool _isDisabled; + [BUTRDataSourceProperty] + public bool IsDisabled { get => _isDisabled; set => SetField(ref _isDisabled, value); } + private bool _isDisabled; - [BUTRDataSourceProperty] - public MBBindingList SettingProperties { get => _settingProperties; set => SetField(ref _settingProperties, value); } - private MBBindingList _settingProperties = new(); + [BUTRDataSourceProperty] + public MBBindingList SettingProperties { get => _settingProperties; set => SetField(ref _settingProperties, value); } + private MBBindingList _settingProperties = new(); - [BUTRDataSourceProperty] - public string NeedsGameLaunchMessage { get => _needsGameLaunchMessage; set => SetField(ref _needsGameLaunchMessage, value); } - private string _needsGameLaunchMessage = new BUTRTextObject("{=jfNh7Sg3}One-time game launch is required!").ToString(); + [BUTRDataSourceProperty] + public string NeedsGameLaunchMessage { get => _needsGameLaunchMessage; set => SetField(ref _needsGameLaunchMessage, value); } + private string _needsGameLaunchMessage = new BUTRTextObject("{=jfNh7Sg3}One-time game launch is required!").ToString(); - [BUTRDataSourceProperty] - public bool NeedsGameLaunch { get => _needsGameLaunch; set => SetField(ref _needsGameLaunch, value); } - private bool _needsGameLaunch; + [BUTRDataSourceProperty] + public bool NeedsGameLaunch { get => _needsGameLaunch; set => SetField(ref _needsGameLaunch, value); } + private bool _needsGameLaunch; - public BUTRLauncherOptionsVM(OptionsType optionsType, Action saveUserData, Action refreshOptions) - { - _optionsType = optionsType; - _saveUserData = saveUserData; - _refreshOptions = refreshOptions; - } + public BUTRLauncherOptionsVM(OptionsType optionsType, Action saveUserData, Action refreshOptions) + { + _optionsType = optionsType; + _saveUserData = saveUserData; + _refreshOptions = refreshOptions; + } - public void Refresh() + public void Refresh() + { + SettingProperties.Clear(); + switch (_optionsType) { - SettingProperties.Clear(); - switch (_optionsType) - { - case OptionsType.Launcher: - RefreshLauncherOptions(); - break; - case OptionsType.Game: - RefreshGameOptions(); - break; - case OptionsType.Engine: - RefreshEngineOptions(); - break; - } - } - private void RefreshLauncherOptions() - { - _launcherExData = new LauncherExData( - LauncherSettings.AutomaticallyCheckForUpdates, - LauncherSettings.FixCommonIssues, - LauncherSettings.CompactModuleList, - LauncherSettings.HideRandomImage, - LauncherSettings.DisableBinaryCheck, - LauncherSettings.BetaSorting, - LauncherSettings.BigMode, - LauncherSettings.EnableDPIScaling, - LauncherSettings.DisableCrashHandlerWhenDebuggerIsAttached, - LauncherSettings.DisableCatchAutoGenExceptions, - LauncherSettings.UseVanillaCrashHandler); - - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = new BUTRTextObject("{=LXlsSS8t}Fix Common Issues").ToString(), - HintText = new BUTRTextObject("{=J9VbkLW4}Fixes issues like 0Harmony.dll being in the /bin folder").ToString(), - SettingType = SettingType.Bool, - PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.FixCommonIssues))!, this) - })); - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = new BUTRTextObject("{=vUAqDj9H}Compact Module List").ToString(), - HintText = $"{new BUTRTextObject("{=44qrhQ6g}Requires restart!")} {new BUTRTextObject("{=Qn1aPNQM}Makes the Mods tab content smaller")}", - SettingType = SettingType.Bool, - PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.CompactModuleList))!, this) - })); - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = new BUTRTextObject("{=GUWbD65T}Disable Binary Compatibility Check").ToString(), - HintText = $"{new BUTRTextObject("{=z9WqFewN}DISABLED!")} {new BUTRTextObject("{=44qrhQ6g}Requires restart!")} {new BUTRTextObject("{=lmpQeQBS}Disables Launcher's own check for binary compatibility of mods")}", - SettingType = SettingType.Bool, - PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.DisableBinaryCheck))!, this) - })); - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = new BUTRTextObject("{=iD27wEq7}Hide Random Image").ToString(), - HintText = new BUTRTextObject("{=LaPvZjwC}Hide's the Rider image so the launcher looks more compact").ToString(), - SettingType = SettingType.Bool, - PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.HideRandomImage))!, this) - })); - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = new BUTRTextObject("{=QJSBiZdJ}Beta Sorting").ToString(), - HintText = new BUTRTextObject("{=HVhaqeb4}Uses the new sorting algorithm after v1.12.x. Disable to use the old algorithm").ToString(), - SettingType = SettingType.Bool, - PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.BetaSorting))!, this) - })); - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = new BUTRTextObject("{=1zt99vTt}Big Mode").ToString(), - HintText = new BUTRTextObject("{=XUSDSpvf}Makes the launcher bigger in height").ToString(), - SettingType = SettingType.Bool, - PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.BigMode))!, this) - })); - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = new BUTRTextObject("{=1zt99vTt}Enable DPI Scaling").ToString(), - HintText = $"{new BUTRTextObject("{=44qrhQ6g}Requires restart!")} {new BUTRTextObject("{=JusnHy6S}Enables Windows DPI Scaling to remove blurriness of UI elements")}", - SettingType = SettingType.Bool, - PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.EnableDPIScaling))!, this) - })); - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = new BUTRTextObject("{=IsR2rbnG}Restore Game Options Backup").ToString(), - HintText = new BUTRTextObject("{=uKUsA3Sp}LauncherEx always makes a backup before saving the first time. This will restore the original files").ToString(), - SettingType = SettingType.Button, - PropertyReference = new ProxyRef(() => new BUTRTextObject("{=TLDgPay9}Restore").ToString(), _ => - { - var backupPath = $"{ConfigReader.GameConfigPath}.bak"; - if (File.Exists(backupPath)) - { - File.Copy(backupPath, ConfigReader.GameConfigPath, true); - File.Delete(backupPath); - _refreshOptions(); - } - }) - })); - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = new BUTRTextObject("{=5XzSM7RN}Restore Engine Options Backup").ToString(), - HintText = new BUTRTextObject("{=uKUsA3Sp}LauncherEx always makes a backup before saving the first time. This will restore the original files").ToString(), - SettingType = SettingType.Button, - PropertyReference = new ProxyRef(() => new BUTRTextObject("{=TLDgPay9}Restore").ToString(), _ => - { - var backupPath = $"{ConfigReader.EngineConfigPath}.bak"; - if (File.Exists(backupPath)) - { - File.Copy(backupPath, ConfigReader.EngineConfigPath, true); - File.Delete(backupPath); - _refreshOptions(); - } - }) - })); - /* - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = "Automatically Check for Updates", - SettingType = SettingType.Bool, - PropertyReference = new PropertyRef(typeof(BUTRLoaderAppDomainManager).GetProperty(nameof(BUTRLoaderAppDomainManager.AutomaticallyCheckForUpdates))!, this) - })); - */ - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = new BUTRTextObject("{=QzPFvxGy}Disable BLSE Crash Handler When Debugger Is Attached").ToString(), - HintText = new BUTRTextObject("{=P5NWQtKr}Stops BLSE Crash Handler when a debugger is attached. Do not disable if not sure.").ToString(), - SettingType = SettingType.Bool, - PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.DisableCrashHandlerWhenDebuggerIsAttached))!, this) - })); - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = new BUTRTextObject("{=NkCBdPSE}Disable Auto Generated Method Exception Catching").ToString(), - HintText = new BUTRTextObject("{=QWGZy8Ym}Disables catching every Native->Managed call. It should catch every exception not catched the standard way. Do not disable if not sure.").ToString(), - SettingType = SettingType.Bool, - PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.DisableCatchAutoGenExceptions))!, this) - })); - SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition - { - DisplayName = new BUTRTextObject("{=qKK4Ehyd}Use Vanilla Crash Handler").ToString(), - HintText = new BUTRTextObject("{=RTmWsIEA}Disables ButterLib's and BEW's Crash Handlers with the new Watchdog Crash Handler. Do not enable if not sure.").ToString(), - SettingType = SettingType.Bool, - PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.UseVanillaCrashHandler))!, this) - })); + case OptionsType.Launcher: + RefreshLauncherOptions(); + break; + case OptionsType.Game: + RefreshGameOptions(); + break; + case OptionsType.Engine: + RefreshEngineOptions(); + break; } - private void RefreshGameOptions() + } + private void RefreshLauncherOptions() + { + _launcherExData = new LauncherExData( + LauncherSettings.AutomaticallyCheckForUpdates, + LauncherSettings.FixCommonIssues, + LauncherSettings.CompactModuleList, + LauncherSettings.HideRandomImage, + LauncherSettings.DisableBinaryCheck, + LauncherSettings.BetaSorting, + LauncherSettings.BigMode, + LauncherSettings.EnableDPIScaling, + LauncherSettings.DisableCrashHandlerWhenDebuggerIsAttached, + LauncherSettings.DisableCatchAutoGenExceptions, + LauncherSettings.UseVanillaCrashHandler); + + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition + { + DisplayName = new BUTRTextObject("{=LXlsSS8t}Fix Common Issues").ToString(), + HintText = new BUTRTextObject("{=J9VbkLW4}Fixes issues like 0Harmony.dll being in the /bin folder").ToString(), + SettingType = SettingType.Bool, + PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.FixCommonIssues))!, this) + })); + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition + { + DisplayName = new BUTRTextObject("{=vUAqDj9H}Compact Module List").ToString(), + HintText = $"{new BUTRTextObject("{=44qrhQ6g}Requires restart!")} {new BUTRTextObject("{=Qn1aPNQM}Makes the Mods tab content smaller")}", + SettingType = SettingType.Bool, + PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.CompactModuleList))!, this) + })); + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition + { + DisplayName = new BUTRTextObject("{=GUWbD65T}Disable Binary Compatibility Check").ToString(), + HintText = $"{new BUTRTextObject("{=z9WqFewN}DISABLED!")} {new BUTRTextObject("{=44qrhQ6g}Requires restart!")} {new BUTRTextObject("{=lmpQeQBS}Disables Launcher's own check for binary compatibility of mods")}", + SettingType = SettingType.Bool, + PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.DisableBinaryCheck))!, this) + })); + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition + { + DisplayName = new BUTRTextObject("{=iD27wEq7}Hide Random Image").ToString(), + HintText = new BUTRTextObject("{=LaPvZjwC}Hide's the Rider image so the launcher looks more compact").ToString(), + SettingType = SettingType.Bool, + PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.HideRandomImage))!, this) + })); + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition { - try + DisplayName = new BUTRTextObject("{=QJSBiZdJ}Beta Sorting").ToString(), + HintText = new BUTRTextObject("{=HVhaqeb4}Uses the new sorting algorithm after v1.12.x. Disable to use the old algorithm").ToString(), + SettingType = SettingType.Bool, + PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.BetaSorting))!, this) + })); + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition + { + DisplayName = new BUTRTextObject("{=1zt99vTt}Big Mode").ToString(), + HintText = new BUTRTextObject("{=XUSDSpvf}Makes the launcher bigger in height").ToString(), + SettingType = SettingType.Bool, + PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.BigMode))!, this) + })); + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition + { + DisplayName = new BUTRTextObject("{=1zt99vTt}Enable DPI Scaling").ToString(), + HintText = $"{new BUTRTextObject("{=44qrhQ6g}Requires restart!")} {new BUTRTextObject("{=JusnHy6S}Enables Windows DPI Scaling to remove blurriness of UI elements")}", + SettingType = SettingType.Bool, + PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.EnableDPIScaling))!, this) + })); + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition + { + DisplayName = new BUTRTextObject("{=IsR2rbnG}Restore Game Options Backup").ToString(), + HintText = new BUTRTextObject("{=uKUsA3Sp}LauncherEx always makes a backup before saving the first time. This will restore the original files").ToString(), + SettingType = SettingType.Button, + PropertyReference = new ProxyRef(() => new BUTRTextObject("{=TLDgPay9}Restore").ToString(), _ => { - var options = ConfigReader.GetGameOptions(path => File.Exists(path) ? File.ReadAllBytes(path) : null); - if (options.Count == 0) - NeedsGameLaunch = true; - - foreach (var (key, value) in options) + var backupPath = $"{ConfigReader.GameConfigPath}.bak"; + if (File.Exists(backupPath)) { - SettingProperties.Add(CreateSettingsPropertyVM(key, value, ToSeparateWords)); + File.Copy(backupPath, ConfigReader.GameConfigPath, true); + File.Delete(backupPath); + _refreshOptions(); } - } - catch (Exception) { /* ignore */ } - } - private void RefreshEngineOptions() + }) + })); + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition { - try + DisplayName = new BUTRTextObject("{=5XzSM7RN}Restore Engine Options Backup").ToString(), + HintText = new BUTRTextObject("{=uKUsA3Sp}LauncherEx always makes a backup before saving the first time. This will restore the original files").ToString(), + SettingType = SettingType.Button, + PropertyReference = new ProxyRef(() => new BUTRTextObject("{=TLDgPay9}Restore").ToString(), _ => { - var options = ConfigReader.GetEngineOptions(path => File.Exists(path) ? File.ReadAllBytes(path) : null); - if (options.Count == 0) - NeedsGameLaunch = true; - - foreach (var (key, value) in options) + var backupPath = $"{ConfigReader.EngineConfigPath}.bak"; + if (File.Exists(backupPath)) { - SettingProperties.Add(CreateSettingsPropertyVM(key, value, x => ToTitleCase(x.Replace("_", " ")))); + File.Copy(backupPath, ConfigReader.EngineConfigPath, true); + File.Delete(backupPath); + _refreshOptions(); } - } - catch (Exception) { /* ignore */ } - } - - public void Save() + }) + })); + /* + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition + { + DisplayName = "Automatically Check for Updates", + SettingType = SettingType.Bool, + PropertyReference = new PropertyRef(typeof(BUTRLoaderAppDomainManager).GetProperty(nameof(BUTRLoaderAppDomainManager.AutomaticallyCheckForUpdates))!, this) + })); + */ + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition + { + DisplayName = new BUTRTextObject("{=QzPFvxGy}Disable BLSE Crash Handler When Debugger Is Attached").ToString(), + HintText = new BUTRTextObject("{=P5NWQtKr}Stops BLSE Crash Handler when a debugger is attached. Do not disable if not sure.").ToString(), + SettingType = SettingType.Bool, + PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.DisableCrashHandlerWhenDebuggerIsAttached))!, this) + })); + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition + { + DisplayName = new BUTRTextObject("{=NkCBdPSE}Disable Auto Generated Method Exception Catching").ToString(), + HintText = new BUTRTextObject("{=QWGZy8Ym}Disables catching every Native->Managed call. It should catch every exception not catched the standard way. Do not disable if not sure.").ToString(), + SettingType = SettingType.Bool, + PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.DisableCatchAutoGenExceptions))!, this) + })); + SettingProperties.Add(new SettingsPropertyVM(new SettingsPropertyDefinition { - switch (_optionsType) + DisplayName = new BUTRTextObject("{=qKK4Ehyd}Use Vanilla Crash Handler").ToString(), + HintText = new BUTRTextObject("{=RTmWsIEA}Disables ButterLib's and BEW's Crash Handlers with the new Watchdog Crash Handler. Do not enable if not sure.").ToString(), + SettingType = SettingType.Bool, + PropertyReference = new PropertyRef(typeof(LauncherSettings).GetProperty(nameof(LauncherSettings.UseVanillaCrashHandler))!, this) + })); + } + private void RefreshGameOptions() + { + try + { + var options = ConfigReader.GetGameOptions(path => File.Exists(path) ? File.ReadAllBytes(path) : null); + if (options.Count == 0) + NeedsGameLaunch = true; + + foreach (var (key, value) in options) { - case OptionsType.Launcher: - SaveLauncherOptions(); - break; - case OptionsType.Game: - SaveGameOptions(); - break; - case OptionsType.Engine: - SaveEngineOptions(); - break; + SettingProperties.Add(CreateSettingsPropertyVM(key, value, ToSeparateWords)); } } - private void SaveLauncherOptions() + catch (Exception) { /* ignore */ } + } + private void RefreshEngineOptions() + { + try { - if (_launcherExData is null) - return; + var options = ConfigReader.GetEngineOptions(path => File.Exists(path) ? File.ReadAllBytes(path) : null); + if (options.Count == 0) + NeedsGameLaunch = true; - if (_launcherExData.AutomaticallyCheckForUpdates != LauncherSettings.AutomaticallyCheckForUpdates) + foreach (var (key, value) in options) { - _saveUserData(); - return; + SettingProperties.Add(CreateSettingsPropertyVM(key, value, x => ToTitleCase(x.Replace("_", " ")))); } + } + catch (Exception) { /* ignore */ } + } - if (_launcherExData.FixCommonIssues != LauncherSettings.FixCommonIssues) - { - _saveUserData(); - return; - } + public void Save() + { + switch (_optionsType) + { + case OptionsType.Launcher: + SaveLauncherOptions(); + break; + case OptionsType.Game: + SaveGameOptions(); + break; + case OptionsType.Engine: + SaveEngineOptions(); + break; + } + } + private void SaveLauncherOptions() + { + if (_launcherExData is null) + return; - if (_launcherExData.CompactModuleList != LauncherSettings.CompactModuleList) - { - _saveUserData(); - return; - } + if (_launcherExData.AutomaticallyCheckForUpdates != LauncherSettings.AutomaticallyCheckForUpdates) + { + _saveUserData(); + return; + } - if (_launcherExData.HideRandomImage != LauncherSettings.HideRandomImage) - { - _saveUserData(); - return; - } + if (_launcherExData.FixCommonIssues != LauncherSettings.FixCommonIssues) + { + _saveUserData(); + return; + } - if (_launcherExData.DisableBinaryCheck != LauncherSettings.DisableBinaryCheck) - { - _saveUserData(); - return; - } + if (_launcherExData.CompactModuleList != LauncherSettings.CompactModuleList) + { + _saveUserData(); + return; + } - if (_launcherExData.BetaSorting != LauncherSettings.BetaSorting) - { - _saveUserData(); - return; - } + if (_launcherExData.HideRandomImage != LauncherSettings.HideRandomImage) + { + _saveUserData(); + return; + } - if (_launcherExData.BigMode != LauncherSettings.BigMode) - { - _saveUserData(); - return; - } + if (_launcherExData.DisableBinaryCheck != LauncherSettings.DisableBinaryCheck) + { + _saveUserData(); + return; + } - if (_launcherExData.EnableDPIScaling != LauncherSettings.EnableDPIScaling) - { - _saveUserData(); - return; - } + if (_launcherExData.BetaSorting != LauncherSettings.BetaSorting) + { + _saveUserData(); + return; + } - if (_launcherExData.DisableCrashHandlerWhenDebuggerIsAttached != LauncherSettings.DisableCrashHandlerWhenDebuggerIsAttached) - { - _saveUserData(); - return; - } + if (_launcherExData.BigMode != LauncherSettings.BigMode) + { + _saveUserData(); + return; + } - if (_launcherExData.DisableCatchAutoGenExceptions != LauncherSettings.DisableCatchAutoGenExceptions) - { - _saveUserData(); - return; - } + if (_launcherExData.EnableDPIScaling != LauncherSettings.EnableDPIScaling) + { + _saveUserData(); + return; + } - if (_launcherExData.UseVanillaCrashHandler != LauncherSettings.UseVanillaCrashHandler) - { - _saveUserData(); - return; - } + if (_launcherExData.DisableCrashHandlerWhenDebuggerIsAttached != LauncherSettings.DisableCrashHandlerWhenDebuggerIsAttached) + { + _saveUserData(); + return; } - private void SaveGameOptions() + + if (_launcherExData.DisableCatchAutoGenExceptions != LauncherSettings.DisableCatchAutoGenExceptions) { - var backupPath = $"{ConfigReader.GameConfigPath}.bak"; - if (File.Exists(ConfigReader.GameConfigPath) && !File.Exists(backupPath)) - File.Copy(ConfigReader.GameConfigPath, backupPath); + _saveUserData(); + return; + } - var hasChanges = false; - var sb = new StringBuilder(); - foreach (var settingProperty in SettingProperties) - { - if (settingProperty.SettingPropertyDefinition is not ConfigSettingsPropertyDefinition propertyDefinition) - continue; + if (_launcherExData.UseVanillaCrashHandler != LauncherSettings.UseVanillaCrashHandler) + { + _saveUserData(); + return; + } + } + private void SaveGameOptions() + { + var backupPath = $"{ConfigReader.GameConfigPath}.bak"; + if (File.Exists(ConfigReader.GameConfigPath) && !File.Exists(backupPath)) + File.Copy(ConfigReader.GameConfigPath, backupPath); - if (!string.Equals(propertyDefinition.OriginalValue, settingProperty.ValueAsString, StringComparison.Ordinal)) - hasChanges = true; + var hasChanges = false; + var sb = new StringBuilder(); + foreach (var settingProperty in SettingProperties) + { + if (settingProperty.SettingPropertyDefinition is not ConfigSettingsPropertyDefinition propertyDefinition) + continue; - sb.AppendLine($"{propertyDefinition.ConfigKey}={settingProperty.ValueAsString}"); - } - if (hasChanges) - { - File.WriteAllText(ConfigReader.GameConfigPath, sb.ToString()); - var options = _launcherManagerHandler.GetTWOptions(); - BUTRLocalizationManager.ActiveLanguage = options.Language; - } + if (!string.Equals(propertyDefinition.OriginalValue, settingProperty.ValueAsString, StringComparison.Ordinal)) + hasChanges = true; + + sb.AppendLine($"{propertyDefinition.ConfigKey}={settingProperty.ValueAsString}"); } - private void SaveEngineOptions() + if (hasChanges) { - var backupPath = $"{ConfigReader.EngineConfigPath}.bak"; - if (File.Exists(ConfigReader.EngineConfigPath) && !File.Exists(backupPath)) - File.Copy(ConfigReader.EngineConfigPath, backupPath); + File.WriteAllText(ConfigReader.GameConfigPath, sb.ToString()); + var options = _launcherManagerHandler.GetTWOptions(); + BUTRLocalizationManager.ActiveLanguage = options.Language; + } + } + private void SaveEngineOptions() + { + var backupPath = $"{ConfigReader.EngineConfigPath}.bak"; + if (File.Exists(ConfigReader.EngineConfigPath) && !File.Exists(backupPath)) + File.Copy(ConfigReader.EngineConfigPath, backupPath); - var hasChanges = false; - var sb = new StringBuilder(); - foreach (var settingProperty in SettingProperties) - { - if (settingProperty.SettingPropertyDefinition is not ConfigSettingsPropertyDefinition propertyDefinition) - continue; + var hasChanges = false; + var sb = new StringBuilder(); + foreach (var settingProperty in SettingProperties) + { + if (settingProperty.SettingPropertyDefinition is not ConfigSettingsPropertyDefinition propertyDefinition) + continue; - if (!string.Equals(propertyDefinition.OriginalValue, settingProperty.ValueAsString, StringComparison.Ordinal)) - hasChanges = true; + if (!string.Equals(propertyDefinition.OriginalValue, settingProperty.ValueAsString, StringComparison.Ordinal)) + hasChanges = true; - sb.AppendLine($"{propertyDefinition.ConfigKey} = {settingProperty.ValueAsString}"); - } - if (hasChanges) - File.WriteAllText(ConfigReader.EngineConfigPath, sb.ToString()); + sb.AppendLine($"{propertyDefinition.ConfigKey} = {settingProperty.ValueAsString}"); } + if (hasChanges) + File.WriteAllText(ConfigReader.EngineConfigPath, sb.ToString()); + } - private static SettingsPropertyVM CreateSettingsPropertyVM(string key, string value, Func keyProcessor) + private static SettingsPropertyVM CreateSettingsPropertyVM(string key, string value, Func keyProcessor) + { + var settingsType = bool.TryParse(value, out _) ? SettingType.Bool + : int.TryParse(value, out _) ? SettingType.Int + : float.TryParse(value, out _) ? SettingType.Float + : SettingType.String; + var storage = settingsType switch { - var settingsType = bool.TryParse(value, out _) ? SettingType.Bool - : int.TryParse(value, out _) ? SettingType.Int - : float.TryParse(value, out _) ? SettingType.Float - : SettingType.String; - var storage = settingsType switch - { - SettingType.Bool => (IRef) new StorageRef(bool.Parse(value)), - SettingType.Int => (IRef) new StorageRef(int.Parse(value)), - SettingType.Float => (IRef) new StorageRef(float.Parse(value)), - SettingType.String => (IRef) new StorageRef(value), - _ => throw new ArgumentOutOfRangeException() - }; - var propertyRef = settingsType switch - { - SettingType.Bool => (IRef) new ProxyRef(() => (bool) storage.Value!, val => { storage.Value = val; }), - SettingType.Int => (IRef) new ProxyRef(() => (int) storage.Value!, val => { storage.Value = val; }), - SettingType.Float => (IRef) new ProxyRef(() => (float) storage.Value!, val => { storage.Value = val; }), - SettingType.String => (IRef) new ProxyRef(() => (string) storage.Value!, val => { storage.Value = val; }), - _ => throw new ArgumentOutOfRangeException() - }; - return new SettingsPropertyVM(new ConfigSettingsPropertyDefinition - { - ConfigKey = key, - OriginalValue = value, - DisplayName = keyProcessor(key), - SettingType = settingsType, - PropertyReference = propertyRef - }); - } - - [return: NotNullIfNotNull("value")] - private static string? ToSeparateWords(string? value) + SettingType.Bool => (IRef) new StorageRef(bool.Parse(value)), + SettingType.Int => (IRef) new StorageRef(int.Parse(value)), + SettingType.Float => (IRef) new StorageRef(float.Parse(value)), + SettingType.String => (IRef) new StorageRef(value), + _ => throw new ArgumentOutOfRangeException() + }; + var propertyRef = settingsType switch { - if (value == null) return null; - if (value.Length <= 1) return value; + SettingType.Bool => (IRef) new ProxyRef(() => (bool) storage.Value!, val => { storage.Value = val; }), + SettingType.Int => (IRef) new ProxyRef(() => (int) storage.Value!, val => { storage.Value = val; }), + SettingType.Float => (IRef) new ProxyRef(() => (float) storage.Value!, val => { storage.Value = val; }), + SettingType.String => (IRef) new ProxyRef(() => (string) storage.Value!, val => { storage.Value = val; }), + _ => throw new ArgumentOutOfRangeException() + }; + return new SettingsPropertyVM(new ConfigSettingsPropertyDefinition + { + ConfigKey = key, + OriginalValue = value, + DisplayName = keyProcessor(key), + SettingType = settingsType, + PropertyReference = propertyRef + }); + } - var inChars = value.ToCharArray(); - var uCWithAnyLC = new List(); - var i = 0; - while (i < inChars.Length && char.IsUpper(inChars[i])) { ++i; } + [return: NotNullIfNotNull("value")] + private static string? ToSeparateWords(string? value) + { + if (value == null) return null; + if (value.Length <= 1) return value; - for (; i < inChars.Length; i++) - { - if (!char.IsUpper(inChars[i])) continue; - uCWithAnyLC.Add(i); - if (++i >= inChars.Length || !char.IsUpper(inChars[i])) continue; - while (++i < inChars.Length) - { - if (char.IsUpper(inChars[i])) continue; - uCWithAnyLC.Add(i - 1); - break; - } - } + var inChars = value.ToCharArray(); + var uCWithAnyLC = new List(); + var i = 0; + while (i < inChars.Length && char.IsUpper(inChars[i])) { ++i; } - var outChars = new char[inChars.Length + uCWithAnyLC.Count]; - var lastIndex = 0; - for (i = 0; i < uCWithAnyLC.Count; i++) - { - var currentIndex = uCWithAnyLC[i]; - Array.Copy(inChars, lastIndex, outChars, lastIndex + i, currentIndex - lastIndex); - outChars[currentIndex + i] = ' '; - lastIndex = currentIndex; + for (; i < inChars.Length; i++) + { + if (!char.IsUpper(inChars[i])) continue; + uCWithAnyLC.Add(i); + if (++i >= inChars.Length || !char.IsUpper(inChars[i])) continue; + while (++i < inChars.Length) + { + if (char.IsUpper(inChars[i])) continue; + uCWithAnyLC.Add(i - 1); + break; } + } - var lastPos = lastIndex + uCWithAnyLC.Count; - Array.Copy(inChars, lastIndex, outChars, lastPos, outChars.Length - lastPos); - return new string(outChars); + var outChars = new char[inChars.Length + uCWithAnyLC.Count]; + var lastIndex = 0; + for (i = 0; i < uCWithAnyLC.Count; i++) + { + var currentIndex = uCWithAnyLC[i]; + Array.Copy(inChars, lastIndex, outChars, lastIndex + i, currentIndex - lastIndex); + outChars[currentIndex + i] = ' '; + lastIndex = currentIndex; } - [return: NotNullIfNotNull("value")] - private static string? ToTitleCase(string? value) => value is null ? null : CultureInfo.InvariantCulture.TextInfo.ToTitleCase(value); + var lastPos = lastIndex + uCWithAnyLC.Count; + Array.Copy(inChars, lastIndex, outChars, lastPos, outChars.Length - lastPos); + return new string(outChars); } + + [return: NotNullIfNotNull("value")] + private static string? ToTitleCase(string? value) => value is null ? null : CultureInfo.InvariantCulture.TextInfo.ToTitleCase(value); } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherSaveVM.cs b/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherSaveVM.cs index 4cec560..5b058d0 100644 --- a/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherSaveVM.cs +++ b/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherSaveVM.cs @@ -15,173 +15,172 @@ using ApplicationVersion = Bannerlord.ModuleManager.ApplicationVersion; -namespace Bannerlord.LauncherEx.ViewModels +namespace Bannerlord.LauncherEx.ViewModels; + +internal sealed class BUTRLauncherSaveVM : BUTRViewModel { - internal sealed class BUTRLauncherSaveVM : BUTRViewModel - { - private record ModuleListEntry(string Name, ApplicationVersion Version); + private record ModuleListEntry(string Name, ApplicationVersion Version); - [BUTRDataSourceProperty] - public string Name { get => _name; set => SetField(ref _name, value); } - private string _name; + [BUTRDataSourceProperty] + public string Name { get => _name; set => SetField(ref _name, value); } + private string _name; - [BUTRDataSourceProperty] - public string Version { get => _version; set => SetField(ref _version, value); } - private string _version; + [BUTRDataSourceProperty] + public string Version { get => _version; set => SetField(ref _version, value); } + private string _version; - [BUTRDataSourceProperty] - public string CharacterName { get => _characterName; set => SetField(ref _characterName, value); } - private string _characterName; + [BUTRDataSourceProperty] + public string CharacterName { get => _characterName; set => SetField(ref _characterName, value); } + private string _characterName; - [BUTRDataSourceProperty] - public string Level { get => _level; set => SetField(ref _level, value); } - private string _level; + [BUTRDataSourceProperty] + public string Level { get => _level; set => SetField(ref _level, value); } + private string _level; - [BUTRDataSourceProperty] - public string Days { get => _days; set => SetField(ref _days, value); } - private string _days; + [BUTRDataSourceProperty] + public string Days { get => _days; set => SetField(ref _days, value); } + private string _days; - [BUTRDataSourceProperty] - public string CreatedAt { get => _createdAt; set => SetField(ref _createdAt, value); } - private string _createdAt; + [BUTRDataSourceProperty] + public string CreatedAt { get => _createdAt; set => SetField(ref _createdAt, value); } + private string _createdAt; - [BUTRDataSourceProperty] - public bool IsSelected { get => _isSelected; set => SetField(ref _isSelected, value); } - private bool _isSelected; + [BUTRDataSourceProperty] + public bool IsSelected { get => _isSelected; set => SetField(ref _isSelected, value); } + private bool _isSelected; - [BUTRDataSourceProperty] - public LauncherHintVM? LoadOrderHint { get => _loadOrderHint; set => SetField(ref _loadOrderHint, value); } - private LauncherHintVM? _loadOrderHint; + [BUTRDataSourceProperty] + public LauncherHintVM? LoadOrderHint { get => _loadOrderHint; set => SetField(ref _loadOrderHint, value); } + private LauncherHintVM? _loadOrderHint; - [BUTRDataSourceProperty] - public bool HasWarning { get => _hasWarning; set => SetField(ref _hasWarning, value); } - private bool _hasWarning; + [BUTRDataSourceProperty] + public bool HasWarning { get => _hasWarning; set => SetField(ref _hasWarning, value); } + private bool _hasWarning; - [BUTRDataSourceProperty] - public LauncherHintVM? WarningHint { get => _warningHint; set => SetField(ref _warningHint, value); } - private LauncherHintVM? _warningHint; + [BUTRDataSourceProperty] + public LauncherHintVM? WarningHint { get => _warningHint; set => SetField(ref _warningHint, value); } + private LauncherHintVM? _warningHint; - [BUTRDataSourceProperty] - public bool HasError { get => _hasError; set => SetField(ref _hasError, value); } - private bool _hasError; + [BUTRDataSourceProperty] + public bool HasError { get => _hasError; set => SetField(ref _hasError, value); } + private bool _hasError; - [BUTRDataSourceProperty] - public LauncherHintVM? ErrorHint { get => _errorHint; set => SetField(ref _errorHint, value); } - private LauncherHintVM? _errorHint; + [BUTRDataSourceProperty] + public LauncherHintVM? ErrorHint { get => _errorHint; set => SetField(ref _errorHint, value); } + private LauncherHintVM? _errorHint; - [BUTRDataSourceProperty] - public bool IsVisible { get => _isVisible; set => SetField(ref _isVisible, value); } - private bool _isVisible = true; + [BUTRDataSourceProperty] + public bool IsVisible { get => _isVisible; set => SetField(ref _isVisible, value); } + private bool _isVisible = true; - public string? ModuleListCode { get; private set; } + public string? ModuleListCode { get; private set; } - private readonly LauncherManagerHandler _launcherManagerHandler = BUTRLauncherManagerHandler.Default; - private readonly SaveMetadata _saveMetadata; - private readonly Action _select; - private readonly Func _getModuleById; - private readonly Func _getModuleByName; + private readonly LauncherManagerHandler _launcherManagerHandler = BUTRLauncherManagerHandler.Default; + private readonly SaveMetadata _saveMetadata; + private readonly Action _select; + private readonly Func _getModuleById; + private readonly Func _getModuleByName; - public BUTRLauncherSaveVM(SaveMetadata saveMetadata, Action select, Func getModuleById, Func getModuleByName) - { - _saveMetadata = saveMetadata; - _select = select; - _getModuleById = getModuleById; - _getModuleByName = getModuleByName; - - _name = _saveMetadata.Name; - _version = _saveMetadata.TryGetValue("ApplicationVersion", out var appVersion) && !string.IsNullOrEmpty(appVersion) ? string.Join(".", appVersion.Split('.').Take(3)) : "Save Old"; - _characterName = _saveMetadata.TryGetValue("CharacterName", out var characterName) && !string.IsNullOrEmpty(characterName) ? characterName : "Save Old"; - _level = _saveMetadata.TryGetValue("MainHeroLevel", out var level) && !string.IsNullOrEmpty(level) ? level : "Save Old"; - _days = _saveMetadata.TryGetValue("DayLong", out var daysr) && !string.IsNullOrEmpty(daysr) && float.TryParse(daysr, out var days) ? days.ToString("0") : "Save Old"; - _createdAt = _saveMetadata.TryGetValue("CreationTime", out var ctr) && !string.IsNullOrEmpty(ctr) && long.TryParse(ctr, out var ticks) ? new DateTime(ticks).ToString("d") : "Save Old"; - - ValidateSave(); - } + public BUTRLauncherSaveVM(SaveMetadata saveMetadata, Action select, Func getModuleById, Func getModuleByName) + { + _saveMetadata = saveMetadata; + _select = select; + _getModuleById = getModuleById; + _getModuleByName = getModuleByName; + + _name = _saveMetadata.Name; + _version = _saveMetadata.TryGetValue("ApplicationVersion", out var appVersion) && !string.IsNullOrEmpty(appVersion) ? string.Join(".", appVersion.Split('.').Take(3)) : "Save Old"; + _characterName = _saveMetadata.TryGetValue("CharacterName", out var characterName) && !string.IsNullOrEmpty(characterName) ? characterName : "Save Old"; + _level = _saveMetadata.TryGetValue("MainHeroLevel", out var level) && !string.IsNullOrEmpty(level) ? level : "Save Old"; + _days = _saveMetadata.TryGetValue("DayLong", out var daysr) && !string.IsNullOrEmpty(daysr) && float.TryParse(daysr, out var days) ? days.ToString("0") : "Save Old"; + _createdAt = _saveMetadata.TryGetValue("CreationTime", out var ctr) && !string.IsNullOrEmpty(ctr) && long.TryParse(ctr, out var ticks) ? new DateTime(ticks).ToString("d") : "Save Old"; + + ValidateSave(); + } - private void ValidateSave() + private void ValidateSave() + { + var changeset = _saveMetadata.GetChangeSet(); + var modules = _saveMetadata.GetModules().Select(x => { - var changeset = _saveMetadata.GetChangeSet(); - var modules = _saveMetadata.GetModules().Select(x => - { - var version = _saveMetadata.GetModuleVersion(x); - if (version.ChangeSet == changeset) - version = new ApplicationVersion(version.ApplicationVersionType, version.Major, version.Minor, version.Revision, 0); - return new ModuleListEntry(x, version); - }).ToArray(); - - var existingModules = modules.Select(x => _getModuleByName(x.Name)).OfType().ToArray(); - var nameDuplicates = existingModules.Select(x => x.Name).GroupBy(i => i).Where(g => g.Count() > 1).Select(g => g.Key).ToList(); - if (nameDuplicates.Count > 0) - { - HasError = true; - ErrorHint = new LauncherHintVM(new BUTRTextObject("{=vCwH9226}Duplicate Module Names:{NL}{MODULENAMES}") - .SetTextVariable("MODULENAMES", string.Join("\n", nameDuplicates)).ToString()); - return; - } - var existingModulesByName = existingModules.ToDictionary(x => x.Name, x => x); + var version = _saveMetadata.GetModuleVersion(x); + if (version.ChangeSet == changeset) + version = new ApplicationVersion(version.ApplicationVersionType, version.Major, version.Minor, version.Revision, 0); + return new ModuleListEntry(x, version); + }).ToArray(); + + var existingModules = modules.Select(x => _getModuleByName(x.Name)).OfType().ToArray(); + var nameDuplicates = existingModules.Select(x => x.Name).GroupBy(i => i).Where(g => g.Count() > 1).Select(g => g.Key).ToList(); + if (nameDuplicates.Count > 0) + { + HasError = true; + ErrorHint = new LauncherHintVM(new BUTRTextObject("{=vCwH9226}Duplicate Module Names:{NL}{MODULENAMES}") + .SetTextVariable("MODULENAMES", string.Join("\n", nameDuplicates)).ToString()); + return; + } + var existingModulesByName = existingModules.ToDictionary(x => x.Name, x => x); - ModuleListCode = $"_MODULES_*{string.Join("*", existingModules.Select(x => x.Id))}*_MODULES_"; + ModuleListCode = $"_MODULES_*{string.Join("*", existingModules.Select(x => x.Id))}*_MODULES_"; - var missingNames = modules.Select(x => x.Name).Except(existingModulesByName.Keys).ToArray(); - var loadOrderIssues = LoadOrderChecker.IsLoadOrderCorrect(existingModules).ToList(); + var missingNames = modules.Select(x => x.Name).Except(existingModulesByName.Keys).ToArray(); + var loadOrderIssues = LoadOrderChecker.IsLoadOrderCorrect(existingModules).ToList(); - //LoadOrderHint = new LauncherHintVM($"Load Order:\n{string.Join("\n", existingModules.Select(x => x.Id))}\n\nUnknown Mod Names:{string.Join("\n", missingNames)}"); - LoadOrderHint = new LauncherHintVM(new BUTRTextObject("{=sd6M4KRd}Load Order:{NL}{LOADORDER}") - .SetTextVariable("LOADORDER", string.Join("\n", modules.Select(x => _getModuleByName(x.Name)?.Id ?? $"{x.Name} {new BUTRTextObject("{=kxqLbSqe}(Unknown ID)")}"))).ToString()); + //LoadOrderHint = new LauncherHintVM($"Load Order:\n{string.Join("\n", existingModules.Select(x => x.Id))}\n\nUnknown Mod Names:{string.Join("\n", missingNames)}"); + LoadOrderHint = new LauncherHintVM(new BUTRTextObject("{=sd6M4KRd}Load Order:{NL}{LOADORDER}") + .SetTextVariable("LOADORDER", string.Join("\n", modules.Select(x => _getModuleByName(x.Name)?.Id ?? $"{x.Name} {new BUTRTextObject("{=kxqLbSqe}(Unknown ID)")}"))).ToString()); - if (missingNames.Length > 0 || loadOrderIssues.Count > 0) + if (missingNames.Length > 0 || loadOrderIssues.Count > 0) + { + var text = string.Empty; + if (loadOrderIssues.Count > 0) { - var text = string.Empty; - if (loadOrderIssues.Count > 0) - { - text += new BUTRTextObject("{=HvvA78sZ}Load Order Issues:{NL}{LOADORDERISSUES}") - .SetTextVariable("LOADORDERISSUES", string.Join("\n\n", loadOrderIssues)); - text += missingNames.Length > 0 ? "\n\n\n" : string.Empty; - } - if (missingNames.Length > 0) - { - text += new BUTRTextObject("{=GtDRbC3m}Missing Modules:{NL}{MODULES}") - .SetTextVariable("MODULES", string.Join("\n", missingNames)).ToString(); - } - - HasError = true; - ErrorHint = new LauncherHintVM(text); - return; + text += new BUTRTextObject("{=HvvA78sZ}Load Order Issues:{NL}{LOADORDERISSUES}") + .SetTextVariable("LOADORDERISSUES", string.Join("\n\n", loadOrderIssues)); + text += missingNames.Length > 0 ? "\n\n\n" : string.Empty; } - - var issues = new List(); - foreach (var module in modules) + if (missingNames.Length > 0) { - var existingModule = existingModulesByName[module.Name]; - if (module.Version != existingModule.Version) - { - issues.Add(new BUTRTextObject("{=nYVWoomO}{MODULEID}. Required {REQUIREDVERSION}. Actual {ACTUALVERSION}") - .SetTextVariable("MODULEID", existingModule.Id) - .SetTextVariable("REQUIREDVERSION", module.Version.ToString()) - .SetTextVariable("ACTUALVERSION", existingModule.Version.ToString()).ToString()); - } + text += new BUTRTextObject("{=GtDRbC3m}Missing Modules:{NL}{MODULES}") + .SetTextVariable("MODULES", string.Join("\n", missingNames)).ToString(); } - if (issues.Count > 0) + + HasError = true; + ErrorHint = new LauncherHintVM(text); + return; + } + + var issues = new List(); + foreach (var module in modules) + { + var existingModule = existingModulesByName[module.Name]; + if (module.Version != existingModule.Version) { - HasWarning = true; - WarningHint = new LauncherHintVM(new BUTRTextObject("{=BuMom4Jt}Mismatched Module Versions:{NL}{MODULEVERSIONS}") - .SetTextVariable("MODULEVERSIONS", string.Join("\n\n", issues)).ToString()); + issues.Add(new BUTRTextObject("{=nYVWoomO}{MODULEID}. Required {REQUIREDVERSION}. Actual {ACTUALVERSION}") + .SetTextVariable("MODULEID", existingModule.Id) + .SetTextVariable("REQUIREDVERSION", module.Version.ToString()) + .SetTextVariable("ACTUALVERSION", existingModule.Version.ToString()).ToString()); } } - - [BUTRDataSourceMethod] - public void ExecuteSelect() + if (issues.Count > 0) { - _select(this); + HasWarning = true; + WarningHint = new LauncherHintVM(new BUTRTextObject("{=BuMom4Jt}Mismatched Module Versions:{NL}{MODULEVERSIONS}") + .SetTextVariable("MODULEVERSIONS", string.Join("\n\n", issues)).ToString()); } + } - [BUTRDataSourceMethod] - public void ExecuteOpen() - { - var saveFilePath = _launcherManagerHandler.GetSaveFilePath(_saveMetadata.Name); - if (string.IsNullOrEmpty(saveFilePath) || !File.Exists(saveFilePath)) return; + [BUTRDataSourceMethod] + public void ExecuteSelect() + { + _select(this); + } - Process.Start("explorer.exe", $"/select,\"{saveFilePath}\""); - } + [BUTRDataSourceMethod] + public void ExecuteOpen() + { + var saveFilePath = _launcherManagerHandler.GetSaveFilePath(_saveMetadata.Name); + if (string.IsNullOrEmpty(saveFilePath) || !File.Exists(saveFilePath)) return; + + Process.Start("explorer.exe", $"/select,\"{saveFilePath}\""); } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherSavesVM.cs b/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherSavesVM.cs index f5d5294..88456d0 100644 --- a/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherSavesVM.cs +++ b/src/Bannerlord.LauncherEx/ViewModels/BUTRLauncherSavesVM.cs @@ -8,106 +8,105 @@ using TaleWorlds.Library; -namespace Bannerlord.LauncherEx.ViewModels +namespace Bannerlord.LauncherEx.ViewModels; + +internal sealed class BUTRLauncherSavesVM : BUTRViewModel { - internal sealed class BUTRLauncherSavesVM : BUTRViewModel - { - private readonly LauncherManagerHandler _launcherManagerHandler = BUTRLauncherManagerHandler.Default; - private readonly Func _getModuleById; - private readonly Func _getModuleByName; + private readonly LauncherManagerHandler _launcherManagerHandler = BUTRLauncherManagerHandler.Default; + private readonly Func _getModuleById; + private readonly Func _getModuleByName; - [BUTRDataSourceProperty] - public string NameCategoryText { get => _nameCategoryText; set => SetField(ref _nameCategoryText, value); } - private string _nameCategoryText = new BUTRTextObject("{=JtelOsIW}Name").ToString(); + [BUTRDataSourceProperty] + public string NameCategoryText { get => _nameCategoryText; set => SetField(ref _nameCategoryText, value); } + private string _nameCategoryText = new BUTRTextObject("{=JtelOsIW}Name").ToString(); - [BUTRDataSourceProperty] - public string VersionCategoryText { get => _versionCategoryText; set => SetField(ref _versionCategoryText, value); } - private string _versionCategoryText = new BUTRTextObject("{=14WBFIS1}Version").ToString(); + [BUTRDataSourceProperty] + public string VersionCategoryText { get => _versionCategoryText; set => SetField(ref _versionCategoryText, value); } + private string _versionCategoryText = new BUTRTextObject("{=14WBFIS1}Version").ToString(); - [BUTRDataSourceProperty] - public string CharacterNameCategoryText { get => _characterNameCategoryText; set => SetField(ref _characterNameCategoryText, value); } - private string _characterNameCategoryText = new BUTRTextObject("{=OJsGrGVi}Character").ToString(); + [BUTRDataSourceProperty] + public string CharacterNameCategoryText { get => _characterNameCategoryText; set => SetField(ref _characterNameCategoryText, value); } + private string _characterNameCategoryText = new BUTRTextObject("{=OJsGrGVi}Character").ToString(); - [BUTRDataSourceProperty] - public string LevelCategoryText { get => _levelCategoryText; set => SetField(ref _levelCategoryText, value); } - private string _levelCategoryText = new BUTRTextObject("{=JxpEEQdF}Level").ToString(); + [BUTRDataSourceProperty] + public string LevelCategoryText { get => _levelCategoryText; set => SetField(ref _levelCategoryText, value); } + private string _levelCategoryText = new BUTRTextObject("{=JxpEEQdF}Level").ToString(); - [BUTRDataSourceProperty] - public string DaysCategoryText { get => _daysCategoryText; set => SetField(ref _daysCategoryText, value); } - private string _daysCategoryText = new BUTRTextObject("{=qkkTPycE}Days").ToString(); + [BUTRDataSourceProperty] + public string DaysCategoryText { get => _daysCategoryText; set => SetField(ref _daysCategoryText, value); } + private string _daysCategoryText = new BUTRTextObject("{=qkkTPycE}Days").ToString(); - [BUTRDataSourceProperty] - public string CreatedAtCategoryText { get => _createdAtCategoryText; set => SetField(ref _createdAtCategoryText, value); } - private string _createdAtCategoryText = new BUTRTextObject("{=aYWWDkKX}CreatedAt").ToString(); + [BUTRDataSourceProperty] + public string CreatedAtCategoryText { get => _createdAtCategoryText; set => SetField(ref _createdAtCategoryText, value); } + private string _createdAtCategoryText = new BUTRTextObject("{=aYWWDkKX}CreatedAt").ToString(); - [BUTRDataSourceProperty] - public MBBindingList Saves { get; } = new(); + [BUTRDataSourceProperty] + public MBBindingList Saves { get; } = new(); - [BUTRDataSourceProperty] - public bool IsDisabled { get => _isDisabled; set => SetField(ref _isDisabled, value); } - private bool _isDisabled; + [BUTRDataSourceProperty] + public bool IsDisabled { get => _isDisabled; set => SetField(ref _isDisabled, value); } + private bool _isDisabled; - [BUTRDataSourceProperty] - public string SearchText + [BUTRDataSourceProperty] + public string SearchText + { + get => _searchText; + set { - get => _searchText; - set + if (SetField(ref _searchText, value)) { - if (SetField(ref _searchText, value)) - { - SearchTextChanged(); - } + SearchTextChanged(); } } - private string _searchText = string.Empty; + } + private string _searchText = string.Empty; - public BUTRLauncherSaveVM? Selected => Saves.FirstOrDefault(x => x.IsSelected); + public BUTRLauncherSaveVM? Selected => Saves.FirstOrDefault(x => x.IsSelected); - public BUTRLauncherSavesVM(Func getModuleById, Func getModuleByName) - { - _getModuleById = getModuleById; - _getModuleByName = getModuleByName; + public BUTRLauncherSavesVM(Func getModuleById, Func getModuleByName) + { + _getModuleById = getModuleById; + _getModuleByName = getModuleByName; - ExecuteRefresh(); - } + ExecuteRefresh(); + } - private void SelectSave(BUTRLauncherSaveVM saveVM) + private void SelectSave(BUTRLauncherSaveVM saveVM) + { + var previousState = saveVM.IsSelected; + foreach (var save in Saves) { - var previousState = saveVM.IsSelected; - foreach (var save in Saves) - { - save.IsSelected = false; - } - saveVM.IsSelected = !previousState; - OnPropertyChanged("SaveSelected"); + save.IsSelected = false; } + saveVM.IsSelected = !previousState; + OnPropertyChanged("SaveSelected"); + } - private void SearchTextChanged() + private void SearchTextChanged() + { + var searchText = SearchText; + if (string.IsNullOrEmpty(searchText)) { - var searchText = SearchText; - if (string.IsNullOrEmpty(searchText)) - { - foreach (var saveVM in Saves) - { - saveVM.IsVisible = true; - } - return; - } - foreach (var saveVM in Saves) { - saveVM.IsVisible = saveVM.Name.IndexOf(SearchText, StringComparison.OrdinalIgnoreCase) != -1; + saveVM.IsVisible = true; } + return; } - [BUTRDataSourceMethod] - public void ExecuteRefresh() + foreach (var saveVM in Saves) { - Saves.Clear(); - foreach (var saveFile in _launcherManagerHandler.GetSaveFiles()) - { - Saves.Add(new BUTRLauncherSaveVM(saveFile, SelectSave, _getModuleById, _getModuleByName)); - } + saveVM.IsVisible = saveVM.Name.IndexOf(SearchText, StringComparison.OrdinalIgnoreCase) != -1; + } + } + + [BUTRDataSourceMethod] + public void ExecuteRefresh() + { + Saves.Clear(); + foreach (var saveFile in _launcherManagerHandler.GetSaveFiles()) + { + Saves.Add(new BUTRLauncherSaveVM(saveFile, SelectSave, _getModuleById, _getModuleByName)); } } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/ViewModels/SettingsPropertyVM.cs b/src/Bannerlord.LauncherEx/ViewModels/SettingsPropertyVM.cs index 8260d72..6e89be5 100644 --- a/src/Bannerlord.LauncherEx/ViewModels/SettingsPropertyVM.cs +++ b/src/Bannerlord.LauncherEx/ViewModels/SettingsPropertyVM.cs @@ -3,191 +3,190 @@ using System.ComponentModel; using System.Globalization; -namespace Bannerlord.LauncherEx.ViewModels +namespace Bannerlord.LauncherEx.ViewModels; + +internal sealed class SettingsPropertyVM : BUTRViewModel { - internal sealed class SettingsPropertyVM : BUTRViewModel + public ISettingsPropertyDefinition SettingPropertyDefinition { get; } + private IRef PropertyReference => SettingPropertyDefinition.PropertyReference; + private SettingType SettingType => SettingPropertyDefinition.SettingType; + + [BUTRDataSourceProperty] + public string Name => SettingPropertyDefinition.DisplayName; + + [BUTRDataSourceProperty] + public string HintText => SettingPropertyDefinition.HintText.Length > 0 ? $"{Name}: {SettingPropertyDefinition.HintText}" : string.Empty; + + [BUTRDataSourceProperty] + public bool IsIntVisible { get; } + [BUTRDataSourceProperty] + public bool IsFloatVisible { get; } + [BUTRDataSourceProperty] + public bool IsBoolVisible { get; } + [BUTRDataSourceProperty] + public bool IsStringVisible { get; } + [BUTRDataSourceProperty] + public bool IsButtonVisible { get; } + + [BUTRDataSourceProperty] + public float FloatValue { - public ISettingsPropertyDefinition SettingPropertyDefinition { get; } - private IRef PropertyReference => SettingPropertyDefinition.PropertyReference; - private SettingType SettingType => SettingPropertyDefinition.SettingType; - - [BUTRDataSourceProperty] - public string Name => SettingPropertyDefinition.DisplayName; - - [BUTRDataSourceProperty] - public string HintText => SettingPropertyDefinition.HintText.Length > 0 ? $"{Name}: {SettingPropertyDefinition.HintText}" : string.Empty; - - [BUTRDataSourceProperty] - public bool IsIntVisible { get; } - [BUTRDataSourceProperty] - public bool IsFloatVisible { get; } - [BUTRDataSourceProperty] - public bool IsBoolVisible { get; } - [BUTRDataSourceProperty] - public bool IsStringVisible { get; } - [BUTRDataSourceProperty] - public bool IsButtonVisible { get; } - - [BUTRDataSourceProperty] - public float FloatValue + get => IsFloatVisible ? PropertyReference.Value is float val ? val : float.MinValue : 0f; + set { - get => IsFloatVisible ? PropertyReference.Value is float val ? val : float.MinValue : 0f; - set + if (IsFloatVisible && FloatValue != value) { - if (IsFloatVisible && FloatValue != value) - { - PropertyReference.Value = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(TextBoxValue)); - } + PropertyReference.Value = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(TextBoxValue)); } } - [BUTRDataSourceProperty] - public int IntValue + } + [BUTRDataSourceProperty] + public int IntValue + { + get => IsIntVisible ? PropertyReference.Value is int val ? val : int.MinValue : 0; + set { - get => IsIntVisible ? PropertyReference.Value is int val ? val : int.MinValue : 0; - set + if (IsIntVisible && IntValue != value) { - if (IsIntVisible && IntValue != value) - { - PropertyReference.Value = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(TextBoxValue)); - } + PropertyReference.Value = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(TextBoxValue)); } } - [BUTRDataSourceProperty] - public bool BoolValue + } + [BUTRDataSourceProperty] + public bool BoolValue + { + get => IsBoolVisible && PropertyReference.Value is bool val ? val : false; + set { - get => IsBoolVisible && PropertyReference.Value is bool val ? val : false; - set + if (IsBoolVisible && BoolValue != value) { - if (IsBoolVisible && BoolValue != value) - { - PropertyReference.Value = value; - OnPropertyChanged(); - } + PropertyReference.Value = value; + OnPropertyChanged(); } } - [BUTRDataSourceProperty] - public string StringValue + } + [BUTRDataSourceProperty] + public string StringValue + { + get => IsStringVisible ? PropertyReference.Value is string val ? val : "ERROR" : string.Empty; + set { - get => IsStringVisible ? PropertyReference.Value is string val ? val : "ERROR" : string.Empty; - set + if (IsStringVisible && StringValue != value) { - if (IsStringVisible && StringValue != value) - { - PropertyReference.Value = value; - OnPropertyChanged(); - } + PropertyReference.Value = value; + OnPropertyChanged(); } } - [BUTRDataSourceProperty] - public string ButtonValue + } + [BUTRDataSourceProperty] + public string ButtonValue + { + get => IsButtonVisible ? PropertyReference.Value is string val ? val : "ERROR" : string.Empty; + set { - get => IsButtonVisible ? PropertyReference.Value is string val ? val : "ERROR" : string.Empty; - set + if (IsButtonVisible && ButtonValue != value) { - if (IsButtonVisible && ButtonValue != value) - { - PropertyReference.Value = value; - OnPropertyChanged(); - } + PropertyReference.Value = value; + OnPropertyChanged(); } } + } - [BUTRDataSourceProperty] - public float MaxValue => (float) SettingPropertyDefinition.MaxValue; - [BUTRDataSourceProperty] - public float MinValue => (float) SettingPropertyDefinition.MinValue; - [BUTRDataSourceProperty] - public string TextBoxValue => SettingType switch - { - SettingType.Int when PropertyReference.Value is int val => val.ToString(), - SettingType.Float when PropertyReference.Value is float val => val.ToString("0.0000", CultureInfo.InvariantCulture), - _ => string.Empty - }; + [BUTRDataSourceProperty] + public float MaxValue => (float) SettingPropertyDefinition.MaxValue; + [BUTRDataSourceProperty] + public float MinValue => (float) SettingPropertyDefinition.MinValue; + [BUTRDataSourceProperty] + public string TextBoxValue => SettingType switch + { + SettingType.Int when PropertyReference.Value is int val => val.ToString(), + SettingType.Float when PropertyReference.Value is float val => val.ToString("0.0000", CultureInfo.InvariantCulture), + _ => string.Empty + }; - public string ValueAsString => SettingType switch - { - SettingType.Int when PropertyReference.Value is int val => val.ToString(), - SettingType.Float when PropertyReference.Value is float val => val.ToString("0.0000", CultureInfo.InvariantCulture), - SettingType.String when PropertyReference.Value is string val => val, - SettingType.Bool when PropertyReference.Value is bool val => val ? "True" : "False", - SettingType.Button when PropertyReference.Value is string val => val, - _ => string.Empty - }; - - public SettingsPropertyVM(ISettingsPropertyDefinition definition) - { - SettingPropertyDefinition = definition; + public string ValueAsString => SettingType switch + { + SettingType.Int when PropertyReference.Value is int val => val.ToString(), + SettingType.Float when PropertyReference.Value is float val => val.ToString("0.0000", CultureInfo.InvariantCulture), + SettingType.String when PropertyReference.Value is string val => val, + SettingType.Bool when PropertyReference.Value is bool val => val ? "True" : "False", + SettingType.Button when PropertyReference.Value is string val => val, + _ => string.Empty + }; + + public SettingsPropertyVM(ISettingsPropertyDefinition definition) + { + SettingPropertyDefinition = definition; - // Moved to constructor - IsIntVisible = SettingType == SettingType.Int; - IsFloatVisible = SettingType == SettingType.Float; - IsBoolVisible = SettingType == SettingType.Bool; - IsStringVisible = SettingType == SettingType.String; - IsButtonVisible = SettingType == SettingType.Button; - // Moved to constructor + // Moved to constructor + IsIntVisible = SettingType == SettingType.Int; + IsFloatVisible = SettingType == SettingType.Float; + IsBoolVisible = SettingType == SettingType.Bool; + IsStringVisible = SettingType == SettingType.String; + IsButtonVisible = SettingType == SettingType.Button; + // Moved to constructor - PropertyReference.PropertyChanged += PropertyReference_OnPropertyChanged; + PropertyReference.PropertyChanged += PropertyReference_OnPropertyChanged; - RefreshValues(); - } + RefreshValues(); + } - public override void OnFinalize() - { - PropertyReference.PropertyChanged -= PropertyReference_OnPropertyChanged; + public override void OnFinalize() + { + PropertyReference.PropertyChanged -= PropertyReference_OnPropertyChanged; - base.OnFinalize(); - } + base.OnFinalize(); + } - private void PropertyReference_OnPropertyChanged(object? obj, PropertyChangedEventArgs args) - { - RefreshValues(); - } + private void PropertyReference_OnPropertyChanged(object? obj, PropertyChangedEventArgs args) + { + RefreshValues(); + } - public override void RefreshValues() - { - base.RefreshValues(); + public override void RefreshValues() + { + base.RefreshValues(); - switch (SettingType) - { - case SettingType.Bool: - OnPropertyChanged(nameof(BoolValue)); - break; - case SettingType.Int: - OnPropertyChanged(nameof(IntValue)); - break; - case SettingType.Float: - OnPropertyChanged(nameof(FloatValue)); - break; - case SettingType.String: - OnPropertyChanged(nameof(StringValue)); - break; - } - OnPropertyChanged(nameof(TextBoxValue)); + switch (SettingType) + { + case SettingType.Bool: + OnPropertyChanged(nameof(BoolValue)); + break; + case SettingType.Int: + OnPropertyChanged(nameof(IntValue)); + break; + case SettingType.Float: + OnPropertyChanged(nameof(FloatValue)); + break; + case SettingType.String: + OnPropertyChanged(nameof(StringValue)); + break; } + OnPropertyChanged(nameof(TextBoxValue)); + } - public override string ToString() => Name; + public override string ToString() => Name; - [BUTRDataSourceMethod] - public void OnHover() - { - if (!string.IsNullOrEmpty(HintText)) - HintManager.ShowHint(HintText); - } + [BUTRDataSourceMethod] + public void OnHover() + { + if (!string.IsNullOrEmpty(HintText)) + HintManager.ShowHint(HintText); + } - [BUTRDataSourceMethod] - public void OnHoverEnd() - { - HintManager.HideHint(); - } + [BUTRDataSourceMethod] + public void OnHoverEnd() + { + HintManager.HideHint(); + } - [BUTRDataSourceMethod] - public void OnValueClick() - { - PropertyReference.Value = PropertyReference.Value; - } + [BUTRDataSourceMethod] + public void OnValueClick() + { + PropertyReference.Value = PropertyReference.Value; } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Widgets/LauncherSearchBoxWidget.cs b/src/Bannerlord.LauncherEx/Widgets/LauncherSearchBoxWidget.cs index e599333..f6b0121 100644 --- a/src/Bannerlord.LauncherEx/Widgets/LauncherSearchBoxWidget.cs +++ b/src/Bannerlord.LauncherEx/Widgets/LauncherSearchBoxWidget.cs @@ -3,47 +3,46 @@ using TaleWorlds.GauntletUI; using TaleWorlds.GauntletUI.BaseTypes; -namespace Bannerlord.LauncherEx.Widgets +namespace Bannerlord.LauncherEx.Widgets; + +internal sealed class LauncherSearchBoxWidget : BrushWidget { - internal sealed class LauncherSearchBoxWidget : BrushWidget + public EditableTextWidget? EditableTextWidget { - public EditableTextWidget? EditableTextWidget + get => _editableTextWidget; + set { - get => _editableTextWidget; - set + if (this.SetField(ref _editableTextWidget, value)) { - if (this.SetField(ref _editableTextWidget, value)) - { - if (_editableTextWidget is not null) - _editableTextWidget.PropertyChanged -= SearchTextOnPropertyChanged; - - if (value is not null) - value.PropertyChanged += SearchTextOnPropertyChanged; - } + if (_editableTextWidget is not null) + _editableTextWidget.PropertyChanged -= SearchTextOnPropertyChanged; + + if (value is not null) + value.PropertyChanged += SearchTextOnPropertyChanged; } } + } - private void SearchTextOnPropertyChanged(PropertyOwnerObject propertyOwnerObject, string propertyName, object value) - { - if (propertyName == nameof(TaleWorlds.GauntletUI.BaseTypes.EditableTextWidget.RealText) && value is string searchText) - SearchText = searchText; - } + private void SearchTextOnPropertyChanged(PropertyOwnerObject propertyOwnerObject, string propertyName, object value) + { + if (propertyName == nameof(TaleWorlds.GauntletUI.BaseTypes.EditableTextWidget.RealText) && value is string searchText) + SearchText = searchText; + } - private EditableTextWidget? _editableTextWidget; + private EditableTextWidget? _editableTextWidget; - public string? SearchText + public string? SearchText + { + get => _searchText; + set { - get => _searchText; - set + if (this.SetField(ref _searchText, value) && EditableTextWidget is not null) { - if (this.SetField(ref _searchText, value) && EditableTextWidget is not null) - { - EditableTextWidget.RealText = value; - } + EditableTextWidget.RealText = value; } } - private string? _searchText; - - public LauncherSearchBoxWidget(UIContext context) : base(context) { } } + private string? _searchText; + + public LauncherSearchBoxWidget(UIContext context) : base(context) { } } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Widgets/LauncherToggleButtonWidget.cs b/src/Bannerlord.LauncherEx/Widgets/LauncherToggleButtonWidget.cs index f7bced4..f61689d 100644 --- a/src/Bannerlord.LauncherEx/Widgets/LauncherToggleButtonWidget.cs +++ b/src/Bannerlord.LauncherEx/Widgets/LauncherToggleButtonWidget.cs @@ -6,240 +6,239 @@ using TaleWorlds.GauntletUI; using TaleWorlds.GauntletUI.BaseTypes; -namespace Bannerlord.LauncherEx.Widgets +namespace Bannerlord.LauncherEx.Widgets; + +internal sealed class LauncherToggleButtonWidget : ImageWidget { - internal sealed class LauncherToggleButtonWidget : ImageWidget + private enum ButtonClickState { - private enum ButtonClickState - { - None, - HandlingClick, - HandlingAlternateClick - } + None, + HandlingClick, + HandlingAlternateClick + } - //[Editor] - public Widget? ToggleIndicator + //[Editor] + public Widget? ToggleIndicator + { + get => _toggleIndicator; + set { - get => _toggleIndicator; - set + if (_toggleIndicator != value) { - if (_toggleIndicator != value) - { - _toggleIndicator = value; - Refresh(); - } + _toggleIndicator = value; + Refresh(); } } - private Widget? _toggleIndicator; + } + private Widget? _toggleIndicator; - //[Editor] - public bool IsSelected + //[Editor] + public bool IsSelected + { + get => _isSelected; + set { - get => _isSelected; - set + if (_isSelected != value) { - if (_isSelected != value) - { - _isSelected = value; - Refresh(); - RefreshState(); - OnPropertyChanged(value); - } + _isSelected = value; + Refresh(); + RefreshState(); + OnPropertyChanged(value); } } - private bool _isSelected; + } + private bool _isSelected; - //[Editor] - public bool DominantSelectedState { get => _dominantSelectedState; set => this.SetField(ref _dominantSelectedState, value); } - private bool _dominantSelectedState = true; + //[Editor] + public bool DominantSelectedState { get => _dominantSelectedState; set => this.SetField(ref _dominantSelectedState, value); } + private bool _dominantSelectedState = true; - //[Editor] - public bool ManualToggle { get => _manualToggle; set => this.SetField(ref _manualToggle, value); } - private bool _manualToggle; + //[Editor] + public bool ManualToggle { get => _manualToggle; set => this.SetField(ref _manualToggle, value); } + private bool _manualToggle; - private const float _maxDoubleClickDeltaTimeInSeconds = 0.5f; - private float _lastClickTime; + private const float _maxDoubleClickDeltaTimeInSeconds = 0.5f; + private float _lastClickTime; - private ButtonClickState _clickState; + private ButtonClickState _clickState; - public List> ClickEventHandlers = new(); + public List> ClickEventHandlers = new(); - public LauncherToggleButtonWidget(UIContext context) : base(context) - { - FrictionEnabled = true; - } + public LauncherToggleButtonWidget(UIContext context) : base(context) + { + FrictionEnabled = true; + } - protected override bool OnPreviewMousePressed() - { - base.OnPreviewMousePressed(); - return true; - } + protected override bool OnPreviewMousePressed() + { + base.OnPreviewMousePressed(); + return true; + } - protected override void RefreshState() + protected override void RefreshState() + { + base.RefreshState(); + if (!OverrideDefaultStateSwitchingEnabled) { - base.RefreshState(); - if (!OverrideDefaultStateSwitchingEnabled) + if (IsDisabled) { - if (IsDisabled) - { - SetState("Disabled"); - } - else if (IsSelected && DominantSelectedState) - { - SetState("Selected"); - } - else if (IsPressed) - { - SetState("Pressed"); - } - else if (IsHovered) - { - SetState("Hovered"); - } - else if (IsSelected && !DominantSelectedState) - { - SetState("Selected"); - } - else - { - SetState("Default"); - } + SetState("Disabled"); } - - if (UpdateChildrenStates) + else if (IsSelected && DominantSelectedState) { - for (var i = 0; i < ChildCount; i++) - { - var child = GetChild(i); - if (child is not ImageWidget { OverrideDefaultStateSwitchingEnabled: true }) - { - child.SetState(CurrentState); - } - } + SetState("Selected"); } - } - - private void Refresh() => ShowHideToggle(); - - private void ShowHideToggle() - { - if (ToggleIndicator == null) return; - - if (_isSelected) + else if (IsPressed) { - ToggleIndicator.Show(); - return; + SetState("Pressed"); + } + else if (IsHovered) + { + SetState("Hovered"); + } + else if (IsSelected && !DominantSelectedState) + { + SetState("Selected"); + } + else + { + SetState("Default"); } - - ToggleIndicator.Hide(); } - protected override void OnMousePressed() + if (UpdateChildrenStates) { - if (_clickState != ButtonClickState.None) return; - - _clickState = ButtonClickState.HandlingClick; - this.SetIsPressed(true); - if (!DoNotPassEventsToChildren) + for (var i = 0; i < ChildCount; i++) { - for (var i = 0; i < ChildCount; i++) + var child = GetChild(i); + if (child is not ImageWidget { OverrideDefaultStateSwitchingEnabled: true }) { - var child = GetChild(i); - child?.SetIsPressed(true); + child.SetState(CurrentState); } } } + } + + private void Refresh() => ShowHideToggle(); - protected override void OnMouseReleased() + private void ShowHideToggle() + { + if (ToggleIndicator == null) return; + + if (_isSelected) { - if (_clickState != ButtonClickState.HandlingClick) return; + ToggleIndicator.Show(); + return; + } - _clickState = ButtonClickState.None; - this.SetIsPressed(false); - if (!DoNotPassEventsToChildren) - { - for (var i = 0; i < ChildCount; i++) - { - var child = GetChild(i); - child?.SetIsPressed(false); - } - } + ToggleIndicator.Hide(); + } + + protected override void OnMousePressed() + { + if (_clickState != ButtonClickState.None) return; - if (IsPointInsideMeasuredAreaAndCheckIfVisible()) + _clickState = ButtonClickState.HandlingClick; + this.SetIsPressed(true); + if (!DoNotPassEventsToChildren) + { + for (var i = 0; i < ChildCount; i++) { - HandleClick(); + var child = GetChild(i); + child?.SetIsPressed(true); } } + } - private bool IsPointInsideMeasuredAreaAndCheckIfVisible() => this.IsPointInsideMeasuredArea() && IsRecursivelyVisible(); + protected override void OnMouseReleased() + { + if (_clickState != ButtonClickState.HandlingClick) return; - protected override void OnMouseAlternatePressed() + _clickState = ButtonClickState.None; + this.SetIsPressed(false); + if (!DoNotPassEventsToChildren) { - if (_clickState != ButtonClickState.None) return; - - _clickState = ButtonClickState.HandlingAlternateClick; - this.SetIsPressed(true); - if (!DoNotPassEventsToChildren) + for (var i = 0; i < ChildCount; i++) { - for (var i = 0; i < ChildCount; i++) - { - var child = GetChild(i); - child?.SetIsPressed(true); - } + var child = GetChild(i); + child?.SetIsPressed(false); } } - protected override void OnMouseAlternateReleased() + if (IsPointInsideMeasuredAreaAndCheckIfVisible()) { - if (_clickState != ButtonClickState.HandlingAlternateClick) return; + HandleClick(); + } + } - _clickState = ButtonClickState.None; - this.SetIsPressed(false); - if (!DoNotPassEventsToChildren) - { - for (var i = 0; i < ChildCount; i++) - { - var child = GetChild(i); - child?.SetIsPressed(false); - } - } + private bool IsPointInsideMeasuredAreaAndCheckIfVisible() => this.IsPointInsideMeasuredArea() && IsRecursivelyVisible(); + + protected override void OnMouseAlternatePressed() + { + if (_clickState != ButtonClickState.None) return; - if (IsPointInsideMeasuredAreaAndCheckIfVisible()) + _clickState = ButtonClickState.HandlingAlternateClick; + this.SetIsPressed(true); + if (!DoNotPassEventsToChildren) + { + for (var i = 0; i < ChildCount; i++) { - HandleAlternateClick(); + var child = GetChild(i); + child?.SetIsPressed(true); } } + } + + protected override void OnMouseAlternateReleased() + { + if (_clickState != ButtonClickState.HandlingAlternateClick) return; - private void HandleClick() + _clickState = ButtonClickState.None; + this.SetIsPressed(false); + if (!DoNotPassEventsToChildren) { - foreach (var action in ClickEventHandlers) + for (var i = 0; i < ChildCount; i++) { - action(this); + var child = GetChild(i); + child?.SetIsPressed(false); } + } - if (!ManualToggle) - { - IsSelected = !IsSelected; - } + if (IsPointInsideMeasuredAreaAndCheckIfVisible()) + { + HandleAlternateClick(); + } + } - OnClick(); - EventFired("Click", Array.Empty()); - if (Context.EventManager.Time - _lastClickTime < _maxDoubleClickDeltaTimeInSeconds) - { - EventFired("DoubleClick", Array.Empty()); - return; - } + private void HandleClick() + { + foreach (var action in ClickEventHandlers) + { + action(this); + } - _lastClickTime = Context.EventManager.Time; + if (!ManualToggle) + { + IsSelected = !IsSelected; } - private void HandleAlternateClick() + OnClick(); + EventFired("Click", Array.Empty()); + if (Context.EventManager.Time - _lastClickTime < _maxDoubleClickDeltaTimeInSeconds) { - OnAlternateClick(); - EventFired("AlternateClick", Array.Empty()); + EventFired("DoubleClick", Array.Empty()); + return; } - private void OnClick() { } - private void OnAlternateClick() { } + _lastClickTime = Context.EventManager.Time; } + + private void HandleAlternateClick() + { + OnAlternateClick(); + EventFired("AlternateClick", Array.Empty()); + } + + private void OnClick() { } + private void OnAlternateClick() { } } \ No newline at end of file