BepInEx.IL2CPP: retarget to net6.0, switch to Il2CppInterop

This commit is contained in:
ghorsington committed 2022-08-14 17:18:58 +03:00
1 parent 514d273371
commit 57658bdbb7
16 files changed
+379 -523

No files matched your search

@@ -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)
+2 -1
View File
@@ -1,6 +1,7 @@
using BepInEx.Configuration;
using BepInEx.Logging;
using UnhollowerBaseLib;
using Il2CppInterop.Runtime;
using Logger = BepInEx.Logging.Logger;
namespace BepInEx.IL2CPP;
+4 -7
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>BepInEx support library for Il2Cpp games</Description>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AssemblyTitle>BepInEx.IL2CPP</AssemblyTitle>
<Product>BepInEx.IL2CPP</Product>
@@ -24,14 +24,11 @@
<ProjectReference Include="..\BepInEx.Preloader.Core\BepInEx.Preloader.Core.csproj" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="BepInEx.Il2Cpp.TlsAdapter" Version="1.0.1"/>
<PackageReference Include="HarmonyX" Version="2.10.0"/>
<PackageReference Include="Iced" Version="1.17.0"/>
<PackageReference Include="Il2CppAssemblyUnhollower.BaseLib" Version="0.4.33.3"/>
<PackageReference Include="Il2CppAssemblyUnhollower.BaseLib.Legacy" Version="0.4.33.3"/>
<PackageReference Include="Il2CppAssemblyUnhollower.Lib" Version="0.4.33.3"/>
<PackageReference Include="Il2CppDumper" Version="6.6.4"/>
<PackageReference Include="Il2CppReferenceLibs.Core" Version="2.0.0" IncludeAssets="compile" PrivateAssets="all"/>
<PackageReference Include="Il2CppInterop.Generator" Version="1.0.0-pre.1"/>
<PackageReference Include="Il2CppInterop.ReferenceLibs" Version="1.0.0-pre.1"/>
<PackageReference Include="Il2CppInterop.Runtime" Version="1.0.0-pre.1"/>
<PackageReference Include="MonoMod.RuntimeDetour" Version="22.5.1.1"/>
<PackageReference Include="Samboy063.Cpp2IL.Core" Version="2022.0.5"/>
</ItemGroup>
@@ -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<Void>.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
{
@@ -1,5 +1,5 @@
using System;
using UnhollowerRuntimeLib;
using Il2CppInterop.Runtime.Injection;
namespace BepInEx.IL2CPP.Hook;
+6 -29
View File
@@ -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<BasePlugin>
{
private static RuntimeInvokeDetourDelegate originalInvoke;
private static InstallUnityTlsInterfaceDelegate originalInstallUnityTlsInterface;
private static readonly ConfigEntry<bool> ConfigUnityLogging = ConfigFile.CoreConfig.Bind(
"Logging", "UnityLogListening",
@@ -37,7 +35,6 @@ public class IL2CPPChainloader : BaseChainloader<BasePlugin>
"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<BasePlugin>
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<BasePlugin>
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<BasePlugin>
{
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<BasePlugin>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr RuntimeInvokeDetourDelegate(IntPtr method, IntPtr obj, IntPtr parameters, IntPtr exc);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void InstallUnityTlsInterfaceDelegate(IntPtr unityTlsInterfaceStruct);
}
+283
View File
@@ -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<bool> 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<string> 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<bool> 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<AssemblyDefinition> 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<AssemblyDefinition> 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<AssemblyDefinition> 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());
}
}
+1 -1
View File
@@ -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";
@@ -10,9 +10,9 @@ public class IL2CPPUnityLogSource : ILogSource
{
Application.s_LogCallbackHandler = new Action<string, string, LogType>(UnityLogCallback);
UnhollowerBaseLib.IL2CPP
.ResolveICall<
SetLogCallbackDefinedDelegate>("UnityEngine.Application::SetLogCallbackDefined")(true);
Il2CppInterop.Runtime.IL2CPP
.ResolveICall<
SetLogCallbackDefinedDelegate>("UnityEngine.Application::SetLogCallbackDefined")(true);
}
public string SourceName { get; } = "Unity";
+4 -17
View File
@@ -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");
-410
View File
@@ -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<bool> 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<string> 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<IL2CPPDumperType> 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<bool> 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<string> 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<string> 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<Func<AppDomain, string, string, object>>(AccessTools.Method(appDomain,
nameof(CreateInstanceAndUnwrap), new[] { typeof(string), typeof(string) }));
}
public static Func<AppDomain, string, string, object> 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<AssemblyDefinition> 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());
}
}
}
+2 -2
View File
@@ -333,8 +333,8 @@ public enum KeyCode
public static class Input
{
private static readonly GetKeyIntDelegate GetKeyInt_Value
= UnhollowerBaseLib.IL2CPP
.ResolveICall<GetKeyIntDelegate>("UnityEngine.Input::GetKeyInt(UnityEngine.KeyCode)");
= Il2CppInterop.Runtime.IL2CPP
.ResolveICall<GetKeyIntDelegate>("UnityEngine.Input::GetKeyInt(UnityEngine.KeyCode)");
public static bool GetKeyInt(KeyCode key) => GetKeyInt_Value(key);
+4 -6
View File
@@ -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;
@@ -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;
@@ -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");
+2 -2
View File
@@ -1,6 +1,6 @@
using System;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Injection;
using UnityEngine;
namespace BepInEx.IL2CPP.Utils;