diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b89a207..e6a75ee 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,9 +7,9 @@ on: - 'src/Bannerlord.LauncherEx/**.*' - 'src/Bannerlord.BLSE/**.*' - 'src/Bannerlord.BLSE.Shared/**.*' - - 'src/Bannerlord.BLSE.NativeAOT.Standalone/**.*' - - 'src/Bannerlord.BLSE.NativeAOT.Launcher/**.*' - - 'src/Bannerlord.BLSE.NativeAOT.LauncherEx/**.*' + - 'src/Bannerlord.BLSE.Loaders.Standalone/**.*' + - 'src/Bannerlord.BLSE.Loaders.Launcher/**.*' + - 'src/Bannerlord.BLSE.Loaders.LauncherEx/**.*' workflow_dispatch: env: @@ -46,7 +46,7 @@ jobs: build-loaders-standalone: name: Build Loaders Standalone - runs-on: windows-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -55,10 +55,10 @@ jobs: with: github-token: ${{secrets.GITHUB_TOKEN}} - - name: Build Bannerlord.BLSE.NativeAOT.Standalone + - name: Build Bannerlord.BLSE.Loaders.Standalone run: | mkdir bannerlord; - dotnet build src/Bannerlord.BLSE.NativeAOT.Standalone/Bannerlord.BLSE.NativeAOT.Standalone.csproj --configuration Release -p:GameFolder="$PWD/bannerlord" /nowarn:MSB4011; + dotnet build src/Bannerlord.BLSE.Loaders.Standalone/Bannerlord.BLSE.Loaders.Standalone.csproj --configuration Release -p:GameFolder="$PWD/bannerlord" /nowarn:MSB4011; shell: pwsh - name: Upload Bannerlord folder @@ -70,7 +70,7 @@ jobs: build-loaders-launcher: name: Build Loaders Launcher - runs-on: windows-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -79,10 +79,10 @@ jobs: with: github-token: ${{secrets.GITHUB_TOKEN}} - - name: Build Bannerlord.BLSE.NativeAOT.Launcher + - name: Build Bannerlord.BLSE.Loaders.Launcher run: | mkdir bannerlord; - dotnet build src/Bannerlord.BLSE.NativeAOT.Launcher/Bannerlord.BLSE.NativeAOT.Launcher.csproj --configuration Release -p:GameFolder="$PWD/bannerlord" /nowarn:MSB4011; + dotnet build src/Bannerlord.BLSE.Loaders.Launcher/Bannerlord.BLSE.Loaders.Launcher.csproj --configuration Release -p:GameFolder="$PWD/bannerlord" /nowarn:MSB4011; shell: pwsh - name: Upload Bannerlord folder @@ -94,7 +94,7 @@ jobs: build-loaders-launcherex: name: Build Loaders LauncehrEx - runs-on: windows-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -103,10 +103,10 @@ jobs: with: github-token: ${{secrets.GITHUB_TOKEN}} - - name: Build Bannerlord.BLSE.NativeAOT.LauncherEx + - name: Build Bannerlord.BLSE.Loaders.LauncherEx run: | mkdir bannerlord; - dotnet build src/Bannerlord.BLSE.NativeAOT.LauncherEx/Bannerlord.BLSE.NativeAOT.LauncherEx.csproj --configuration Release -p:GameFolder="$PWD/bannerlord" /nowarn:MSB4011; + dotnet build src/Bannerlord.BLSE.Loaders.LauncherEx/Bannerlord.BLSE.Loaders.LauncherEx.csproj --configuration Release -p:GameFolder="$PWD/bannerlord" /nowarn:MSB4011; shell: pwsh - name: Upload Bannerlord folder diff --git a/build/common.props b/build/common.props index de09cd6..09a69e8 100644 --- a/build/common.props +++ b/build/common.props @@ -10,7 +10,7 @@ - 1.1.0 + 1.2.0 2.10.1 3.0.0.135 5.0.198 diff --git a/changelog.txt b/changelog.txt index 592083d..8243cfd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,12 @@ --------------------------------------------------------------------------------------------------- +Version: 1.2.0 +Game Versions: v1.0.0,v1.0.1,v1.0.2,v1.0.3,v1.1.0,v1.1.1 +* Reverted native code +* Added AccessViolationException handling support +* Fixed Singleplayer not being shown by default +* Better error messages +* Added /forcenetcore for Steam/GOG/Epic +--------------------------------------------------------------------------------------------------- Version: 1.1.0 Game Versions: v1.0.0,v1.0.1,v1.0.2,v1.0.3,v1.1.0,v1.1.1 * The launchers are now native code instead of C#, but for the cost of increased size diff --git a/src/Bannerlord.BLSE.NativeAOT.Launcher/Bannerlord.BLSE.NativeAOT.Launcher.csproj b/src/Bannerlord.BLSE.Loaders.Launcher/Bannerlord.BLSE.Loaders.Launcher.csproj similarity index 71% rename from src/Bannerlord.BLSE.NativeAOT.Launcher/Bannerlord.BLSE.NativeAOT.Launcher.csproj rename to src/Bannerlord.BLSE.Loaders.Launcher/Bannerlord.BLSE.Loaders.Launcher.csproj index bfc6a24..973f3f0 100644 --- a/src/Bannerlord.BLSE.NativeAOT.Launcher/Bannerlord.BLSE.NativeAOT.Launcher.csproj +++ b/src/Bannerlord.BLSE.Loaders.Launcher/Bannerlord.BLSE.Loaders.Launcher.csproj @@ -2,21 +2,26 @@ winexe - net7.0 - enable + x64 + net472 + 11.0 enable ../../resources/BLSE_SMALL.ico true - $(DefineConstants);LAUNCHER; + $(DefineConstants);LAUNCHER;NETCOREHOSTING;_NETFRAMEWORKHOSTING Bannerlord.BLSE.Launcher + + + - - + - + + + + + + + diff --git a/src/Bannerlord.BLSE.Loaders.Launcher/Properties/launchSettings.json b/src/Bannerlord.BLSE.Loaders.Launcher/Properties/launchSettings.json new file mode 100644 index 0000000..fd4205a --- /dev/null +++ b/src/Bannerlord.BLSE.Loaders.Launcher/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "Bannerlord.BLSE.NativeAOT.Launcher": { + "commandName": "Project", + "environmentVariables": { + + } + } + } +} diff --git a/src/Bannerlord.BLSE.NativeAOT.LauncherEx/Bannerlord.BLSE.NativeAOT.LauncherEx.csproj b/src/Bannerlord.BLSE.Loaders.LauncherEx/Bannerlord.BLSE.Loaders.LauncherEx.csproj similarity index 70% rename from src/Bannerlord.BLSE.NativeAOT.LauncherEx/Bannerlord.BLSE.NativeAOT.LauncherEx.csproj rename to src/Bannerlord.BLSE.Loaders.LauncherEx/Bannerlord.BLSE.Loaders.LauncherEx.csproj index 4899b75..090f036 100644 --- a/src/Bannerlord.BLSE.NativeAOT.LauncherEx/Bannerlord.BLSE.NativeAOT.LauncherEx.csproj +++ b/src/Bannerlord.BLSE.Loaders.LauncherEx/Bannerlord.BLSE.Loaders.LauncherEx.csproj @@ -2,21 +2,26 @@ winexe - net7.0 - enable + x64 + net472 + 11.0 enable ../../resources/BLSE_SMALL.ico true - $(DefineConstants);LAUNCHEREX; + $(DefineConstants);LAUNCHEREX;NETCOREHOSTING;_NETFRAMEWORKHOSTING Bannerlord.BLSE.LauncherEx + + - - + + + + + + + diff --git a/src/Bannerlord.BLSE.NativeAOT.Standalone/Bannerlord.BLSE.NativeAOT.Standalone.csproj b/src/Bannerlord.BLSE.Loaders.Standalone/Bannerlord.BLSE.Loaders.Standalone.csproj similarity index 73% rename from src/Bannerlord.BLSE.NativeAOT.Standalone/Bannerlord.BLSE.NativeAOT.Standalone.csproj rename to src/Bannerlord.BLSE.Loaders.Standalone/Bannerlord.BLSE.Loaders.Standalone.csproj index 729c679..405ccfc 100644 --- a/src/Bannerlord.BLSE.NativeAOT.Standalone/Bannerlord.BLSE.NativeAOT.Standalone.csproj +++ b/src/Bannerlord.BLSE.Loaders.Standalone/Bannerlord.BLSE.Loaders.Standalone.csproj @@ -2,21 +2,26 @@ winexe - net7.0 - enable + x64 + net472 + 11.0 enable ../../resources/BLSE_SMALL.ico true - $(DefineConstants);STANDALONE; + $(DefineConstants);STANDALONE;NETCOREHOSTING;_NETFRAMEWORKHOSTING Bannerlord.BLSE.Standalone + + - - + + + diff --git a/src/Bannerlord.BLSE.NativeAOT.Standalone/NETCoreLoader.cs b/src/Bannerlord.BLSE.Loaders.Standalone/NETCoreLoader.Hosting.cs similarity index 82% rename from src/Bannerlord.BLSE.NativeAOT.Standalone/NETCoreLoader.cs rename to src/Bannerlord.BLSE.Loaders.Standalone/NETCoreLoader.Hosting.cs index 1a1cce2..730ebd4 100644 --- a/src/Bannerlord.BLSE.NativeAOT.Standalone/NETCoreLoader.cs +++ b/src/Bannerlord.BLSE.Loaders.Standalone/NETCoreLoader.Hosting.cs @@ -1,20 +1,24 @@ -using System.Runtime.InteropServices; +#if NETCOREHOSTING +using System; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; using System.Text; namespace Bannerlord.BLSE; -[UnmanagedFunctionPointer(CallingConvention.StdCall)] -file delegate void EntryDelegate(int argc, IntPtr[] argv); - -public static partial class NETCoreLoader +public static class NETCoreLoader { + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate void EntryDelegate(int argc, IntPtr[] argv); + private const string CoreCLRPath = "Microsoft.NETCore.App/coreclr.dll"; - [LibraryImport(CoreCLRPath)] - private static partial int coreclr_initialize(IntPtr exePath, IntPtr appDomainFriendlyName, int propertyCount, IntPtr[] propertyKeys, IntPtr[] propertyValues, out IntPtr hostHandle, out IntPtr domainId); + [DllImport(CoreCLRPath)] + private static extern int coreclr_initialize(IntPtr exePath, IntPtr appDomainFriendlyName, int propertyCount, IntPtr[] propertyKeys, IntPtr[] propertyValues, out IntPtr hostHandle, out IntPtr domainId); - [LibraryImport(CoreCLRPath)] - private static partial int coreclr_create_delegate(IntPtr hostHandle, uint domainId, IntPtr entryPointAssemblyName, IntPtr entryPointTypeName, IntPtr entryPointMethodName, out IntPtr @delegate); + [DllImport(CoreCLRPath)] + private static extern int coreclr_create_delegate(IntPtr hostHandle, uint domainId, IntPtr entryPointAssemblyName, IntPtr entryPointTypeName, IntPtr entryPointMethodName, out IntPtr @delegate); private static IntPtr NativeUTF8(string str) { @@ -89,4 +93,5 @@ public static void Launch(string[] args) var @delegate = Marshal.GetDelegateForFunctionPointer(pMethod); @delegate(args.Length, args2); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/Bannerlord.BLSE.NativeAOT.Standalone/NETFrameworkLoader.cs b/src/Bannerlord.BLSE.Loaders.Standalone/NETFrameworkLoader.Hosting.cs similarity index 70% rename from src/Bannerlord.BLSE.NativeAOT.Standalone/NETFrameworkLoader.cs rename to src/Bannerlord.BLSE.Loaders.Standalone/NETFrameworkLoader.Hosting.cs index eb6226a..6cd0488 100644 --- a/src/Bannerlord.BLSE.NativeAOT.Standalone/NETFrameworkLoader.cs +++ b/src/Bannerlord.BLSE.Loaders.Standalone/NETFrameworkLoader.Hosting.cs @@ -1,8 +1,48 @@ -using System.Diagnostics.CodeAnalysis; +#if NETFRAMEWORKHOSTING +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.InteropServices; namespace Bannerlord.BLSE; +[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Local")] +[SuppressMessage("ReSharper", "UnusedVariable")] +file readonly unsafe struct IEnumUnknown +{ +#pragma warning disable CS0649 + private readonly IEnumUnknownVtbl* vtbl; +#pragma warning restore CS0649 + + public static nint Release(IEnumUnknown* host) + { + var release = (delegate*) host->vtbl->Release; + return release(host); + } + + public static nint Next(IEnumUnknown* host, int celt, T** pEnumRuntime, int* pCeltFetched) where T : unmanaged + { + var start = (delegate*) host->vtbl->Next; + return start(host, celt, pEnumRuntime, pCeltFetched); + } +} + +[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Local")] +[SuppressMessage("ReSharper", "UnusedVariable")] +file readonly unsafe struct IEnumUnknownVtbl +{ +#pragma warning disable CS0649 + public readonly void* QueryInterface; + public readonly void* AddRef; + public readonly void* Release; + public readonly void* Next; + public readonly void* Skip; + public readonly void* Reset; + public readonly void* Clone; +#pragma warning restore CS0649 +} + [SuppressMessage("ReSharper", "UnusedMethodReturnValue.Local")] [SuppressMessage("ReSharper", "UnusedVariable")] file readonly unsafe struct ICLRRuntimeHost @@ -75,6 +115,15 @@ public static nint Release(ICLRRuntimeInfo* host) return release(host); } + public static string GetVersionString(ICLRRuntimeInfo* host) + { + var chars = stackalloc char[20]; + var size = 20; + var release = (delegate*) host->vtbl->GetVersionString; + var result = release(host, &chars[0], &size); + return new string(chars); + } + private static T* GetInterface(ICLRRuntimeInfo* host, Guid* rclsid, Guid* riid) where T : unmanaged { var getInterface = (delegate*) host->vtbl->GetInterface; @@ -116,6 +165,12 @@ file readonly unsafe struct ICLRRuntimeInfoVtbl #pragma warning restore CS0649 } +file unsafe class InstalledRuntime +{ + public ICLRRuntimeInfo* RuntimeInfo { get; set; } + public string Version { get; set; } +} + [SuppressMessage("ReSharper", "UnusedMethodReturnValue.Local")] [SuppressMessage("ReSharper", "UnusedVariable")] file readonly unsafe struct ICLRMetaHost @@ -148,6 +203,34 @@ public static nint Release(ICLRMetaHost* host) var release = (delegate*) host->vtbl->Release; return release(host); } + + public static List GetInstalledRuntimes(ICLRMetaHost* host) + { + var runtimeEnumerator = EnumerateInstalledRuntimes(host); + var list = new List(); + var fetched = 0; + ICLRRuntimeInfo* runtimeInfo = null; + for (;;) + { + var result = IEnumUnknown.Next(runtimeEnumerator, 1, &runtimeInfo, &fetched); + if (result != 0) break; + list.Add(new InstalledRuntime + { + RuntimeInfo = runtimeInfo, + Version = ICLRRuntimeInfo.GetVersionString(runtimeInfo), + }); + } + IEnumUnknown.Release(runtimeEnumerator); + return list; + } + + public static IEnumUnknown* EnumerateInstalledRuntimes(ICLRMetaHost* host) + { + IEnumUnknown* ptr; + var enumerateInstalledRuntimes = (delegate*) host->vtbl->EnumerateInstalledRuntimes; + var result = enumerateInstalledRuntimes(host, &ptr); + return ptr; + } public static ICLRRuntimeInfo* GetRuntime(ICLRMetaHost* host, string str) { @@ -189,12 +272,14 @@ public static void Launch(string[] args) Environment.SetEnvironmentVariable("COMPlus_legacyCorruptedStateExceptionsPolicy", "1"); var clrMetaHost = ICLRMetaHost.Create(); - var runtimeInfo = ICLRMetaHost.GetRuntime(clrMetaHost, "v4.0.30319"); - var runtimeHost = ICLRRuntimeInfo.GetRuntimeHost(runtimeInfo); + var runtimes = ICLRMetaHost.GetInstalledRuntimes(clrMetaHost); + var runtime = runtimes.OrderBy(x => x.Version).Last(x => x.Version.StartsWith("v4")); + var runtimeHost = ICLRRuntimeInfo.GetRuntimeHost(runtime.RuntimeInfo); var startResult = ICLRRuntimeHost.Start(runtimeHost); var executeResult = ICLRRuntimeHost.ExecuteInDefaultAppDomain(runtimeHost, "Bannerlord.BLSE.Shared.dll", "Bannerlord.BLSE.Shared.Program", "NativeEntry2", string.Join("|||", args)); - ICLRRuntimeInfo.Release(runtimeInfo); + ICLRRuntimeInfo.Release(runtime.RuntimeInfo); ICLRRuntimeHost.Release(runtimeHost); ICLRMetaHost.Release(clrMetaHost); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/Bannerlord.BLSE.Loaders.Standalone/NETFrameworkLoader.cs b/src/Bannerlord.BLSE.Loaders.Standalone/NETFrameworkLoader.cs new file mode 100644 index 0000000..e00cf7a --- /dev/null +++ b/src/Bannerlord.BLSE.Loaders.Standalone/NETFrameworkLoader.cs @@ -0,0 +1,23 @@ +#if !NETFRAMEWORKHOSTING +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace Bannerlord.BLSE; + +[SuppressMessage("ReSharper", "UnusedVariable")] +public static class NETFrameworkLoader +{ + private delegate void Main(string[] args); + + public static void Launch(string[] args) + { + // Catch AccessViolation + Environment.SetEnvironmentVariable("COMPlus_legacyCorruptedStateExceptionsPolicy", "1"); + + var sharedAssembly = Assembly.LoadFrom("Bannerlord.BLSE.Shared.dll"); + var sharedMainDelegate = (Main) Delegate.CreateDelegate(typeof(Main), sharedAssembly.GetType("Bannerlord.BLSE.Shared.Program"), "Main"); + sharedMainDelegate(args); + } +} +#endif \ No newline at end of file diff --git a/src/Bannerlord.BLSE.NativeAOT.Standalone/Program.cs b/src/Bannerlord.BLSE.Loaders.Standalone/Program.cs similarity index 66% rename from src/Bannerlord.BLSE.NativeAOT.Standalone/Program.cs rename to src/Bannerlord.BLSE.Loaders.Standalone/Program.cs index 050723d..e604328 100644 --- a/src/Bannerlord.BLSE.NativeAOT.Standalone/Program.cs +++ b/src/Bannerlord.BLSE.Loaders.Standalone/Program.cs @@ -1,4 +1,7 @@ -namespace Bannerlord.BLSE; +using System.IO; +using System.Linq; + +namespace Bannerlord.BLSE; public static class Program { @@ -11,16 +14,22 @@ public static void Main(string[] args) #elif LAUNCHEREX args = new[] { "launcherex" }.Concat(args).ToArray(); #endif + switch (new DirectoryInfo(Directory.GetCurrentDirectory()).Name) { case "Win64_Shipping_Client": - NETFrameworkLoader.Launch(args); + { + if (args.Contains("/forcenetcore")) + NETCoreLoader.Launch(args); + else + NETFrameworkLoader.Launch(args); break; + } case "Gaming.Desktop.x64_Shipping_Client": + { NETCoreLoader.Launch(args); break; + } } - - NETFrameworkLoader.Launch(args); } } \ No newline at end of file diff --git a/src/Bannerlord.BLSE.Shared/Launcher.cs b/src/Bannerlord.BLSE.Shared/Launcher.cs index 24437d6..b19b3eb 100644 --- a/src/Bannerlord.BLSE.Shared/Launcher.cs +++ b/src/Bannerlord.BLSE.Shared/Launcher.cs @@ -9,19 +9,18 @@ using System; using System.Runtime.InteropServices; +using Windows.Win32; + namespace Bannerlord.BLSE.Shared; public static class Launcher { - [DllImport("user32.dll")] - private static extern bool SetProcessDPIAware(); - private static readonly Harmony _featureHarmony = new("bannerlord.blse.features"); public static void Launch(string[] args) { if (Environment.OSVersion.Version.Major >= 6) - SetProcessDPIAware(); + PInvoke.SetProcessDPIAware(); InterceptorFeature.Enable(_featureHarmony); AssemblyResolverFeature.Enable(_featureHarmony); diff --git a/src/Bannerlord.BLSE.Shared/LauncherEx.cs b/src/Bannerlord.BLSE.Shared/LauncherEx.cs index d2a8a97..8ae4ea0 100644 --- a/src/Bannerlord.BLSE.Shared/LauncherEx.cs +++ b/src/Bannerlord.BLSE.Shared/LauncherEx.cs @@ -14,19 +14,18 @@ using System.Runtime.InteropServices; using System.Text; +using Windows.Win32; + namespace Bannerlord.BLSE.Shared; public static class LauncherEx { - [DllImport("user32.dll")] - private static extern bool SetProcessDPIAware(); - private static readonly Harmony _featureHarmony = new("bannerlord.blse.features"); public static void Launch(string[] args) { if (Environment.OSVersion.Version.Major >= 6) - SetProcessDPIAware(); + PInvoke.SetProcessDPIAware(); InterceptorFeature.Enable(_featureHarmony); AssemblyResolverFeature.Enable(_featureHarmony); diff --git a/src/Bannerlord.BLSE.Shared/NativeMethods.txt b/src/Bannerlord.BLSE.Shared/NativeMethods.txt index 516ca18..aaf9e0a 100644 --- a/src/Bannerlord.BLSE.Shared/NativeMethods.txt +++ b/src/Bannerlord.BLSE.Shared/NativeMethods.txt @@ -1,4 +1,5 @@ MessageBox DeleteFile GetConsoleWindow -ShowWindow \ No newline at end of file +ShowWindow +SetProcessDPIAware \ No newline at end of file diff --git a/src/Bannerlord.BLSE.Shared/Program.cs b/src/Bannerlord.BLSE.Shared/Program.cs index 5e34f9d..8273aac 100644 --- a/src/Bannerlord.BLSE.Shared/Program.cs +++ b/src/Bannerlord.BLSE.Shared/Program.cs @@ -53,9 +53,10 @@ public static void NativeEntry(int argc, IntPtr argv) } Main(args); } - + public static int NativeEntry2(string args) { + GetEntryAssembly.Enable(); Main(args.Split(new[] { "|||" }, StringSplitOptions.RemoveEmptyEntries)); return 0; } diff --git a/src/Bannerlord.BLSE.Shared/Standalone.cs b/src/Bannerlord.BLSE.Shared/Standalone.cs index b0d429c..34a76d1 100644 --- a/src/Bannerlord.BLSE.Shared/Standalone.cs +++ b/src/Bannerlord.BLSE.Shared/Standalone.cs @@ -18,6 +18,8 @@ using TaleWorlds.MountAndBlade.Launcher.Library; using TaleWorlds.SaveSystem; +using Windows.Win32; + using MessageBoxButtons = Bannerlord.BLSE.Shared.Utils.MessageBoxButtons; using MessageBoxDefaultButton = Bannerlord.BLSE.Shared.Utils.MessageBoxDefaultButton; using MessageBoxIcon = Bannerlord.BLSE.Shared.Utils.MessageBoxIcon; @@ -26,9 +28,6 @@ namespace Bannerlord.BLSE.Shared; public static class Standalone { - [DllImport("user32.dll")] - private static extern bool SetProcessDPIAware(); - private static readonly Harmony _featureHarmony = new("bannerlord.blse.features"); private static string[] GetModules(MetaData metadata) @@ -97,7 +96,7 @@ private static void TryLoadLoadOrderFromSaveFile(ref string[] args) public static void Launch(string[] args) { if (Environment.OSVersion.Version.Major >= 6) - SetProcessDPIAware(); + PInvoke.SetProcessDPIAware(); TryLoadLoadOrderFromSaveFile(ref args); diff --git a/src/Bannerlord.BLSE.Shared/Utils/GetEntryAssembly.cs b/src/Bannerlord.BLSE.Shared/Utils/GetEntryAssembly.cs new file mode 100644 index 0000000..e799c03 --- /dev/null +++ b/src/Bannerlord.BLSE.Shared/Utils/GetEntryAssembly.cs @@ -0,0 +1,25 @@ +using HarmonyLib; +using HarmonyLib.BUTR.Extensions; + +using System.Reflection; + +namespace Bannerlord.BLSE.Shared.Utils; + +internal static class GetEntryAssembly +{ + private static readonly Harmony _harmony = new("Bannerlord.BLSE.Shared.Patches.GetEntryAssembly"); + + + public static void Enable() + { + var result = _harmony.TryPatch( + AccessTools2.Method(typeof(Assembly), nameof(Assembly.GetEntryAssembly)), + prefix: AccessTools2.Method(typeof(Unblocker), nameof(GetEntryAssemblyPrefix))); + } + + private static bool GetEntryAssemblyPrefix(ref Assembly __result) + { + __result = typeof(GetEntryAssembly).Assembly; + return false; + } +} \ No newline at end of file diff --git a/src/Bannerlord.BLSE.Shared/Utils/LauncherExceptionHandler.cs b/src/Bannerlord.BLSE.Shared/Utils/LauncherExceptionHandler.cs index 4e17a20..13659c4 100644 --- a/src/Bannerlord.BLSE.Shared/Utils/LauncherExceptionHandler.cs +++ b/src/Bannerlord.BLSE.Shared/Utils/LauncherExceptionHandler.cs @@ -4,6 +4,8 @@ using System; using System.IO; using System.Reflection; +using System.Runtime.ExceptionServices; +using System.Security; using System.Text; namespace Bannerlord.BLSE.Shared.Utils; @@ -31,6 +33,7 @@ private static void MainPrefix() _harmony.Unpatch(AccessTools2.DeclaredMethod("TaleWorlds.Starter.Library.Program:Main"), AccessTools2.Method(typeof(Unblocker), nameof(MainPrefix))); } + [HandleProcessCorruptedStateExceptions, SecurityCritical] private static void CurrentDomainOnUnhandledException(object? _, UnhandledExceptionEventArgs e) { static string GetRecursiveException(Exception ex) => new StringBuilder() diff --git a/src/Bannerlord.BLSE.sln b/src/Bannerlord.BLSE.sln index 0258778..6af1fcf 100644 --- a/src/Bannerlord.BLSE.sln +++ b/src/Bannerlord.BLSE.sln @@ -33,11 +33,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bannerlord.BLSE.Shared", "B EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "loaders", "loaders", "{15129193-D2B6-4A7C-97EF-3736ED1372CF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bannerlord.BLSE.NativeAOT.Launcher", "Bannerlord.BLSE.NativeAOT.Launcher\Bannerlord.BLSE.NativeAOT.Launcher.csproj", "{A99D50E0-924A-47A3-A465-5D53F2DDE4DE}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bannerlord.BLSE.Loaders.Launcher", "Bannerlord.BLSE.Loaders.Launcher\Bannerlord.BLSE.Loaders.Launcher.csproj", "{A99D50E0-924A-47A3-A465-5D53F2DDE4DE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bannerlord.BLSE.NativeAOT.Standalone", "Bannerlord.BLSE.NativeAOT.Standalone\Bannerlord.BLSE.NativeAOT.Standalone.csproj", "{25C39C41-4013-440F-A1BA-5F25EF6A040B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bannerlord.BLSE.Loaders.Standalone", "Bannerlord.BLSE.Loaders.Standalone\Bannerlord.BLSE.Loaders.Standalone.csproj", "{25C39C41-4013-440F-A1BA-5F25EF6A040B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bannerlord.BLSE.NativeAOT.LauncherEx", "Bannerlord.BLSE.NativeAOT.LauncherEx\Bannerlord.BLSE.NativeAOT.LauncherEx.csproj", "{1605D5AF-A37B-4062-AAB7-DAA921AF74FC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bannerlord.BLSE.Loaders.LauncherEx", "Bannerlord.BLSE.Loaders.LauncherEx\Bannerlord.BLSE.Loaders.LauncherEx.csproj", "{1605D5AF-A37B-4062-AAB7-DAA921AF74FC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -59,16 +59,16 @@ Global {59A34CE4-AC23-495F-BC7B-133114E7E6D8}.Release|Any CPU.Build.0 = Release|Any CPU {A99D50E0-924A-47A3-A465-5D53F2DDE4DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {A99D50E0-924A-47A3-A465-5D53F2DDE4DE}.Release|Any CPU.Build.0 = Release|Any CPU - {A99D50E0-924A-47A3-A465-5D53F2DDE4DE}.Debug|Any CPU.ActiveCfg = Release|Any CPU - {A99D50E0-924A-47A3-A465-5D53F2DDE4DE}.Debug|Any CPU.Build.0 = Release|Any CPU + {A99D50E0-924A-47A3-A465-5D53F2DDE4DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A99D50E0-924A-47A3-A465-5D53F2DDE4DE}.Debug|Any CPU.Build.0 = Debug|Any CPU {25C39C41-4013-440F-A1BA-5F25EF6A040B}.Release|Any CPU.ActiveCfg = Release|Any CPU {25C39C41-4013-440F-A1BA-5F25EF6A040B}.Release|Any CPU.Build.0 = Release|Any CPU - {25C39C41-4013-440F-A1BA-5F25EF6A040B}.Debug|Any CPU.ActiveCfg = Release|Any CPU - {25C39C41-4013-440F-A1BA-5F25EF6A040B}.Debug|Any CPU.Build.0 = Release|Any CPU + {25C39C41-4013-440F-A1BA-5F25EF6A040B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25C39C41-4013-440F-A1BA-5F25EF6A040B}.Debug|Any CPU.Build.0 = Debug|Any CPU {1605D5AF-A37B-4062-AAB7-DAA921AF74FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {1605D5AF-A37B-4062-AAB7-DAA921AF74FC}.Release|Any CPU.Build.0 = Release|Any CPU - {1605D5AF-A37B-4062-AAB7-DAA921AF74FC}.Debug|Any CPU.ActiveCfg = Release|Any CPU - {1605D5AF-A37B-4062-AAB7-DAA921AF74FC}.Debug|Any CPU.Build.0 = Release|Any CPU + {1605D5AF-A37B-4062-AAB7-DAA921AF74FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1605D5AF-A37B-4062-AAB7-DAA921AF74FC}.Debug|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.cs b/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.cs index 78dadb1..bb25534 100644 --- a/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.cs +++ b/src/Bannerlord.LauncherEx/BUTRLauncherManagerHandler.cs @@ -57,7 +57,10 @@ private BUTRLauncherManagerHandler(UserDataManager userDataManager) loadLoadOrder: LoadTWLoadOrder, saveLoadOrder: loadOrder => { - var userGameTypeData = _userDataManager.UserData.SingleplayerData; + 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) { @@ -67,6 +70,7 @@ private BUTRLauncherManagerHandler(UserDataManager userDataManager) IsSelected = entry.IsSelected, }); } + _userDataManager.UserData.GameType = state.IsSingleplayer ? GameType.Singleplayer : GameType.Multiplayer; _userDataManager.SaveUserData(); }, sendNotification: (id, type, message, ms) => diff --git a/src/Bannerlord.LauncherEx/Bannerlord.LauncherEx.csproj b/src/Bannerlord.LauncherEx/Bannerlord.LauncherEx.csproj index 8fce17f..ad70afa 100644 --- a/src/Bannerlord.LauncherEx/Bannerlord.LauncherEx.csproj +++ b/src/Bannerlord.LauncherEx/Bannerlord.LauncherEx.csproj @@ -6,7 +6,7 @@ enable x64 full - 1.23.4 + 1.24.0 System.Diagnostics.CodeAnalysis.UnscopedRefAttribute true false diff --git a/src/Bannerlord.LauncherEx/Helpers/Input/WindowsClipboard.cs b/src/Bannerlord.LauncherEx/Helpers/Input/WindowsClipboard.cs index 2c45783..913d3f9 100644 --- a/src/Bannerlord.LauncherEx/Helpers/Input/WindowsClipboard.cs +++ b/src/Bannerlord.LauncherEx/Helpers/Input/WindowsClipboard.cs @@ -3,18 +3,15 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; -using System.Threading.Tasks; + +using Windows.Win32; +using Windows.Win32.Foundation; namespace Bannerlord.LauncherEx.Helpers; internal static class WindowsClipboard { - public static async Task SetTextAsync(string text, CancellationToken cancellation) - { - await TryOpenClipboardAsync(cancellation); - - InnerSet(text); - } + private const uint CFUnicodeText = 13; public static void SetText(string text) { @@ -23,23 +20,23 @@ public static void SetText(string text) InnerSet(text); } - static void InnerSet(string text) + private static unsafe void InnerSet(string text) { - EmptyClipboard(); - IntPtr hGlobal = default; + PInvoke.EmptyClipboard(); + var hGlobal = HANDLE.Null; try { var bytes = (text.Length + 1) * 2; - hGlobal = Marshal.AllocHGlobal(bytes); + hGlobal = (HANDLE) Marshal.AllocHGlobal(bytes); - if (hGlobal == default) + if (hGlobal == IntPtr.Zero) { ThrowWin32(); } - var target = GlobalLock(hGlobal); + var target = new IntPtr(PInvoke.GlobalLock(hGlobal)); - if (target == default) + if (target == IntPtr.Zero) { ThrowWin32(); } @@ -50,52 +47,33 @@ static void InnerSet(string text) } finally { - GlobalUnlock(target); + PInvoke.GlobalUnlock(target); } - if (SetClipboardData(cfUnicodeText, hGlobal) == default) + if (PInvoke.SetClipboardData(CFUnicodeText, hGlobal) == IntPtr.Zero) { ThrowWin32(); } - hGlobal = default; + hGlobal = HANDLE.Null; } finally { - if (hGlobal != default) + if (hGlobal != IntPtr.Zero) { Marshal.FreeHGlobal(hGlobal); } - CloseClipboard(); - } - } - - static async Task TryOpenClipboardAsync(CancellationToken cancellation) - { - var num = 10; - while (true) - { - if (OpenClipboard(default)) - { - break; - } - - if (--num == 0) - { - ThrowWin32(); - } - - await Task.Delay(100, cancellation); + PInvoke.CloseClipboard(); } } - static void TryOpenClipboard() + private static void TryOpenClipboard() { var num = 10; while (true) { - if (OpenClipboard(default)) + if (PInvoke.OpenClipboard(HWND.Null)) { break; } @@ -109,20 +87,9 @@ static void TryOpenClipboard() } } - public static async Task GetTextAsync(CancellationToken cancellation) - { - if (!IsClipboardFormatAvailable(cfUnicodeText)) - { - return null; - } - await TryOpenClipboardAsync(cancellation); - - return InnerGet(); - } - public static string? GetText() { - if (!IsClipboardFormatAvailable(cfUnicodeText)) + if (!PInvoke.IsClipboardFormatAvailable(CFUnicodeText)) { return null; } @@ -131,26 +98,26 @@ static void TryOpenClipboard() return InnerGet(); } - static string? InnerGet() + private static unsafe string? InnerGet() { - IntPtr handle = default; + var handle = IntPtr.Zero; - IntPtr pointer = default; + var pointer = IntPtr.Zero; try { - handle = GetClipboardData(cfUnicodeText); - if (handle == default) + handle = PInvoke.GetClipboardData(CFUnicodeText); + if (handle == IntPtr.Zero) { return null; } - pointer = GlobalLock(handle); - if (pointer == default) + pointer = new IntPtr(PInvoke.GlobalLock(handle)); + if (pointer == IntPtr.Zero) { return null; } - var size = GlobalSize(handle); + var size = (int) PInvoke.GlobalSize(handle); var buff = new byte[size]; Marshal.Copy(pointer, buff, 0, size); @@ -159,50 +126,14 @@ static void TryOpenClipboard() } finally { - if (pointer != default) + if (pointer != IntPtr.Zero) { - GlobalUnlock(handle); + PInvoke.GlobalUnlock(handle); } - CloseClipboard(); + PInvoke.CloseClipboard(); } } - const uint cfUnicodeText = 13; - - static void ThrowWin32() - { - throw new Win32Exception(Marshal.GetLastWin32Error()); - } - - [DllImport("User32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool IsClipboardFormatAvailable(uint format); - - [DllImport("User32.dll", SetLastError = true)] - static extern IntPtr GetClipboardData(uint uFormat); - - [DllImport("kernel32.dll", SetLastError = true)] - static extern IntPtr GlobalLock(IntPtr hMem); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool GlobalUnlock(IntPtr hMem); - - [DllImport("user32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool OpenClipboard(IntPtr hWndNewOwner); - - [DllImport("user32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool CloseClipboard(); - - [DllImport("user32.dll", SetLastError = true)] - static extern IntPtr SetClipboardData(uint uFormat, IntPtr data); - - [DllImport("user32.dll")] - static extern bool EmptyClipboard(); - - [DllImport("Kernel32.dll", SetLastError = true)] - static extern int GlobalSize(IntPtr hMem); + private static void ThrowWin32() => throw new Win32Exception(Marshal.GetLastWin32Error()); } \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Mixins/LauncherVMMixin.cs b/src/Bannerlord.LauncherEx/Mixins/LauncherVMMixin.cs index 2f35777..8b516c7 100644 --- a/src/Bannerlord.LauncherEx/Mixins/LauncherVMMixin.cs +++ b/src/Bannerlord.LauncherEx/Mixins/LauncherVMMixin.cs @@ -379,29 +379,6 @@ public void SaveOptions() OptionsEngineData.Save(); } - public void UpdateAndSaveUserModsData(bool isMultiplayer) - { - if (_userDataManager is null || ViewModel?.ModsData.GetModules() is not { } modules) - return; - if (_userDataManager.UserData.GameType == GameType.Singleplayer && isMultiplayer) - return; - if (_userDataManager.UserData.GameType == GameType.Multiplayer && !isMultiplayer) - return; - - var userData = _userDataManager.UserData; - var userGameTypeData = isMultiplayer ? userData.MultiplayerData : userData.SingleplayerData; - userGameTypeData.ModDatas.Clear(); - foreach (var moduleVM in modules) - { - userGameTypeData.ModDatas.Add(new UserModData - { - Id = moduleVM.ModuleInfoExtended.Id, - IsSelected = moduleVM.IsSelected, - }); - } - _userDataManager.SaveUserData(); - } - // Ensure save is triggered when launching the game [BUTRDataSourceMethod] public void ExecuteConfirmUnverifiedDLLStart() diff --git a/src/Bannerlord.LauncherEx/NativeMethods.txt b/src/Bannerlord.LauncherEx/NativeMethods.txt index ad06a09..df05b64 100644 --- a/src/Bannerlord.LauncherEx/NativeMethods.txt +++ b/src/Bannerlord.LauncherEx/NativeMethods.txt @@ -9,4 +9,13 @@ CreateToolhelp32Snapshot Process32First Process32Next CloseHandle -MessageBox \ No newline at end of file +MessageBox +IsClipboardFormatAvailable +GetClipboardData +GlobalLock +GlobalUnlock +OpenClipboard +CloseClipboard +SetClipboardData +EmptyClipboard +GlobalSize \ No newline at end of file diff --git a/src/Bannerlord.LauncherEx/Patches/LauncherVMPatch.cs b/src/Bannerlord.LauncherEx/Patches/LauncherVMPatch.cs index b3d1669..1a6913d 100644 --- a/src/Bannerlord.LauncherEx/Patches/LauncherVMPatch.cs +++ b/src/Bannerlord.LauncherEx/Patches/LauncherVMPatch.cs @@ -1,6 +1,4 @@ -using Bannerlord.LauncherEx.Extensions; -using Bannerlord.LauncherEx.Mixins; -using Bannerlord.LauncherManager; +using Bannerlord.LauncherManager; using HarmonyLib; using HarmonyLib.BUTR.Extensions; @@ -43,16 +41,8 @@ public static bool Enable(Harmony harmony) return true; } - public static bool UpdateAndSaveUserModsDataPrefix(LauncherVM __instance, bool isMultiplayer) - { - if (__instance.GetMixin() is { } mixin) - { - mixin.UpdateAndSaveUserModsData(isMultiplayer); - return false; - } - - return true; - } + // Disable Vanilla's saving + public static bool UpdateAndSaveUserModsDataPrefix() => false; [MethodImpl(MethodImplOptions.NoOptimization)] public static bool GetApplicationVersionOfModulePrefix(string id, ref ApplicationVersion __result)