From 57658bdbb778a251ce727dea8c152740637d9b86 Mon Sep 17 00:00:00 2001 From: ghorsington Date: Sat, 14 May 2022 22:46:00 +0300 Subject: [PATCH] BepInEx.IL2CPP: retarget to net6.0, switch to Il2CppInterop --- .../Console/Windows/WindowsConsoleDriver.cs | 43 +- BepInEx.IL2CPP/BasePlugin.cs | 3 +- BepInEx.IL2CPP/BepInEx.IL2CPP.csproj | 11 +- .../Hook/IL2CPPDetourMethodPatcher.cs | 58 +-- .../Hook/UnhollowerDetourHandler.cs | 2 +- BepInEx.IL2CPP/IL2CPPChainloader.cs | 35 +- BepInEx.IL2CPP/Il2CppInteropManager.cs | 283 ++++++++++++ BepInEx.IL2CPP/Logging/IL2CPPLogSource.cs | 2 +- .../Logging/IL2CPPUnityLogSource.cs | 6 +- BepInEx.IL2CPP/Preloader.cs | 21 +- BepInEx.IL2CPP/ProxyAssemblyGenerator.cs | 410 ------------------ BepInEx.IL2CPP/UnityEngine/Input.cs | 4 +- BepInEx.IL2CPP/UnityPreloadRunner.cs | 10 +- .../Collections/Il2CppManagedEnumerable.cs | 2 +- .../Collections/Il2CppManagedEnumerator.cs | 8 +- BepInEx.IL2CPP/Utils/Il2CppUtils.cs | 4 +- 16 files changed, 379 insertions(+), 523 deletions(-) create mode 100644 BepInEx.IL2CPP/Il2CppInteropManager.cs delete mode 100644 BepInEx.IL2CPP/ProxyAssemblyGenerator.cs diff --git a/BepInEx.Core/Console/Windows/WindowsConsoleDriver.cs b/BepInEx.Core/Console/Windows/WindowsConsoleDriver.cs index 9cd7013..706da35 100644 --- a/BepInEx.Core/Console/Windows/WindowsConsoleDriver.cs +++ b/BepInEx.Core/Console/Windows/WindowsConsoleDriver.cs @@ -32,8 +32,35 @@ internal class WindowsConsoleDriver : IConsoleDriver private bool useWinApiEncoder; - private int ConsoleWidth => getWindowWidth?.Invoke() ?? 0; - private int ConsoleHeight => getWindowHeight?.Invoke() ?? 0; + private int ConsoleWidth + { + get + { + try + { + return getWindowWidth?.Invoke() ?? 0; + } + catch (IOException) + { + return 0; + } + } + } + + private int ConsoleHeight + { + get + { + try + { + return getWindowHeight?.Invoke() ?? 0; + } + catch (IOException) + { + return 0; + } + } + } public TextWriter StandardOut { get; private set; } public TextWriter ConsoleOut { get; private set; } @@ -60,14 +87,7 @@ internal class WindowsConsoleDriver : IConsoleDriver public void CreateConsole(uint codepage) { - try - { - ConsoleWindow.Attach(); - } - catch (Exception e) - { - // Can't allocate console; we can still continue on in case there is stdout set up - } + ConsoleWindow.Attach(); // Make sure of ConsoleEncoding helper class because on some Monos // Encoding.GetEncoding throws NotImplementedException on most codepages @@ -90,8 +110,7 @@ internal class WindowsConsoleDriver : IConsoleDriver AutoFlush = true }; - var consoleOutStream = - OpenFileStream(ConsoleWindow.ConsoleOutHandle != IntPtr.Zero ? ConsoleWindow.ConsoleOutHandle : stdout); + var consoleOutStream = OpenFileStream(ConsoleWindow.ConsoleOutHandle); // Can't use Console.OutputEncoding because it can be null (i.e. not preference by user) ConsoleOut = new StreamWriter(consoleOutStream, useWinApiEncoder ? ConsoleEncoding.OutputEncoding : Utility.UTF8NoBom) diff --git a/BepInEx.IL2CPP/BasePlugin.cs b/BepInEx.IL2CPP/BasePlugin.cs index b9de98f..6af7046 100644 --- a/BepInEx.IL2CPP/BasePlugin.cs +++ b/BepInEx.IL2CPP/BasePlugin.cs @@ -1,6 +1,7 @@ using BepInEx.Configuration; using BepInEx.Logging; -using UnhollowerBaseLib; +using Il2CppInterop.Runtime; +using Logger = BepInEx.Logging.Logger; namespace BepInEx.IL2CPP; diff --git a/BepInEx.IL2CPP/BepInEx.IL2CPP.csproj b/BepInEx.IL2CPP/BepInEx.IL2CPP.csproj index 28d5dad..6a2055e 100644 --- a/BepInEx.IL2CPP/BepInEx.IL2CPP.csproj +++ b/BepInEx.IL2CPP/BepInEx.IL2CPP.csproj @@ -1,7 +1,7 @@  BepInEx support library for Il2Cpp games - netstandard2.1 + net6.0 true BepInEx.IL2CPP BepInEx.IL2CPP @@ -24,14 +24,11 @@ - - - - - - + + + diff --git a/BepInEx.IL2CPP/Hook/IL2CPPDetourMethodPatcher.cs b/BepInEx.IL2CPP/Hook/IL2CPPDetourMethodPatcher.cs index 1179dba..24e7f05 100644 --- a/BepInEx.IL2CPP/Hook/IL2CPPDetourMethodPatcher.cs +++ b/BepInEx.IL2CPP/Hook/IL2CPPDetourMethodPatcher.cs @@ -7,12 +7,13 @@ using System.Runtime.InteropServices; using BepInEx.Logging; using HarmonyLib; using HarmonyLib.Public.Patching; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.Runtime; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo; using MonoMod.Cil; using MonoMod.RuntimeDetour; using MonoMod.Utils; -using UnhollowerBaseLib; -using UnhollowerBaseLib.Runtime; -using UnhollowerBaseLib.Runtime.VersionSpecific.MethodInfo; +using Logger = BepInEx.Logging.Logger; using ValueType = Il2CppSystem.ValueType; using Void = Il2CppSystem.Void; @@ -21,16 +22,16 @@ namespace BepInEx.IL2CPP.Hook; public unsafe class IL2CPPDetourMethodPatcher : MethodPatcher { private static readonly MethodInfo IL2CPPToManagedStringMethodInfo - = AccessTools.Method(typeof(UnhollowerBaseLib.IL2CPP), - nameof(UnhollowerBaseLib.IL2CPP.Il2CppStringToManaged)); + = AccessTools.Method(typeof(Il2CppInterop.Runtime.IL2CPP), + nameof(Il2CppInterop.Runtime.IL2CPP.Il2CppStringToManaged)); private static readonly MethodInfo ManagedToIL2CPPStringMethodInfo - = AccessTools.Method(typeof(UnhollowerBaseLib.IL2CPP), - nameof(UnhollowerBaseLib.IL2CPP.ManagedStringToIl2Cpp)); + = AccessTools.Method(typeof(Il2CppInterop.Runtime.IL2CPP), + nameof(Il2CppInterop.Runtime.IL2CPP.ManagedStringToIl2Cpp)); private static readonly MethodInfo ObjectBaseToPtrMethodInfo - = AccessTools.Method(typeof(UnhollowerBaseLib.IL2CPP), - nameof(UnhollowerBaseLib.IL2CPP.Il2CppObjectBaseToPtr)); + = AccessTools.Method(typeof(Il2CppInterop.Runtime.IL2CPP), + nameof(Il2CppInterop.Runtime.IL2CPP.Il2CppObjectBaseToPtr)); private static readonly MethodInfo ReportExceptionMethodInfo = AccessTools.Method(typeof(IL2CPPDetourMethodPatcher), nameof(ReportException)); @@ -78,12 +79,12 @@ public unsafe class IL2CPPDetourMethodPatcher : MethodPatcher { try { - var methodField = UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(Original); + var methodField = Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(Original); if (methodField == null) { var fieldInfoField = - UnhollowerUtils.GetIl2CppFieldInfoPointerFieldForGeneratedFieldAccessor(Original); + Il2CppInteropUtils.GetIl2CppFieldInfoPointerFieldForGeneratedFieldAccessor(Original); if (fieldInfoField != null) throw new @@ -95,7 +96,7 @@ public unsafe class IL2CPPDetourMethodPatcher : MethodPatcher // Get the native MethodInfo struct for the target method originalNativeMethodInfo = - UnityVersionHandler.Wrap((Il2CppMethodInfo*)(IntPtr)methodField.GetValue(null)); + UnityVersionHandler.Wrap((Il2CppMethodInfo*) (IntPtr) methodField.GetValue(null)); // Create a trampoline from the original target method var trampolinePtr = @@ -159,16 +160,16 @@ public unsafe class IL2CPPDetourMethodPatcher : MethodPatcher // Remove il2cpp_object_get_virtual_method if (cursor.TryGotoNext(x => x.MatchLdarg(0), - x => x.MatchCall(typeof(UnhollowerBaseLib.IL2CPP), - nameof(UnhollowerBaseLib.IL2CPP.Il2CppObjectBaseToPtr)), + x => x.MatchCall(typeof(Il2CppInterop.Runtime.IL2CPP), + nameof(Il2CppInterop.Runtime.IL2CPP.Il2CppObjectBaseToPtr)), x => x.MatchLdsfld(out _), - x => x.MatchCall(typeof(UnhollowerBaseLib.IL2CPP), - nameof(UnhollowerBaseLib.IL2CPP.il2cpp_object_get_virtual_method)))) + x => x.MatchCall(typeof(Il2CppInterop.Runtime.IL2CPP), + nameof(Il2CppInterop.Runtime.IL2CPP.il2cpp_object_get_virtual_method)))) cursor.RemoveRange(4); else cursor.Goto(0) .GotoNext(x => - x.MatchLdsfld(UnhollowerUtils + x.MatchLdsfld(Il2CppInteropUtils .GetIl2CppMethodInfoPointerFieldForGeneratedMethod(Original))) .Remove(); @@ -199,7 +200,7 @@ public unsafe class IL2CPPDetourMethodPatcher : MethodPatcher private DynamicMethodDefinition GenerateNativeToManagedTrampoline(MethodInfo targetManagedMethodInfo) { - // managedParams are the unhollower types used on the managed side + // managedParams are the interop types used on the managed side // unmanagedParams are IntPtr references that are used by IL2CPP compiled assembly var paramStartIndex = 0; @@ -240,7 +241,7 @@ public unsafe class IL2CPPDetourMethodPatcher : MethodPatcher il.BeginExceptionBlock(); // Declare a list of variables to dereference back to the original pointers. - // This is required due to the needed unhollower type conversions, so we can't directly pass some addresses as byref types + // This is required due to the needed interop type conversions, so we can't directly pass some addresses as byref types var indirectVariables = new LocalBuilder[managedParams.Length]; if (!Original.IsStatic) @@ -283,8 +284,9 @@ public unsafe class IL2CPPDetourMethodPatcher : MethodPatcher if (hasReturnBuffer) { uint align = 0; - var size = UnhollowerBaseLib.IL2CPP.il2cpp_class_value_size(Il2CppTypeToClassPointer(managedReturnType), - ref align); + var size = + Il2CppInterop.Runtime.IL2CPP.il2cpp_class_value_size(Il2CppTypeToClassPointer(managedReturnType), + ref align); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldloc, managedReturnVariable); @@ -326,14 +328,16 @@ public unsafe class IL2CPPDetourMethodPatcher : MethodPatcher // Struct that's passed on the stack => handle as general struct uint align = 0; var fixedSize = - UnhollowerBaseLib.IL2CPP.il2cpp_class_value_size(Il2CppTypeToClassPointer(managedType), ref align); + Il2CppInterop.Runtime.IL2CPP.il2cpp_class_value_size(Il2CppTypeToClassPointer(managedType), ref align); return GetFixedSizeStructType(fixedSize); } else if (managedType == typeof(string) || managedType.IsSubclassOf(typeof(Il2CppObjectBase)) ) // General reference type { return typeof(IntPtr); - } else if(managedType == typeof(bool)) { + } + else if (managedType == typeof(bool)) + { // bool is byte in Il2Cpp, but int in CLR => force size to be correct return typeof(byte); } @@ -353,8 +357,8 @@ public unsafe class IL2CPPDetourMethodPatcher : MethodPatcher { if (type == typeof(void)) return Il2CppClassPointerStore.NativeClassPtr; - return (IntPtr)typeof(Il2CppClassPointerStore<>).MakeGenericType(type).GetField("NativeClassPtr") - .GetValue(null); + return (IntPtr) typeof(Il2CppClassPointerStore<>).MakeGenericType(type).GetField("NativeClassPtr") + .GetValue(null); } private static void EmitConvertArgumentToManaged(ILGenerator il, @@ -373,8 +377,8 @@ public unsafe class IL2CPPDetourMethodPatcher : MethodPatcher // We don't handle byref structs on x86 yet but we're yet to encounter those il.Emit(Environment.Is64BitProcess ? OpCodes.Ldarg : OpCodes.Ldarga_S, argIndex); il.Emit(OpCodes.Call, - AccessTools.Method(typeof(UnhollowerBaseLib.IL2CPP), - nameof(UnhollowerBaseLib.IL2CPP.il2cpp_value_box))); + AccessTools.Method(typeof(Il2CppInterop.Runtime.IL2CPP), + nameof(Il2CppInterop.Runtime.IL2CPP.il2cpp_value_box))); } else { diff --git a/BepInEx.IL2CPP/Hook/UnhollowerDetourHandler.cs b/BepInEx.IL2CPP/Hook/UnhollowerDetourHandler.cs index ab68d51..8f2a6e5 100644 --- a/BepInEx.IL2CPP/Hook/UnhollowerDetourHandler.cs +++ b/BepInEx.IL2CPP/Hook/UnhollowerDetourHandler.cs @@ -1,5 +1,5 @@ using System; -using UnhollowerRuntimeLib; +using Il2CppInterop.Runtime.Injection; namespace BepInEx.IL2CPP.Hook; diff --git a/BepInEx.IL2CPP/IL2CPPChainloader.cs b/BepInEx.IL2CPP/IL2CPPChainloader.cs index a9c6f36..6529dc1 100644 --- a/BepInEx.IL2CPP/IL2CPPChainloader.cs +++ b/BepInEx.IL2CPP/IL2CPPChainloader.cs @@ -12,10 +12,9 @@ using BepInEx.Logging; using BepInEx.Preloader.Core; using BepInEx.Preloader.Core.Logging; using HarmonyLib.Public.Patching; -using Il2Cpp.TlsAdapter; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.Injection; using MonoMod.Utils; -using UnhollowerBaseLib; -using UnhollowerRuntimeLib; using UnityEngine; using Logger = BepInEx.Logging.Logger; @@ -24,7 +23,6 @@ namespace BepInEx.IL2CPP; public class IL2CPPChainloader : BaseChainloader { private static RuntimeInvokeDetourDelegate originalInvoke; - private static InstallUnityTlsInterfaceDelegate originalInstallUnityTlsInterface; private static readonly ConfigEntry ConfigUnityLogging = ConfigFile.CoreConfig.Bind( "Logging", "UnityLogListening", @@ -37,7 +35,6 @@ public class IL2CPPChainloader : BaseChainloader "Include unity log messages in log file output."); private static FastNativeDetour RuntimeInvokeDetour { get; set; } - private static FastNativeDetour InstallUnityTlsInterfaceDetour { get; set; } public static IL2CPPChainloader Instance { get; set; } @@ -57,11 +54,12 @@ public class IL2CPPChainloader : BaseChainloader public override void Initialize(string gameExePath = null) { - GeneratedDatabasesUtil.DatabasesLocationOverride = Preloader.IL2CPPUnhollowedPath; PatchManager.ResolvePatcher += IL2CPPDetourMethodPatcher.TryResolve; base.Initialize(gameExePath); Instance = this; + Logger.Log(LogLevel.Info, $"Runtime version: {Environment.Version}"); + Logger.Log(LogLevel.Info, $"Runtime information: {RuntimeInformation.FrameworkDescription}"); ClassInjector.Detour = new UnhollowerDetourHandler(); @@ -82,30 +80,12 @@ public class IL2CPPChainloader : BaseChainloader FastNativeDetour.CreateAndApply(runtimeInvokePtr, OnInvokeMethod, out originalInvoke, CallingConvention.Cdecl); - if (gameAssemblyModule.BaseAddress.TryGetFunction("il2cpp_unity_install_unitytls_interface", - out var installTlsPtr)) - InstallUnityTlsInterfaceDetour = - FastNativeDetour.CreateAndApply(installTlsPtr, OnInstallUnityTlsInterface, - out originalInstallUnityTlsInterface, CallingConvention.Cdecl); - - Logger.Log(LogLevel.Debug, "Initializing TLS adapters"); - Il2CppTlsAdapter.Initialize(); - PreloaderLogger.Log.Log(LogLevel.Debug, "Runtime invoke patched"); } - private static void OnInstallUnityTlsInterface(IntPtr unityTlsInterfaceStruct) - { - Logger.Log(LogLevel.Debug, $"Captured UnityTls interface at {unityTlsInterfaceStruct.ToInt64():x8}"); - Il2CppTlsAdapter.Options.UnityTlsInterface = unityTlsInterfaceStruct; - originalInstallUnityTlsInterface(unityTlsInterfaceStruct); - InstallUnityTlsInterfaceDetour.Dispose(); - InstallUnityTlsInterfaceDetour = null; - } - private static IntPtr OnInvokeMethod(IntPtr method, IntPtr obj, IntPtr parameters, IntPtr exc) { - var methodName = Marshal.PtrToStringAnsi(UnhollowerBaseLib.IL2CPP.il2cpp_method_get_name(method)); + var methodName = Marshal.PtrToStringAnsi(Il2CppInterop.Runtime.IL2CPP.il2cpp_method_get_name(method)); var unhook = false; @@ -157,7 +137,7 @@ public class IL2CPPChainloader : BaseChainloader { var type = pluginAssembly.GetType(pluginInfo.TypeName); - var pluginInstance = (BasePlugin)Activator.CreateInstance(type); + var pluginInstance = (BasePlugin) Activator.CreateInstance(type); pluginInstance.Load(); @@ -166,7 +146,4 @@ public class IL2CPPChainloader : BaseChainloader [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate IntPtr RuntimeInvokeDetourDelegate(IntPtr method, IntPtr obj, IntPtr parameters, IntPtr exc); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void InstallUnityTlsInterfaceDelegate(IntPtr unityTlsInterfaceStruct); } diff --git a/BepInEx.IL2CPP/Il2CppInteropManager.cs b/BepInEx.IL2CPP/Il2CppInteropManager.cs new file mode 100644 index 0000000..63b4860 --- /dev/null +++ b/BepInEx.IL2CPP/Il2CppInteropManager.cs @@ -0,0 +1,283 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Net.Http; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using BepInEx.Configuration; +using BepInEx.Logging; +using Cpp2IL.Core; +using HarmonyLib; +using Il2CppInterop.Generator; +using Il2CppInterop.Runtime; +using LibCpp2IL; +using Mono.Cecil; + +namespace BepInEx.IL2CPP; + +internal static class Il2CppInteropManager +{ + private static readonly ConfigEntry UpdateInteropAssemblies = + ConfigFile.CoreConfig.Bind("IL2CPP", + "UpdateInteropAssemblies", + true, + new StringBuilder() + .AppendLine("Whether to run Il2CppInterop automatically to generate Il2Cpp support assemblies when they are outdated.") + .AppendLine("If disabled assemblies in `BepInEx/interop` won't be updated between game or BepInEx updates!") + .ToString()); + + private static readonly ConfigEntry UnityBaseLibrariesSource = ConfigFile.CoreConfig.Bind( + "IL2CPP", "UnityBaseLibrariesSource", + "https://unity.bepinex.dev/libraries/{VERSION}.zip", + new StringBuilder() + .AppendLine("URL to the ZIP of managed Unity base libraries.") + .AppendLine("The base libraries are used by Il2CppInterop to generate unhollowed Unity assemblies.") + .AppendLine("The URL can include {VERSION} template which will be replaced with the game's Unity engine version.") + .ToString()); + + private static readonly ConfigEntry DumpDummyAssemblies = ConfigFile.CoreConfig.Bind( + "IL2CPP", "DumpDummyAssemblies", + false, + "If enabled, BepInEx will save dummy assemblies generated by an Cpp2IL dumper into BepInEx/dummy."); + + private static readonly ManualLogSource Logger = BepInEx.Logging.Logger.CreateLogSource("InteropManager"); + + public static string GameAssemblyPath => Path.Combine(Paths.GameRootPath, "GameAssembly.dll"); + + private static string HashPath => Path.Combine(IL2CPPInteropAssemblyPath, "assembly-hash.txt"); + + private static string UnityBaseLibsDirectory => Path.Combine(Paths.BepInExRootPath, "unity-libs"); + + internal static string IL2CPPInteropAssemblyPath => Path.Combine(Paths.BepInExRootPath, "interop"); + + private static string ComputeHash() + { + using var md5 = MD5.Create(); + + static void HashFile(ICryptoTransform hash, string file) + { + const int defaultCopyBufferSize = 81920; + using var fs = File.OpenRead(file); + var buffer = new byte[defaultCopyBufferSize]; + int read; + while ((read = fs.Read(buffer)) > 0) + hash.TransformBlock(buffer, 0, read, buffer, 0); + } + + static void HashString(ICryptoTransform hash, string str) + { + var buffer = Encoding.UTF8.GetBytes(str); + hash.TransformBlock(buffer, 0, buffer.Length, buffer, 0); + } + + HashFile(md5, GameAssemblyPath); + + if (Directory.Exists(UnityBaseLibsDirectory)) + foreach (var file in Directory.EnumerateFiles(UnityBaseLibsDirectory, "*.dll", + SearchOption.TopDirectoryOnly)) + { + HashString(md5, Path.GetFileName(file)); + HashFile(md5, file); + } + + // Hash some common dependencies as they can affect output + HashString(md5, typeof(InteropAssemblyGenerator).Assembly.GetName().Version.ToString()); + HashString(md5, typeof(Cpp2IlApi).Assembly.GetName().Version.ToString()); + + md5.TransformFinalBlock(new byte[0], 0, 0); + + return Utility.ByteArrayToString(md5.Hash); + } + + private static bool CheckIfGenerationRequired() + { + static bool NeedGenerationOrSkip() + { + if (!UpdateInteropAssemblies.Value) + { + var hash = ComputeHash(); + Logger.LogWarning($"Interop assemblies are possibly out of date. To disable this message, create file {HashPath} with the following contents: {hash}"); + return false; + } + + return true; + } + + if (!Directory.Exists(IL2CPPInteropAssemblyPath)) + return true; + + if (!File.Exists(HashPath)) + return NeedGenerationOrSkip(); + + if (ComputeHash() != File.ReadAllText(HashPath) && NeedGenerationOrSkip()) + { + Logger.LogInfo("Detected outdated interop assemblies, will regenerate them now"); + return true; + } + + return false; + } + + private static Assembly ResolveInteropAssemblies(object sender, ResolveEventArgs args) + { + var assemblyName = new AssemblyName(args.Name); + return Utility.TryResolveDllAssembly(assemblyName, IL2CPPInteropAssemblyPath, out var foundAssembly) + ? foundAssembly + : null; + } + + public static void Initialize() + { + GeneratedDatabasesUtil.DatabasesLocationOverride = IL2CPPInteropAssemblyPath; + + var interopLogger = BepInEx.Logging.Logger.CreateLogSource("Il2CppInterop"); + Il2CppInterop.Runtime.Logger.InfoHandler += s => interopLogger.LogInfo(s.Trim()); + Il2CppInterop.Runtime.Logger.WarningHandler += s => interopLogger.LogWarning(s.Trim()); + Il2CppInterop.Runtime.Logger.TraceHandler += s => interopLogger.LogDebug(s.Trim()); + Il2CppInterop.Runtime.Logger.ErrorHandler += s => interopLogger.LogError(s.Trim()); + + AppDomain.CurrentDomain.AssemblyResolve += ResolveInteropAssemblies; + + if (!CheckIfGenerationRequired()) + return; + + try + { + Directory.CreateDirectory(IL2CPPInteropAssemblyPath); + Directory.EnumerateFiles(IL2CPPInteropAssemblyPath, "*.dll").Do(File.Delete); + + AppDomain.CurrentDomain.AddCecilPlatformAssemblies(UnityBaseLibsDirectory); + var unityVersion = Preloader.UnityVersion.ToString(3); + DownloadUnityAssemblies(unityVersion); + var dummyAssemblies = RunCpp2Il(); + + if (DumpDummyAssemblies.Value) + { + var dummyPath = Path.Combine(Paths.BepInExRootPath, "dummy"); + Directory.CreateDirectory(dummyPath); + foreach (var assemblyDefinition in dummyAssemblies) + assemblyDefinition.Write(Path.Combine(dummyPath, $"{assemblyDefinition.Name.Name}.dll")); + } + + RunIl2CppInteropGenerator(dummyAssemblies); + + File.WriteAllText(HashPath, ComputeHash()); + } + catch (Exception e) + { + Logger.LogError($"Failed to generate Il2Cpp interop assemblies: {e}"); + } + } + + private static void DownloadUnityAssemblies(string unityVersion) + { + var source = + UnityBaseLibrariesSource.Value.Replace("{VERSION}", unityVersion); + + if (!string.IsNullOrEmpty(source)) + { + Logger.LogMessage("Downloading unity base libraries"); + + Directory.CreateDirectory(UnityBaseLibsDirectory); + Directory.EnumerateFiles(UnityBaseLibsDirectory, "*.dll").Do(File.Delete); + + using var httpClient = new HttpClient(); + using var zipStream = httpClient.GetStreamAsync(source).GetAwaiter().GetResult(); + using var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Read); + + Logger.LogMessage("Extracting downloaded unity base libraries"); + zipArchive.ExtractToDirectory(UnityBaseLibsDirectory); + } + } + + private static List RunCpp2Il() + { + Logger.LogMessage("Running Cpp2IL to generate dummy assemblies"); + + + var metadataPath = Path.Combine(Paths.GameRootPath, + $"{Paths.ProcessName}_Data", + "il2cpp_data", + "Metadata", + "global-metadata.dat"); + + List sourceAssemblies; + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + var cpp2IlLogger = BepInEx.Logging.Logger.CreateLogSource("Cpp2IL"); + + Cpp2IL.Core.Logger.VerboseLog += (message, s) => + cpp2IlLogger.LogDebug($"[{s}] {message.Trim()}"); + Cpp2IL.Core.Logger.InfoLog += (message, s) => + cpp2IlLogger.LogInfo($"[{s}] {message.Trim()}"); + Cpp2IL.Core.Logger.WarningLog += (message, s) => + cpp2IlLogger.LogWarning($"[{s}] {message.Trim()}"); + Cpp2IL.Core.Logger.ErrorLog += (message, s) => + cpp2IlLogger.LogError($"[{s}] {message.Trim()}"); + + var cpp2IlUnityVersion = + Cpp2IlApi.DetermineUnityVersion(Paths.ExecutablePath, + Path.Combine(Paths.GameRootPath, $"{Paths.ProcessName}_Data")); + Cpp2IlApi.InitializeLibCpp2Il(GameAssemblyPath, metadataPath, cpp2IlUnityVersion, false); + sourceAssemblies = Cpp2IlApi.MakeDummyDLLs(); + Cpp2IlApi.RunAttributeRestorationForAllAssemblies(null, + LibCpp2IlMain.MetadataVersion >= 29 || + LibCpp2IlMain.Binary!.InstructionSet is InstructionSet.X86_32 + or InstructionSet.X86_64); + Cpp2IlApi.DisposeAndCleanupAll(); + + stopwatch.Stop(); + Logger.LogInfo($"Cpp2IL finished in {stopwatch.Elapsed}"); + + return sourceAssemblies; + } + + private static void RunIl2CppInteropGenerator(List sourceAssemblies) + { + var opts = new GeneratorOptions + { + GameAssemblyPath = GameAssemblyPath, + Source = sourceAssemblies, + OutputDir = IL2CPPInteropAssemblyPath, + UnityBaseLibsDir = Directory.Exists(UnityBaseLibsDirectory) ? UnityBaseLibsDirectory : null, + NoCopyRuntimeLibs = true + }; + + var renameMapLocation = Path.Combine(Paths.BepInExRootPath, "DeobfuscationMap.csv.gz"); + if (File.Exists(renameMapLocation)) + { + Logger.LogInfo("Parsing deobfuscation rename mappings"); + + using var fileStream = + new FileStream(renameMapLocation, FileMode.Open, FileAccess.Read, FileShare.Read); + using var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress); + + using var reader = new StreamReader(gzipStream, Encoding.UTF8, false); + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + + if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#")) + continue; + + var mapping = line.Split(';'); + + if (mapping.Length != 2) + continue; + + opts.RenameMap[mapping[0]] = mapping[1]; + } + } + + Logger.LogInfo("Generating interop assemblies"); + + InteropAssemblyGenerator.GenerateInteropAssemblies(opts); + + sourceAssemblies.Do(x => x.Dispose()); + } +} diff --git a/BepInEx.IL2CPP/Logging/IL2CPPLogSource.cs b/BepInEx.IL2CPP/Logging/IL2CPPLogSource.cs index c640f32..b2e46f3 100644 --- a/BepInEx.IL2CPP/Logging/IL2CPPLogSource.cs +++ b/BepInEx.IL2CPP/Logging/IL2CPPLogSource.cs @@ -9,7 +9,7 @@ public class IL2CPPLogSource : ILogSource public IL2CPPLogSource() { var loggerPointer = Marshal.GetFunctionPointerForDelegate(new IL2CPPLogCallbackDelegate(IL2CPPLogCallback)); - UnhollowerBaseLib.IL2CPP.il2cpp_register_log_callback(loggerPointer); + Il2CppInterop.Runtime.IL2CPP.il2cpp_register_log_callback(loggerPointer); } public string SourceName { get; } = "IL2CPP"; diff --git a/BepInEx.IL2CPP/Logging/IL2CPPUnityLogSource.cs b/BepInEx.IL2CPP/Logging/IL2CPPUnityLogSource.cs index b46f6df..dba3df6 100644 --- a/BepInEx.IL2CPP/Logging/IL2CPPUnityLogSource.cs +++ b/BepInEx.IL2CPP/Logging/IL2CPPUnityLogSource.cs @@ -10,9 +10,9 @@ public class IL2CPPUnityLogSource : ILogSource { Application.s_LogCallbackHandler = new Action(UnityLogCallback); - UnhollowerBaseLib.IL2CPP - .ResolveICall< - SetLogCallbackDefinedDelegate>("UnityEngine.Application::SetLogCallbackDefined")(true); + Il2CppInterop.Runtime.IL2CPP + .ResolveICall< + SetLogCallbackDefinedDelegate>("UnityEngine.Application::SetLogCallbackDefined")(true); } public string SourceName { get; } = "Unity"; diff --git a/BepInEx.IL2CPP/Preloader.cs b/BepInEx.IL2CPP/Preloader.cs index b551802..1181ac7 100644 --- a/BepInEx.IL2CPP/Preloader.cs +++ b/BepInEx.IL2CPP/Preloader.cs @@ -7,8 +7,7 @@ using BepInEx.Preloader.Core; using BepInEx.Preloader.Core.Logging; using BepInEx.Preloader.Core.Patching; using BepInEx.Preloader.RuntimeFixes; -using UnhollowerBaseLib; -using UnhollowerBaseLib.Runtime; +using Il2CppInterop.Runtime.Runtime; namespace BepInEx.IL2CPP; @@ -23,12 +22,9 @@ public static class Preloader #endregion - public static string IL2CPPUnhollowedPath => ProxyAssemblyGenerator.IL2CPPUnhollowedPath; - private static PreloaderConsoleListener PreloaderLog { get; set; } internal static ManualLogSource Log => PreloaderLogger.Log; - internal static ManualLogSource UnhollowerLog { get; set; } // TODO: This is not needed, maybe remove? (Instance is saved in IL2CPPChainloader itself) private static IL2CPPChainloader Chainloader { get; set; } @@ -55,20 +51,11 @@ public static class Preloader ChainloaderLogHelper.PrintLogInfo(Log); Log.Log(LogLevel.Debug, $"Game executable path: {Paths.ExecutablePath}"); - Log.Log(LogLevel.Debug, $"Unhollowed assembly directory: {IL2CPPUnhollowedPath}"); + Log.Log(LogLevel.Debug, $"Unhollowed assembly directory: {Il2CppInteropManager.IL2CPPInteropAssemblyPath}"); Log.Log(LogLevel.Debug, $"BepInEx root path: {Paths.BepInExRootPath}"); - UnhollowerLog = Logger.CreateLogSource("Unhollower"); - LogSupport.InfoHandler += UnhollowerLog.LogInfo; - LogSupport.WarningHandler += UnhollowerLog.LogWarning; - LogSupport.TraceHandler += UnhollowerLog.LogDebug; - LogSupport.ErrorHandler += UnhollowerLog.LogError; - InitializeUnityVersion(); - - if (ProxyAssemblyGenerator.CheckIfGenerationRequired()) - ProxyAssemblyGenerator.GenerateAssemblies(); - + Il2CppInteropManager.Initialize(); UnityVersionHandler.Initialize(UnityVersion.Major, UnityVersion.Minor, UnityVersion.Build); using (var assemblyPatcher = new AssemblyPatcher()) @@ -77,7 +64,7 @@ public static class Preloader Log.LogInfo($"{assemblyPatcher.PatcherContext.PatcherPlugins.Count} patcher plugin{(assemblyPatcher.PatcherContext.PatcherPlugins.Count == 1 ? "" : "s")} loaded"); - assemblyPatcher.LoadAssemblyDirectories(IL2CPPUnhollowedPath); + assemblyPatcher.LoadAssemblyDirectories(Il2CppInteropManager.IL2CPPInteropAssemblyPath); Log.LogInfo($"{assemblyPatcher.PatcherContext.PatcherPlugins.Count} assemblies discovered"); diff --git a/BepInEx.IL2CPP/ProxyAssemblyGenerator.cs b/BepInEx.IL2CPP/ProxyAssemblyGenerator.cs deleted file mode 100644 index 644bdd5..0000000 --- a/BepInEx.IL2CPP/ProxyAssemblyGenerator.cs +++ /dev/null @@ -1,410 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.IO.Compression; -using System.Net.Http; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using AssemblyUnhollower; -using BepInEx.Configuration; -using BepInEx.Logging; -using Cpp2IL.Core; -using HarmonyLib; -using Il2CppDumper; -using LibCpp2IL; -using Mono.Cecil; -using UnhollowerBaseLib; -using Logger = BepInEx.Logging.Logger; - -namespace BepInEx.IL2CPP; - -internal static class ProxyAssemblyGenerator -{ - private static readonly ConfigEntry ConfigUpdateUnhollowedAssemblies = - ConfigFile.CoreConfig.Bind("IL2CPP", - "UpdateUnhollowedAssemblies", - true, - new StringBuilder() - .AppendLine("Whether to run Il2CppAssemblyUnhollower automatically to generate Il2Cpp support assemblies when they are outdated.") - .AppendLine("If disabled assemblies in `BepInEx/unhollowed` won't be updated between game or BepInEx updates!") - .ToString()); - - private static readonly ConfigEntry ConfigUnityBaseLibrariesSource = ConfigFile.CoreConfig.Bind( - "IL2CPP", "UnityBaseLibrariesSource", - "http://unity.bepinex.dev/libraries/{VERSION}.zip", - new StringBuilder() - .AppendLine("URL to the ZIP of managed Unity base libraries.") - .AppendLine("The base libraries are used by Il2CppUnhollower to generate unhollowed Unity assemblies") - .AppendLine("The URL template MUST use HTTP.") - .AppendLine("The URL can include {VERSION} template which will be replaced with the game's Unity engine version") - .ToString()); - - private static readonly ConfigEntry ConfigIl2CppDumperType = ConfigFile.CoreConfig.Bind( - "IL2CPP", "Il2CppDumperType", - IL2CPPDumperType.Cpp2IL, - new StringBuilder() - .AppendLine("The IL2CPP metadata dumper tool to use when generating dummy assemblies for Il2CppAssemblyUnhollower.") - .AppendLine("Il2CppDumper - Default. The traditional choice that has been used by BepInEx.") - .AppendLine("Cpp2IL - Experimental, may provide better results than Il2CppDumper. Required for use with BepInEx.MelonLoader.Loader.") - .ToString()); - - private static readonly ConfigEntry ConfigDumpDummyAssemblies = ConfigFile.CoreConfig.Bind( - "IL2CPP", "DumpDummyAssemblies", - false, - "If enabled, BepInEx will save dummy assemblies generated by an il2cpp dumper into BepInEx/dummy."); - - private static readonly ConfigEntry UnhollowedAssembliesPath = ConfigFile.CoreConfig.Bind( - "IL2CPP", "UnhollowedAssembliesPath", - "{BepInEx}", - new StringBuilder() - .AppendLine("The path to the folder where unhollowed assemblies are stored.") - .AppendLine("Supports the following placeholders:") - .AppendLine("{BepInEx} - Path to the BepInEx folder.") - .AppendLine("{ProcessName} - Name of the current process") - .ToString()); - - private static readonly ConfigEntry ConfigUnhollowerDeobfuscationRegex = ConfigFile.CoreConfig.Bind( - "IL2CPP", "UnhollowerDeobfuscationRegex", - string.Empty, - new StringBuilder() - .AppendLine("The RegEx string to pass to Il2CppAssemblyUnhollower for renaming obfuscated names.") - .AppendLine("All types and members matching this RegEx will get a name based on their signature,") - .AppendLine("resulting in names that persist after game updates.") - .ToString()); - - private static ManualLogSource Il2cppDumperLogger; - - private static string unhollowedBasePath; - - public static string GameAssemblyPath => Path.Combine(Paths.GameRootPath, "GameAssembly.dll"); - - private static string HashPath => Path.Combine(Preloader.IL2CPPUnhollowedPath, "assembly-hash.txt"); - - private static string UnhollowedBasePath - { - get - { - if (unhollowedBasePath != null) - return unhollowedBasePath; - var path = Utility.GetCommandLineArgValue("--unhollowed-path") ?? UnhollowedAssembliesPath.Value; - unhollowedBasePath = path.Replace("{BepInEx}", Paths.BepInExRootPath) - .Replace("{ProcessName}", Paths.ProcessName); - return unhollowedBasePath; - } - } - - private static string UnityBaseLibsDirectory => Path.Combine(UnhollowedBasePath, "unity-libs"); - - internal static string IL2CPPUnhollowedPath => Path.Combine(UnhollowedBasePath, "unhollowed"); - - private static string ComputeHash() - { - using var md5 = MD5.Create(); - - static void HashFile(ICryptoTransform hash, string file) - { - const int defaultCopyBufferSize = 81920; - using var fs = File.OpenRead(file); - var buffer = new byte[defaultCopyBufferSize]; - int read; - while ((read = fs.Read(buffer)) > 0) - hash.TransformBlock(buffer, 0, read, buffer, 0); - } - - static void HashString(ICryptoTransform hash, string str) - { - var buffer = Encoding.UTF8.GetBytes(str); - hash.TransformBlock(buffer, 0, buffer.Length, buffer, 0); - } - - HashFile(md5, GameAssemblyPath); - - if (Directory.Exists(UnityBaseLibsDirectory)) - foreach (var file in Directory.EnumerateFiles(UnityBaseLibsDirectory, "*.dll", - SearchOption.TopDirectoryOnly)) - { - HashString(md5, Path.GetFileName(file)); - HashFile(md5, file); - } - - // Hash some common dependencies as they can affect output - HashString(md5, typeof(UnhollowedAssemblyGenerator).Assembly.GetName().Version.ToString()); - HashString(md5, typeof(Cpp2IlApi).Assembly.GetName().Version.ToString()); - HashString(md5, typeof(Il2CppDumper.Il2CppDumper).Assembly.GetName().Version.ToString()); - - md5.TransformFinalBlock(new byte[0], 0, 0); - - return Utility.ByteArrayToString(md5.Hash); - } - - public static bool CheckIfGenerationRequired() - { - static bool NeedGenerationOrSkip() - { - if (!ConfigUpdateUnhollowedAssemblies.Value) - { - var hash = ComputeHash(); - Preloader.Log - .Log(LogLevel.Warning, - $"Unhollowed assemblies are possibly out of date. To disable this message, create file {HashPath} with the following contents: {hash}"); - return false; - } - - return true; - } - - if (!Directory.Exists(Preloader.IL2CPPUnhollowedPath)) - return true; - - if (!File.Exists(HashPath)) - return NeedGenerationOrSkip(); - - if (ComputeHash() != File.ReadAllText(HashPath) && NeedGenerationOrSkip()) - { - Preloader.Log.Log(LogLevel.Info, "Detected outdated proxy assemblies, will regenerate them now"); - return true; - } - - return false; - } - - public static void GenerateAssemblies() - { - Il2cppDumperLogger ??= Logger.CreateLogSource(ConfigIl2CppDumperType.Value == IL2CPPDumperType.Il2CppDumper - ? "Il2CppDumper" - : "Cpp2IL"); - - var domain = AppDomainHelper.CreateDomain("GeneratorDomain", new AppDomainHelper.AppDomainSetup - { - ApplicationBase = Paths.BepInExAssemblyDirectory - }); - - var runner = - (AppDomainRunner) AppDomainHelper.CreateInstanceAndUnwrap(domain, - typeof(AppDomainRunner).Assembly.FullName, - typeof(AppDomainRunner).FullName); - - runner.Setup(Paths.ExecutablePath, Paths.BepInExRootPath, Paths.ManagedPath); - var ok = runner.GenerateAssemblies(new AppDomainListener(), Preloader.UnityVersion.ToString(3), - ConfigIl2CppDumperType.Value); - - AppDomain.Unload(domain); - - if (ok) - File.WriteAllText(HashPath, ComputeHash()); - } - - internal enum IL2CPPDumperType - { - Il2CppDumper, - Cpp2IL - } - - private static class AppDomainHelper - { - static AppDomainHelper() - { - var appDomain = typeof(AppDomain); - var evidenceType = typeof(AppDomain).Assembly.GetType("System.Security.Policy.Evidence"); - AppDomainSetupType = typeof(AppDomain).Assembly.GetType("System.AppDomainSetup"); - CreateDomainInternal = - MethodInvoker.GetHandler(AccessTools.Method(appDomain, "CreateDomain", - new[] - { - typeof(string), evidenceType, AppDomainSetupType - })); - CreateInstanceAndUnwrap = - AccessTools.MethodDelegate>(AccessTools.Method(appDomain, - nameof(CreateInstanceAndUnwrap), new[] { typeof(string), typeof(string) })); - } - - public static Func CreateInstanceAndUnwrap { get; } - private static FastInvokeHandler CreateDomainInternal { get; } - private static Type AppDomainSetupType { get; } - - public static AppDomain CreateDomain(string name, AppDomainSetup setup) - { - var realSetup = AccessTools.CreateInstance(AppDomainSetupType); - Traverse.IterateProperties(setup, realSetup, (pSrc, pTgt) => pTgt.SetValue(pSrc.GetValue())); - return CreateDomainInternal(null, name, null, realSetup) as AppDomain; - } - - public class AppDomainSetup - { - public string ApplicationBase { get; set; } - } - } - - [Serializable] - private class AppDomainListener : MarshalByRefObject - { - public void DoPreloaderLog(object data, LogLevel level) => Preloader.Log.Log(level, data); - - public void DoDumperLog(object data, LogLevel level) => Il2cppDumperLogger.Log(level, data); - - public void DoUnhollowerLog(object data, LogLevel level) => Preloader.UnhollowerLog.Log(level, data); - } - - [Serializable] - private class AppDomainRunner : MarshalByRefObject - { - public void Setup(string executablePath, string bepinPath, string managedPath) - { - Paths.SetExecutablePath(executablePath, bepinPath, managedPath); - AppDomain.CurrentDomain.AddCecilPlatformAssemblies(Paths.ManagedPath); - AppDomain.CurrentDomain.AddCecilPlatformAssemblies(UnityBaseLibsDirectory); - } - - public bool GenerateAssemblies(AppDomainListener listener, - string unityVersion, - IL2CPPDumperType dumperType) - { - try - { - GenerateAssembliesInternal(listener, unityVersion, dumperType); - return true; - } - catch (Exception e) - { - listener.DoUnhollowerLog($"Failed to generate unhollowed assemblies: {e}", LogLevel.Fatal); - return false; - } - } - - private void GenerateAssembliesInternal(AppDomainListener listener, - string unityVersion, - IL2CPPDumperType dumperType) - { - var source = - ConfigUnityBaseLibrariesSource.Value.Replace("{VERSION}", unityVersion); - - if (!string.IsNullOrEmpty(source)) - { - listener.DoPreloaderLog("Downloading unity base libraries", LogLevel.Message); - - Directory.CreateDirectory(UnityBaseLibsDirectory); - Directory.EnumerateFiles(UnityBaseLibsDirectory, "*.dll").Do(File.Delete); - - using var httpClient = new HttpClient(); - using var zipStream = httpClient.GetStreamAsync(source).GetAwaiter().GetResult(); - using var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Read); - - listener.DoPreloaderLog("Extracting downloaded unity base libraries", LogLevel.Message); - zipArchive.ExtractToDirectory(UnityBaseLibsDirectory); - } - - listener.DoPreloaderLog("Generating Il2CppUnhollower assemblies", LogLevel.Message); - - Directory.CreateDirectory(Preloader.IL2CPPUnhollowedPath); - Directory.EnumerateFiles(Preloader.IL2CPPUnhollowedPath, "*.dll").Do(File.Delete); - - var metadataPath = Path.Combine(Paths.GameRootPath, - $"{Paths.ProcessName}_Data", - "il2cpp_data", - "Metadata", - "global-metadata.dat"); - - List sourceAssemblies; - - var stopwatch = new Stopwatch(); - stopwatch.Start(); - - if (dumperType == IL2CPPDumperType.Il2CppDumper) - { - listener.DoPreloaderLog("Generating Il2CppDumper intermediate assemblies", LogLevel.Info); - - Il2CppDumper.Il2CppDumper.Init(GameAssemblyPath, - metadataPath, - new Config - { - GenerateStruct = false, - GenerateDummyDll = true - }, - s => listener.DoDumperLog(s, LogLevel.Debug), - out var metadata, - out var il2Cpp); - - var executor = new Il2CppExecutor(metadata, il2Cpp); - var dummy = new DummyAssemblyGenerator(executor, true); - sourceAssemblies = dummy.Assemblies; - } - else // if (dumperType == IL2CPPDumperType.Cpp2IL) - { - Cpp2IL.Core.Logger.VerboseLog += (message, s) => - listener.DoDumperLog($"[{s}] {message.Trim()}", LogLevel.Debug); - Cpp2IL.Core.Logger.InfoLog += (message, s) => - listener.DoDumperLog($"[{s}] {message.Trim()}", LogLevel.Info); - Cpp2IL.Core.Logger.WarningLog += (message, s) => - listener.DoDumperLog($"[{s}] {message.Trim()}", LogLevel.Warning); - Cpp2IL.Core.Logger.ErrorLog += (message, s) => - listener.DoDumperLog($"[{s}] {message.Trim()}", LogLevel.Error); - - var cpp2IlUnityVersion = - Cpp2IlApi.DetermineUnityVersion(Paths.ExecutablePath, - Path.Combine(Paths.GameRootPath, $"{Paths.ProcessName}_Data")); - - Cpp2IlApi.InitializeLibCpp2Il(GameAssemblyPath, metadataPath, cpp2IlUnityVersion, false); - - sourceAssemblies = Cpp2IlApi.MakeDummyDLLs(); - - Cpp2IlApi.RunAttributeRestorationForAllAssemblies(null, - parallel: LibCpp2IlMain.MetadataVersion >= 29 || - LibCpp2IlMain.Binary!.InstructionSet is - InstructionSet.X86_32 - or InstructionSet.X86_64); - - Cpp2IlApi.DisposeAndCleanupAll(); - } - - stopwatch.Stop(); - listener.DoDumperLog("Total time: " + stopwatch.Elapsed, LogLevel.Info); - - if (ConfigDumpDummyAssemblies.Value) - { - var dummyPath = Path.Combine(Paths.BepInExRootPath, "dummy"); - Directory.CreateDirectory(dummyPath); - - foreach (var assemblyDefinition in sourceAssemblies) - assemblyDefinition.Write(Path.Combine(dummyPath, assemblyDefinition.Name.Name + ".dll")); - } - - var unhollowerOptions = new UnhollowerOptions - { - GameAssemblyPath = GameAssemblyPath, - MscorlibPath = Path.Combine(Paths.ManagedPath, "mscorlib.dll"), - Source = sourceAssemblies, - OutputDir = Preloader.IL2CPPUnhollowedPath, - UnityBaseLibsDir = Directory.Exists(UnityBaseLibsDirectory) ? UnityBaseLibsDirectory : null, - NoCopyUnhollowerLibs = true, - ObfuscatedNamesRegex = !string.IsNullOrEmpty(ConfigUnhollowerDeobfuscationRegex.Value) ? new Regex(ConfigUnhollowerDeobfuscationRegex.Value) : null - }; - - var renameMapLocation = Path.Combine(Paths.BepInExRootPath, "DeobfuscationMap.csv.gz"); - if (File.Exists(renameMapLocation)) - { - listener.DoPreloaderLog("Parsing deobfuscation rename mappings", LogLevel.Info); - unhollowerOptions.ReadRenameMap(renameMapLocation); - } - - listener.DoPreloaderLog("Executing Il2CppUnhollower generator", LogLevel.Info); - - LogSupport.InfoHandler += s => listener.DoUnhollowerLog(s.Trim(), LogLevel.Info); - LogSupport.WarningHandler += s => listener.DoUnhollowerLog(s.Trim(), LogLevel.Warning); - LogSupport.TraceHandler += s => listener.DoUnhollowerLog(s.Trim(), LogLevel.Debug); - LogSupport.ErrorHandler += s => listener.DoUnhollowerLog(s.Trim(), LogLevel.Error); - - try - { - UnhollowedAssemblyGenerator.GenerateUnhollowedAssemblies(unhollowerOptions); - } - catch (Exception e) - { - listener.DoUnhollowerLog($"Exception while unhollowing: {e}", LogLevel.Error); - } - - sourceAssemblies.Do(x => x.Dispose()); - } - } -} diff --git a/BepInEx.IL2CPP/UnityEngine/Input.cs b/BepInEx.IL2CPP/UnityEngine/Input.cs index 22edfb1..3cc325c 100644 --- a/BepInEx.IL2CPP/UnityEngine/Input.cs +++ b/BepInEx.IL2CPP/UnityEngine/Input.cs @@ -333,8 +333,8 @@ public enum KeyCode public static class Input { private static readonly GetKeyIntDelegate GetKeyInt_Value - = UnhollowerBaseLib.IL2CPP - .ResolveICall("UnityEngine.Input::GetKeyInt(UnityEngine.KeyCode)"); + = Il2CppInterop.Runtime.IL2CPP + .ResolveICall("UnityEngine.Input::GetKeyInt(UnityEngine.KeyCode)"); public static bool GetKeyInt(KeyCode key) => GetKeyInt_Value(key); diff --git a/BepInEx.IL2CPP/UnityPreloadRunner.cs b/BepInEx.IL2CPP/UnityPreloadRunner.cs index aaf821b..da13ab4 100644 --- a/BepInEx.IL2CPP/UnityPreloadRunner.cs +++ b/BepInEx.IL2CPP/UnityPreloadRunner.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -20,13 +19,13 @@ internal static class UnityPreloaderRunner EnvVars.DOORSTOP_DLL_SEARCH_DIRS); // Cecil 0.11 requires one to manually set up list of trusted assemblies for assembly resolving + // The main BCL path AppDomain.CurrentDomain.AddCecilPlatformAssemblies(Paths.ManagedPath); - - Preloader.IL2CPPUnhollowedPath = Path.Combine(Paths.BepInExRootPath, "unhollowed"); + // The parent path -> .NET has some extra managed DLLs in there + AppDomain.CurrentDomain.AddCecilPlatformAssemblies(Path.GetDirectoryName(Paths.ManagedPath)); AppDomain.CurrentDomain.AssemblyResolve += LocalResolve; - Preloader.Run(); } @@ -42,8 +41,7 @@ internal static class UnityPreloaderRunner if (Utility.TryResolveDllAssembly(assemblyName, Paths.BepInExAssemblyDirectory, out foundAssembly) || Utility.TryResolveDllAssembly(assemblyName, Paths.PatcherPluginPath, out foundAssembly) - || Utility.TryResolveDllAssembly(assemblyName, Paths.PluginPath, out foundAssembly) - || Utility.TryResolveDllAssembly(assemblyName, Preloader.IL2CPPUnhollowedPath, out foundAssembly)) + || Utility.TryResolveDllAssembly(assemblyName, Paths.PluginPath, out foundAssembly)) return foundAssembly; return null; diff --git a/BepInEx.IL2CPP/Utils/Collections/Il2CppManagedEnumerable.cs b/BepInEx.IL2CPP/Utils/Collections/Il2CppManagedEnumerable.cs index 6a0e070..dc8bd70 100644 --- a/BepInEx.IL2CPP/Utils/Collections/Il2CppManagedEnumerable.cs +++ b/BepInEx.IL2CPP/Utils/Collections/Il2CppManagedEnumerable.cs @@ -1,6 +1,6 @@ using System.Collections; +using Il2CppInterop.Runtime.Injection; using Il2CppSystem; -using UnhollowerRuntimeLib; using ArgumentNullException = System.ArgumentNullException; using IEnumerator = Il2CppSystem.Collections.IEnumerator; using IntPtr = System.IntPtr; diff --git a/BepInEx.IL2CPP/Utils/Collections/Il2CppManagedEnumerator.cs b/BepInEx.IL2CPP/Utils/Collections/Il2CppManagedEnumerator.cs index aa62c7c..391099a 100644 --- a/BepInEx.IL2CPP/Utils/Collections/Il2CppManagedEnumerator.cs +++ b/BepInEx.IL2CPP/Utils/Collections/Il2CppManagedEnumerator.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Reflection.Emit; using HarmonyLib; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.Injection; using Il2CppSystem; -using UnhollowerBaseLib; -using UnhollowerRuntimeLib; using ArgumentNullException = System.ArgumentNullException; using Il2CppIEnumerator = Il2CppSystem.Collections.IEnumerator; using IntPtr = System.IntPtr; @@ -67,7 +67,7 @@ public class Il2CppManagedEnumerator : Object il.Emit(OpCodes.Stloc, loc); il.Emit(OpCodes.Ldloca, loc); il.Emit(OpCodes.Call, - typeof(UnhollowerBaseLib.IL2CPP).GetMethod(nameof(UnhollowerBaseLib.IL2CPP.il2cpp_value_box))); + typeof(Il2CppInterop.Runtime.IL2CPP).GetMethod(nameof(Il2CppInterop.Runtime.IL2CPP.il2cpp_value_box))); il.Emit(OpCodes.Newobj, typeof(Object).GetConstructor(new[] { typeof(IntPtr) })); il.Emit(OpCodes.Ret); @@ -80,7 +80,7 @@ public class Il2CppManagedEnumerator : Object { var t = obj.GetType(); if (obj is string s) - return new Object(UnhollowerBaseLib.IL2CPP.ManagedStringToIl2Cpp(s)); + return new Object(Il2CppInterop.Runtime.IL2CPP.ManagedStringToIl2Cpp(s)); if (t.IsPrimitive) return GetValueBoxer(t)(obj); throw new NotSupportedException($"Type {t} cannot be converted directly to an Il2Cpp object"); diff --git a/BepInEx.IL2CPP/Utils/Il2CppUtils.cs b/BepInEx.IL2CPP/Utils/Il2CppUtils.cs index 7061619..0506e7b 100644 --- a/BepInEx.IL2CPP/Utils/Il2CppUtils.cs +++ b/BepInEx.IL2CPP/Utils/Il2CppUtils.cs @@ -1,6 +1,6 @@ using System; -using UnhollowerBaseLib; -using UnhollowerRuntimeLib; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.Injection; using UnityEngine; namespace BepInEx.IL2CPP.Utils;