using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Net.Http; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using AsmResolver.DotNet; using AssetRipper.Primitives; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.IL2CPP.RuntimeFixes; using BepInEx.Logging; using BepInEx.Preloader.Core; using BepInEx.Preloader.Core.Logging; using BepInEx.Preloader.Core.Patching; using BepInEx.Preloader.RuntimeFixes; using BepInEx.Unity.Common; using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP.Hook; using BepInEx.Unity.IL2CPP.Hook.Dobby; using BepInEx.Unity.IL2CPP.Hook.Funchook; using BepInEx.Unity.IL2CPP.Logging; using BepInEx.Unity.IL2CPP.Utils; using BepInEx.Unity.IL2CPP.Utils.Collections; using Cpp2IL.Core; using Cpp2IL.Core.Api; using Cpp2IL.Core.InstructionSets; using Cpp2IL.Core.Logging; using Cpp2IL.Core.OutputFormats; using Cpp2IL.Core.ProcessingLayers; using HarmonyLib; using Il2CppInterop.Common; using Il2CppInterop.Common.Host; using Il2CppInterop.Generator; using Il2CppInterop.Generator.Runners; using Il2CppInterop.HarmonySupport; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.Startup; using Il2CppSystem; using Il2CppSystem.Collections; using LibCpp2IL; using Microsoft.CodeAnalysis; using Microsoft.Extensions.Logging; using Mono.Cecil; using MonoMod.RuntimeDetour; using MonoMod.Utils; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("BepInEx")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © 2022 BepInEx Team")] [assembly: AssemblyDescription("BepInEx support library for Il2Cpp games")] [assembly: AssemblyFileVersion("6.0.0.0")] [assembly: AssemblyInformationalVersion("6.0.0-be.741+88b4838fef3eac5baebd0824416246c9b0855394")] [assembly: AssemblyProduct("BepInEx.Unity.IL2CPP")] [assembly: AssemblyTitle("BepInEx.Unity.IL2CPP")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/BepInEx/BepInEx")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("6.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NativeIntegerAttribute : Attribute { public readonly bool[] TransformFlags; public NativeIntegerAttribute() { TransformFlags = new bool[1] { true }; } public NativeIntegerAttribute(bool[] P_0) { TransformFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Doorstop { internal static class Entrypoint { public static void Start() { string text = Environment.GetEnvironmentVariable("BEPINEX_PRELOADER_LOG") ?? $"preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"; Mutex mutex = null; try { EnvVars.LoadVars(); text = Path.Combine(Path.GetDirectoryName(EnvVars.DOORSTOP_PROCESS_PATH), text); string text2 = Utility.HashStrings(new string[3] { Process.GetCurrentProcess().ProcessName, EnvVars.DOORSTOP_PROCESS_PATH, typeof(Entrypoint).FullName }); mutex = new Mutex(initiallyOwned: false, "Global\\" + text2); mutex.WaitOne(); UnityPreloaderRunner.PreloaderMain(); } catch (Exception ex) { File.WriteAllText(text, ex.ToString()); try { if (PlatformHelper.Is((Platform)37)) { MessageBox.Show("Failed to start BepInEx", "BepInEx"); } else if (NotifySend.IsSupported) { NotifySend.Send("Failed to start BepInEx", "Check logs for details"); } else if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("BEPINEX_FAIL_FAST"))) { return; } } catch (Exception) { } Environment.Exit(1); } finally { mutex?.ReleaseMutex(); } } } } namespace BepInEx.IL2CPP.RuntimeFixes { internal static class RedirectStdErrFix { private const int STD_ERROR_HANDLE = -12; private const int INVALID_HANDLE_VALUE = -1; private const int FILE_SHARE_READ = 1; private const int GENERIC_WRITE = 1073741824; private const int CREATE_ALWAYS = 2; private const int FILE_ATTRIBUTE_NORMAL = 128; [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern nint CreateFile(string fileName, uint desiredAccess, int shareMode, nint securityAttributes, int creationDisposition, int flagsAndAttributes, nint templateFile); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool SetStdHandle(int nStdHandle, nint hConsoleOutput); public static void Apply() { if (PlatformHelper.Is((Platform)37)) { nint num = CreateFile(Path.Combine(Paths.BepInExRootPath, "ErrorLog.log"), 1073741824u, 1, 0, 2, 128, 0); if (num == -1) { Logger.Log((LogLevel)4, (object)"Failed to open error log file; skipping error redirection"); } else if (!SetStdHandle(-12, num)) { Logger.Log((LogLevel)4, (object)"Failed to redirect stderr; skipping error redirection"); } } } } } namespace BepInEx.Unity.IL2CPP { public abstract class BasePlugin { public ManualLogSource Log { get; } public ConfigFile Config { get; } protected BasePlugin() { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown BepInPlugin metadata = MetadataHelper.GetMetadata((object)this); Log = Logger.CreateLogSource(metadata.Name); Config = new ConfigFile(Utility.CombinePaths(new string[2] { Paths.ConfigPath, metadata.GUID + ".cfg" }), false, metadata); } public abstract void Load(); public virtual bool Unload() { return false; } public T AddComponent() where T : Il2CppObjectBase { return IL2CPPChainloader.AddUnityComponent(); } } public class IL2CPPChainloader : BaseChainloader { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate IntPtr RuntimeInvokeDetourDelegate(IntPtr method, IntPtr obj, IntPtr parameters, IntPtr exc); private static RuntimeInvokeDetourDelegate originalInvoke; private static readonly ConfigEntry ConfigUnityLogging = ConfigFile.CoreConfig.Bind("Logging", "UnityLogListening", true, "Enables showing unity log messages in the BepInEx logging system."); private static readonly ConfigEntry ConfigDiskWriteUnityLog = ConfigFile.CoreConfig.Bind("Logging.Disk", "WriteUnityLog", false, "Include unity log messages in log file output."); private static INativeDetour RuntimeInvokeDetour { get; set; } public static IL2CPPChainloader Instance { get; set; } public event Action PluginLoad; public static T AddUnityComponent() where T : Il2CppObjectBase { return AddUnityComponent(typeof(T)).Cast(); } public static Il2CppObjectBase AddUnityComponent(Type t) { return Il2CppUtils.AddComponent(t); } public override void Initialize(string gameExePath = null) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) base.Initialize(gameExePath); Instance = this; if (!NativeLibrary.TryLoad("GameAssembly", typeof(IL2CPPChainloader).Assembly, null, out var handle)) { Logger.Log((LogLevel)1, (object)"Could not locate Il2Cpp game assembly (GameAssembly.dll, UserAssembly.dll or libil2cpp.so). The game might be obfuscated or use a yet unsupported build of Unity."); return; } nint export = NativeLibrary.GetExport(handle, "il2cpp_runtime_invoke"); ManualLogSource log = PreloaderLogger.Log; LogLevel val = (LogLevel)32; LogLevel val2 = val; bool flag = default(bool); BepInExLogInterpolatedStringHandler val3 = new BepInExLogInterpolatedStringHandler(26, 1, val, ref flag); if (flag) { val3.AppendLiteral("Runtime invoke pointer: 0x"); val3.AppendFormatted(((IntPtr)export).ToInt64(), "X"); } log.Log(val2, val3); RuntimeInvokeDetourDelegate to = OnInvokeMethod; RuntimeInvokeDetour = INativeDetour.CreateAndApply(export, to, out originalInvoke); PreloaderLogger.Log.Log((LogLevel)32, (object)"Runtime invoke patched"); } private static IntPtr OnInvokeMethod(IntPtr method, IntPtr obj, IntPtr parameters, IntPtr exc) { string? text = Marshal.PtrToStringAnsi(IL2CPP.il2cpp_method_get_name(method)); bool flag = false; if (text == "Internal_ActiveSceneChanged") { try { if (ConfigUnityLogging.Value) { Logger.Sources.Add((ILogSource)(object)new IL2CPPUnityLogSource()); Application.CallLogCallback("Test call after applying unity logging hook", "", (LogType)1, true); } flag = true; Il2CppInteropManager.PreloadInteropAssemblies(); ((BaseChainloader)Instance).Execute(); } catch (Exception ex) { Logger.Log((LogLevel)1, (object)"Unable to execute IL2CPP chainloader"); Logger.Log((LogLevel)2, (object)ex); } } IntPtr result = originalInvoke(method, obj, parameters, exc); if (flag) { RuntimeInvokeDetour.Dispose(); PreloaderLogger.Log.Log((LogLevel)32, (object)"Runtime invoke unpatched"); } return result; } protected override void InitializeLoggers() { base.InitializeLoggers(); if (!ConfigDiskWriteUnityLog.Value) { DiskLogListener.BlacklistedSources.Add("Unity"); } ChainloaderLogHelper.RewritePreloaderLogs(); Logger.Sources.Add((ILogSource)(object)new IL2CPPLogSource()); } public override BasePlugin LoadPlugin(PluginInfo pluginInfo, Assembly pluginAssembly) { BasePlugin basePlugin = (BasePlugin)Activator.CreateInstance(pluginAssembly.GetType(pluginInfo.TypeName)); this.PluginLoad?.Invoke(pluginInfo, pluginAssembly, basePlugin); basePlugin.Load(); return basePlugin; } } internal static class Il2CppInteropManager { private sealed class AsmToCecilConverter : IAssemblyResolver, IDisposable { private readonly Dictionary asmResolverDictionary; private readonly Dictionary cecilDictionary = new Dictionary(); private readonly Dictionary asmToCecil = new Dictionary(); public AsmToCecilConverter(List list) { asmResolverDictionary = list.ToDictionary((AssemblyDefinition a) => ((object)((AssemblyDescriptor)a).Name)?.ToString(), (AssemblyDefinition a) => a); } public void Dispose() { } public AssemblyDefinition Resolve(AssemblyNameReference name) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown return Resolve(name, new ReaderParameters { AssemblyResolver = (IAssemblyResolver)(object)this }); } public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) { string name2 = name.Name; if (!cecilDictionary.TryGetValue(name2, out var value) && asmResolverDictionary.TryGetValue(name2, out var value2)) { return Convert(value2, parameters); } return value; } public List ConvertAll() { List list = new List(asmResolverDictionary.Count); foreach (AssemblyDefinition value in asmResolverDictionary.Values) { AssemblyDefinition item = Convert(value); list.Add(item); } return list; } private AssemblyDefinition Convert(AssemblyDefinition asmResolverAssembly) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown return Convert(asmResolverAssembly, new ReaderParameters { AssemblyResolver = (IAssemblyResolver)(object)this }); } private AssemblyDefinition Convert(AssemblyDefinition asmResolverAssembly, ReaderParameters readerParameters) { if (asmToCecil.TryGetValue(asmResolverAssembly, out var value)) { return value; } MemoryStream memoryStream = new MemoryStream(); asmResolverAssembly.WriteManifest((Stream)memoryStream); memoryStream.Position = 0L; value = AssemblyDefinition.ReadAssembly((Stream)memoryStream, readerParameters); cecilDictionary.Add(((AssemblyNameReference)value.Name).Name, value); asmToCecil.Add(asmResolverAssembly, value); return value; } } private static readonly ConfigEntry UpdateInteropAssemblies; private static readonly ConfigEntry UnityBaseLibrariesSource; private static readonly ConfigEntry ConfigUnhollowerDeobfuscationRegex; private static readonly ConfigEntry ScanMethodRefs; private static readonly ConfigEntry DumpDummyAssemblies; private static readonly ConfigEntry IL2CPPInteropAssembliesPath; private static readonly ConfigEntry PreloadIL2CPPInteropAssemblies; private static readonly ConfigEntry GlobalMetadataPath; private static readonly ManualLogSource Logger; private static string il2cppInteropBasePath; private static bool initialized; public static string GameAssemblyPath => Environment.GetEnvironmentVariable("BEPINEX_GAME_ASSEMBLY_PATH") ?? Path.Combine(Paths.GameRootPath, "GameAssembly." + PlatformHelper.LibrarySuffix); private static string HashPath => Path.Combine(IL2CPPInteropAssemblyPath, "assembly-hash.txt"); private static string IL2CPPBasePath { get { if (il2cppInteropBasePath != null) { return il2cppInteropBasePath; } il2cppInteropBasePath = (Utility.GetCommandLineArgValue("--unhollowed-path") ?? IL2CPPInteropAssembliesPath.Value).Replace("{BepInEx}", Paths.BepInExRootPath).Replace("{ProcessName}", Paths.ProcessName); return il2cppInteropBasePath; } } private static string UnityBaseLibsDirectory => Path.Combine(IL2CPPBasePath, "unity-libs"); internal static string IL2CPPInteropAssemblyPath => Path.Combine(IL2CPPBasePath, "interop"); private static string RenameMapPath => Path.Combine(Paths.BepInExRootPath, "DeobfuscationMap.csv.gz"); private static ILoggerFactory LoggerFactory { get; } static Il2CppInteropManager() { 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()); 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 interop assemblies.").AppendLine("The URL can include {VERSION} template which will be replaced with the game's Unity engine version.") .ToString()); 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()); ScanMethodRefs = ConfigFile.CoreConfig.Bind("IL2CPP", "ScanMethodRefs", Environment.Is64BitProcess, "If enabled, Il2CppInterop will use xref to find dead methods and generate CallerCount attributes."); DumpDummyAssemblies = ConfigFile.CoreConfig.Bind("IL2CPP", "DumpDummyAssemblies", false, "If enabled, BepInEx will save dummy assemblies generated by an Cpp2IL dumper into BepInEx/dummy."); IL2CPPInteropAssembliesPath = ConfigFile.CoreConfig.Bind("IL2CPP", "IL2CPPInteropAssembliesPath", "{BepInEx}", new StringBuilder().AppendLine("The path to the folder where IL2CPPInterop assemblies are stored.").AppendLine("Supports the following placeholders:").AppendLine("{BepInEx} - Path to the BepInEx folder.") .AppendLine("{ProcessName} - Name of the current process") .ToString()); PreloadIL2CPPInteropAssemblies = ConfigFile.CoreConfig.Bind("IL2CPP", "PreloadIL2CPPInteropAssemblies", true, new StringBuilder().AppendLine("Automatically load all interop assemblies right before loading plugins.").AppendLine("Some plugins may not work properly without this, but it may cause issues in some games.").ToString()); GlobalMetadataPath = ConfigFile.CoreConfig.Bind("IL2CPP", "GlobalMetadataPath", "{GameDataPath}/il2cpp_data/Metadata/global-metadata.dat", new StringBuilder().AppendLine("The path to the IL2CPP metadata file.").AppendLine("Supports the following placeholders:").AppendLine("{BepInEx} - Path to the BepInEx folder.") .AppendLine("{ProcessName} - Name of the current process") .AppendLine("{GameDataPath} - Path to the game's Data folder.") .ToString()); Logger = Logger.CreateLogSource("InteropManager"); LoggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(delegate(ILoggingBuilder b) { b.AddProvider(new BepInExLoggerProvider()).SetMinimumLevel(LogLevel.Trace); }); InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.X86_32); InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.X86_64); LibCpp2IlBinaryRegistry.RegisterBuiltInBinarySupport(); } private static string ComputeHash() { using (MD5 mD = MD5.Create()) { HashFile(mD, GameAssemblyPath); if (Directory.Exists(UnityBaseLibsDirectory)) { foreach (string item in Directory.EnumerateFiles(UnityBaseLibsDirectory, "*.dll", SearchOption.TopDirectoryOnly)) { HashString(mD, Path.GetFileName(item)); HashFile(mD, item); } } if (File.Exists(RenameMapPath)) { HashFile(mD, RenameMapPath); } HashString(mD, typeof(InteropAssemblyGenerator).Assembly.GetName().Version.ToString()); HashString(mD, typeof(Cpp2IlApi).Assembly.GetName().Version.ToString()); mD.TransformFinalBlock(new byte[0], 0, 0); return Utility.ByteArrayToString(mD.Hash); } static void HashFile(ICryptoTransform hash, string file) { using FileStream fileStream = File.OpenRead(file); byte[] array = new byte[81920]; int inputCount; while ((inputCount = fileStream.Read(array)) > 0) { hash.TransformBlock(array, 0, inputCount, array, 0); } } static void HashString(ICryptoTransform hash, string str) { byte[] bytes = Encoding.UTF8.GetBytes(str); hash.TransformBlock(bytes, 0, bytes.Length, bytes, 0); } } private static bool CheckIfGenerationRequired() { if (!Directory.Exists(IL2CPPInteropAssemblyPath)) { return true; } if (!File.Exists(HashPath)) { return NeedGenerationOrSkip(); } if (ComputeHash() != File.ReadAllText(HashPath) && NeedGenerationOrSkip()) { Logger.LogInfo((object)"Detected outdated interop assemblies, will regenerate them now"); return true; } return false; static bool NeedGenerationOrSkip() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!UpdateInteropAssemblies.Value) { string text = ComputeHash(); bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(112, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Interop assemblies are possibly out of date. To disable this message, create file "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(HashPath); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" with the following contents: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text); } Logger.LogWarning(val); return false; } return true; } } private static Assembly ResolveInteropAssemblies(object sender, ResolveEventArgs args) { Assembly result = default(Assembly); if (!Utility.TryResolveDllAssembly(new AssemblyName(args.Name), IL2CPPInteropAssemblyPath, ref result)) { return null; } return result; } public static void Initialize() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown if (initialized) { throw new InvalidOperationException("Already initialized"); } initialized = true; Environment.SetEnvironmentVariable("IL2CPP_INTEROP_DATABASES_LOCATION", IL2CPPInteropAssemblyPath); AppDomain.CurrentDomain.AssemblyResolve += ResolveInteropAssemblies; GenerateInteropAssemblies(); ILogger logger = LoggerFactory.CreateLogger("Il2CppInterop"); UnityVersion version = UnityInfo.Version; RuntimeConfiguration val = new RuntimeConfiguration(); val.set_UnityVersion(new Version(((UnityVersion)(ref version)).Major, ((UnityVersion)(ref version)).Minor, ((UnityVersion)(ref version)).Build)); val.set_DetourProvider((IDetourProvider)(object)new Il2CppInteropDetourProvider()); ((BaseHost)HarmonySupport.AddHarmonySupport(LoggerExtensions.AddLogger(Il2CppInteropRuntime.Create(val), logger))).Start(); } private static void GenerateInteropAssemblies() { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Expected O, but got Unknown if (!CheckIfGenerationRequired()) { return; } try { Directory.CreateDirectory(IL2CPPInteropAssemblyPath); CollectionExtensions.Do(Directory.EnumerateFiles(IL2CPPInteropAssemblyPath, "*.dll"), (Action)File.Delete); Utility.AddCecilPlatformAssemblies(AppDomain.CurrentDomain, UnityBaseLibsDirectory); DownloadUnityAssemblies(); List list = RunCpp2Il(); if (DumpDummyAssemblies.Value) { string text = Path.Combine(Paths.BepInExRootPath, "dummy"); Directory.CreateDirectory(text); foreach (AssemblyDefinition item in list) { item.Write(Path.Combine(text, $"{((AssemblyDescriptor)item).Name}.dll")); } } RunIl2CppInteropGenerator(list); File.WriteAllText(HashPath, ComputeHash()); } catch (Exception ex) { bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(46, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to generate Il2Cpp interop assemblies: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex); } Logger.LogError(val); } } private static void DownloadUnityAssemblies() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown UnityVersion version = UnityInfo.Version; string newValue = $"{((UnityVersion)(ref version)).Major}.{((UnityVersion)(ref version)).Minor}.{((UnityVersion)(ref version)).Build}"; string text = UnityBaseLibrariesSource.Value.Replace("{VERSION}", newValue); if (string.IsNullOrEmpty(text)) { return; } string fileName = Path.GetFileName(new Uri(text).AbsolutePath); DirectoryInfo directoryInfo = Directory.CreateDirectory(UnityBaseLibsDirectory); CollectionExtensions.Do(directoryInfo.EnumerateFiles("*.dll"), (Action)delegate(FileInfo a) { a.Delete(); }); FileInfo fileInfo = directoryInfo.GetFiles(fileName).FirstOrDefault(); bool flag = default(bool); BepInExMessageLogInterpolatedStringHandler val; if (fileInfo != null) { val = new BepInExMessageLogInterpolatedStringHandler(39, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Reading unity base libraries from file "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text); } Logger.LogMessage(val); using FileStream stream = fileInfo.OpenRead(); using ZipArchive source = new ZipArchive(stream, ZipArchiveMode.Read); source.ExtractToDirectory(UnityBaseLibsDirectory); return; } val = new BepInExMessageLogInterpolatedStringHandler(33, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Downloading unity base libraries "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text); } Logger.LogMessage(val); using HttpClient httpClient = new HttpClient(); using Stream stream2 = httpClient.GetStreamAsync(text).GetAwaiter().GetResult(); Logger.LogMessage((object)"Extracting downloaded unity base libraries"); using ZipArchive source2 = new ZipArchive(stream2, ZipArchiveMode.Read); source2.ExtractToDirectory(UnityBaseLibsDirectory); } private static List RunCpp2Il() { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Expected O, but got Unknown string text = Path.Combine(Paths.GameRootPath, GlobalMetadataPath.Value.Replace("{BepInEx}", Paths.BepInExRootPath).Replace("{ProcessName}", Paths.ProcessName).Replace("{GameDataPath}", Paths.GameDataPath)); Logger.LogMessage((object)("Running Cpp2IL to generate dummy assemblies from " + text)); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); ManualLogSource cpp2IlLogger = Logger.CreateLogSource("Cpp2IL"); Logger.VerboseLog += (LogEvent)delegate(string message, string s) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown ManualLogSource obj4 = cpp2IlLogger; bool flag5 = default(bool); BepInExDebugLogInterpolatedStringHandler val5 = new BepInExDebugLogInterpolatedStringHandler(3, 2, ref flag5); if (flag5) { ((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("["); ((BepInExLogInterpolatedStringHandler)val5).AppendFormatted(s); ((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("] "); ((BepInExLogInterpolatedStringHandler)val5).AppendFormatted(message.Trim()); } obj4.LogDebug(val5); }; Logger.InfoLog += (LogEvent)delegate(string message, string s) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown ManualLogSource obj3 = cpp2IlLogger; bool flag4 = default(bool); BepInExInfoLogInterpolatedStringHandler val4 = new BepInExInfoLogInterpolatedStringHandler(3, 2, ref flag4); if (flag4) { ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("["); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted(s); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("] "); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted(message.Trim()); } obj3.LogInfo(val4); }; Logger.WarningLog += (LogEvent)delegate(string message, string s) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown ManualLogSource obj2 = cpp2IlLogger; bool flag3 = default(bool); BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(3, 2, ref flag3); if (flag3) { ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("["); ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted(s); ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("] "); ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted(message.Trim()); } obj2.LogWarning(val3); }; Logger.ErrorLog += (LogEvent)delegate(string message, string s) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown ManualLogSource obj = cpp2IlLogger; bool flag2 = default(bool); BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(3, 2, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("["); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(s); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("] "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(message.Trim()); } obj.LogError(val2); }; UnityVersion version = UnityInfo.Version; Cpp2IlApi.InitializeLibCpp2Il(GameAssemblyPath, text, version, false); List list = new List { (Cpp2IlProcessingLayer)new AttributeInjectorProcessingLayer() }; foreach (Cpp2IlProcessingLayer item in list) { item.PreProcess(Cpp2IlApi.CurrentAppContext, list); } foreach (Cpp2IlProcessingLayer item2 in list) { item2.Process(Cpp2IlApi.CurrentAppContext, (Action)null); } List result = ((AsmResolverDllOutputFormat)new AsmResolverDllOutputFormatDefault()).BuildAssemblies(Cpp2IlApi.CurrentAppContext); LibCpp2IlMain.Reset(); Cpp2IlApi.CurrentAppContext = null; stopwatch.Stop(); bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(19, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Cpp2IL finished in "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(stopwatch.Elapsed); } Logger.LogInfo(val); return result; } private static void RunIl2CppInteropGenerator(List sourceAssemblies) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown GeneratorOptions val = new GeneratorOptions { GameAssemblyPath = (ScanMethodRefs.Value ? GameAssemblyPath : null), Source = sourceAssemblies, OutputDir = IL2CPPInteropAssemblyPath, UnityBaseLibsDir = (Directory.Exists(UnityBaseLibsDirectory) ? UnityBaseLibsDirectory : null), ObfuscatedNamesRegex = ((!string.IsNullOrEmpty(ConfigUnhollowerDeobfuscationRegex.Value)) ? new Regex(ConfigUnhollowerDeobfuscationRegex.Value) : null) }; if (File.Exists(RenameMapPath)) { Logger.LogInfo((object)"Parsing deobfuscation rename mappings"); val.ReadRenameMap(RenameMapPath); } Logger.LogInfo((object)"Generating interop assemblies"); ILogger logger = LoggerFactory.CreateLogger("Il2CppInteropGen"); InteropAssemblyGenerator.AddInteropAssemblyGenerator(LoggerExtensions.AddLogger(Il2CppInteropGenerator.Create(val), logger)).Run(); } internal static void PreloadInteropAssemblies() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown if (!PreloadIL2CPPInteropAssemblies.Value) { return; } Stopwatch stopwatch = Stopwatch.StartNew(); IEnumerable source = Directory.EnumerateFiles(IL2CPPInteropAssemblyPath); int loaded = 0; Parallel.ForEach(source, delegate(string file) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown if (file.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); if (!fileNameWithoutExtension.Equals("netstandard", StringComparison.OrdinalIgnoreCase) && !fileNameWithoutExtension.Equals("Il2Cppnetstandard", StringComparison.OrdinalIgnoreCase)) { try { Assembly.Load(fileNameWithoutExtension); Interlocked.Increment(ref loaded); } catch (Exception ex) { bool flag2 = default(bool); BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(21, 2, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Failed to preload "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(file); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" - "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex); } Logger.LogWarning(val2); } } } }); bool flag = default(bool); BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(35, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Preloaded "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(loaded); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" interop assemblies in "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(stopwatch.ElapsedMilliseconds); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("ms"); } Logger.LogDebug(val); } } public static class Preloader { private static PreloaderConsoleListener PreloaderLog { get; set; } internal static ManualLogSource Log => PreloaderLogger.Log; private static IL2CPPChainloader Chainloader { get; set; } public static void Run() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Expected O, but got Unknown //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Expected O, but got Unknown //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Expected O, but got Unknown //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Expected O, but got Unknown //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Expected O, but got Unknown //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Expected O, but got Unknown try { HarmonyBackendFix.Initialize(); ConsoleSetOutFix.Apply(); UnityInfo.Initialize(Paths.ExecutablePath, Paths.GameDataPath); ConsoleManager.Initialize(false, true); PreloaderLog = new PreloaderConsoleListener(); Logger.Listeners.Add((ILogListener)(object)PreloaderLog); if (ConsoleManager.ConsoleEnabled) { ConsoleManager.CreateConsole(); Logger.Listeners.Add((ILogListener)new ConsoleLogListener()); } RedirectStdErrFix.Apply(); ChainloaderLogHelper.PrintLogInfo(Log); LogLevel val = (LogLevel)16; LogLevel val2 = val; bool flag = default(bool); BepInExLogInterpolatedStringHandler val3 = new BepInExLogInterpolatedStringHandler(20, 1, val, ref flag); if (flag) { val3.AppendLiteral("Running under Unity "); val3.AppendFormatted(UnityInfo.Version); } Logger.Log(val2, val3); val = (LogLevel)16; LogLevel val4 = val; val3 = new BepInExLogInterpolatedStringHandler(17, 1, val, ref flag); if (flag) { val3.AppendLiteral("Runtime version: "); val3.AppendFormatted(Environment.Version); } Logger.Log(val4, val3); val = (LogLevel)16; LogLevel val5 = val; val3 = new BepInExLogInterpolatedStringHandler(21, 1, val, ref flag); if (flag) { val3.AppendLiteral("Runtime information: "); val3.AppendFormatted(RuntimeInformation.FrameworkDescription); } Logger.Log(val5, val3); val = (LogLevel)32; LogLevel val6 = val; val3 = new BepInExLogInterpolatedStringHandler(22, 1, val, ref flag); if (flag) { val3.AppendLiteral("Game executable path: "); val3.AppendFormatted(Paths.ExecutablePath); } Logger.Log(val6, val3); val = (LogLevel)32; LogLevel val7 = val; val3 = new BepInExLogInterpolatedStringHandler(28, 1, val, ref flag); if (flag) { val3.AppendLiteral("Interop assembly directory: "); val3.AppendFormatted(Il2CppInteropManager.IL2CPPInteropAssemblyPath); } Logger.Log(val7, val3); val = (LogLevel)32; LogLevel val8 = val; val3 = new BepInExLogInterpolatedStringHandler(19, 1, val, ref flag); if (flag) { val3.AppendLiteral("BepInEx root path: "); val3.AppendFormatted(Paths.BepInExRootPath); } Logger.Log(val8, val3); if (PlatformHelper.Is((Platform)131072) && !Environment.Is64BitProcess && !NativeLibrary.TryGetExport(NativeLibrary.Load("ntdll"), "RtlRestoreContext", out var _)) { Logger.Log((LogLevel)4, (object)"Your wine version doesn't support CoreCLR properly, expect crashes! Upgrade to wine 7.16 or higher."); } NativeLibrary.SetDllImportResolver(typeof(IL2CPP).Assembly, DllImportResolver); Il2CppInteropManager.Initialize(); AssemblyPatcher val9 = new AssemblyPatcher((Func)((byte[] data, string _) => Assembly.Load(data))); try { val9.AddPatchersFromDirectory(Paths.PatcherPluginPath); ManualLogSource log = Log; BepInExInfoLogInterpolatedStringHandler val10 = new BepInExInfoLogInterpolatedStringHandler(22, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val10).AppendFormatted(val9.PatcherContext.PatcherPlugins.Count); ((BepInExLogInterpolatedStringHandler)val10).AppendLiteral(" patcher plugin"); ((BepInExLogInterpolatedStringHandler)val10).AppendFormatted((val9.PatcherContext.PatcherPlugins.Count == 1) ? "" : "s"); ((BepInExLogInterpolatedStringHandler)val10).AppendLiteral(" loaded"); } log.LogInfo(val10); val9.LoadAssemblyDirectories(new string[1] { Il2CppInteropManager.IL2CPPInteropAssemblyPath }); ManualLogSource log2 = Log; val10 = new BepInExInfoLogInterpolatedStringHandler(22, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val10).AppendFormatted(val9.PatcherContext.PatcherPlugins.Count); ((BepInExLogInterpolatedStringHandler)val10).AppendLiteral(" assemblies discovered"); } log2.LogInfo(val10); val9.PatchAndLoad(); } finally { ((IDisposable)val9)?.Dispose(); } Logger.Listeners.Remove((ILogListener)(object)PreloaderLog); Chainloader = new IL2CPPChainloader(); ((BaseChainloader)Chainloader).Initialize((string)null); } catch (Exception ex) { Log.Log((LogLevel)1, (object)ex); throw; } } private static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) { if (libraryName == "GameAssembly") { return NativeLibrary.Load(Il2CppInteropManager.GameAssemblyPath, assembly, searchPath); } return IntPtr.Zero; } } internal static class UnityPreloaderRunner { public static void PreloaderMain() { string directoryName = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetFullPath(EnvVars.DOORSTOP_INVOKE_DLL_PATH))); PlatformUtils.SetPlatform(); Paths.SetExecutablePath(EnvVars.DOORSTOP_PROCESS_PATH, directoryName, EnvVars.DOORSTOP_MANAGED_FOLDER_DIR, false, EnvVars.DOORSTOP_DLL_SEARCH_DIRS); Utility.AddCecilPlatformAssemblies(AppDomain.CurrentDomain, Paths.ManagedPath); Utility.AddCecilPlatformAssemblies(AppDomain.CurrentDomain, Path.GetDirectoryName(Paths.ManagedPath)); AppDomain.CurrentDomain.AssemblyResolve += LocalResolve; Preloader.Run(); } internal static Assembly LocalResolve(object sender, ResolveEventArgs args) { AssemblyName assemblyName = new AssemblyName(args.Name); Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly x) => x.GetName().Name == assemblyName.Name); if (assembly != null) { return assembly; } if (Utility.TryResolveDllAssembly(assemblyName, Paths.BepInExAssemblyDirectory, ref assembly) || Utility.TryResolveDllAssembly(assemblyName, Paths.PatcherPluginPath, ref assembly) || Utility.TryResolveDllAssembly(assemblyName, Paths.PluginPath, ref assembly)) { return assembly; } return null; } } } namespace BepInEx.Unity.IL2CPP.Utils { internal static class Il2CppUtils { private static GameObject managerGo; public static Il2CppObjectBase AddComponent(Type t) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if ((Object)(object)managerGo == (Object)null) { managerGo = new GameObject { hideFlags = (HideFlags)61, name = "BepInEx_Manager" }; } if (!ClassInjector.IsTypeRegisteredInIl2Cpp(t)) { ClassInjector.RegisterTypeInIl2Cpp(t); } return (Il2CppObjectBase)(object)managerGo.AddComponent(Il2CppType.From(t)); } } internal static class MessageBox { [DllImport("user32.dll", CharSet = CharSet.Ansi, SetLastError = true)] private static extern int MessageBoxA(IntPtr hWnd, string lpText, string lpCaption, uint uType); public static void Show(string text, string caption) { if (!PlatformHelper.Is((Platform)37)) { throw new PlatformNotSupportedException(); } if (MessageBoxA(IntPtr.Zero, text, caption, 0u) == 0) { throw new Win32Exception(); } } } public static class MonoBehaviourExtensions { public static Coroutine StartCoroutine(this MonoBehaviour self, IEnumerator coroutine) { return self.StartCoroutine(coroutine.WrapToIl2Cpp()); } } internal static class NotifySend { private const string EXECUTABLE_NAME = "notify-send"; public static bool IsSupported => Find("notify-send") != null; private static string Find(string fileName) { if (File.Exists(fileName)) { return Path.GetFullPath(fileName); } string environmentVariable = Environment.GetEnvironmentVariable("PATH"); if (environmentVariable == null) { return null; } string[] array = environmentVariable.Split(Path.PathSeparator); for (int i = 0; i < array.Length; i++) { string text = Path.Combine(array[i], fileName); if (File.Exists(text)) { return text; } } return null; } public static void Send(string summary, string body) { if (!IsSupported) { throw new NotSupportedException(); } Process.Start(new ProcessStartInfo(Find("notify-send")) { ArgumentList = { summary, body, "--app-name=BepInEx" } }); } } } namespace BepInEx.Unity.IL2CPP.Utils.Collections { public static class CollectionExtensions { public static IEnumerator WrapToIl2Cpp(this IEnumerator self) { return ((Il2CppObjectBase)new Il2CppManagedEnumerator(self)).Cast(); } public static IEnumerator WrapToManaged(this IEnumerator self) { return new ManagedIl2CppEnumerator(self); } public static IEnumerable WrapToIl2Cpp(this IEnumerable self) { return ((Il2CppObjectBase)new Il2CppManagedEnumerable(self)).Cast(); } public static IEnumerable WrapToManaged(this IEnumerable self) { return new ManagedIl2CppEnumerable(self); } } public class Il2CppManagedEnumerable : Object { private readonly IEnumerable enumerable; static Il2CppManagedEnumerable() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown RegisterTypeOptions val = new RegisterTypeOptions(); val.set_Interfaces(Il2CppInterfaceCollection.op_Implicit(new Type[1] { typeof(IEnumerable) })); ClassInjector.RegisterTypeInIl2Cpp(val); } public Il2CppManagedEnumerable(IntPtr ptr) : base(ptr) { } public Il2CppManagedEnumerable(IEnumerable enumerable) : base(ClassInjector.DerivedConstructorPointer()) { this.enumerable = enumerable ?? throw new ArgumentNullException("enumerable"); ClassInjector.DerivedConstructorBody((Il2CppObjectBase)(object)this); } public IEnumerator GetEnumerator() { return ((Il2CppObjectBase)new Il2CppManagedEnumerator(enumerable.GetEnumerator())).Cast(); } } public class Il2CppManagedEnumerator : Object { private static readonly Dictionary> boxers; private readonly IEnumerator enumerator; public Object Current { get { object current = this.enumerator.Current; IEnumerator val = (IEnumerator)((current is IEnumerator) ? current : null); if (val == null) { if (!(current is IEnumerator enumerator)) { Object val2 = (Object)((current is Object) ? current : null); if (val2 == null) { if (current != null) { return ManagedToIl2CppObject(current); } return null; } return val2; } return (Object)(object)new Il2CppManagedEnumerator(enumerator); } return ((Il2CppObjectBase)val).Cast(); } } static Il2CppManagedEnumerator() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown boxers = new Dictionary>(); RegisterTypeOptions val = new RegisterTypeOptions(); val.set_Interfaces(Il2CppInterfaceCollection.op_Implicit(new Type[1] { typeof(IEnumerator) })); ClassInjector.RegisterTypeInIl2Cpp(val); } public Il2CppManagedEnumerator(IntPtr ptr) : base(ptr) { } public Il2CppManagedEnumerator(IEnumerator enumerator) : base(ClassInjector.DerivedConstructorPointer()) { this.enumerator = enumerator ?? throw new ArgumentNullException("enumerator"); ClassInjector.DerivedConstructorBody((Il2CppObjectBase)(object)this); } public bool MoveNext() { return enumerator.MoveNext(); } public void Reset() { enumerator.Reset(); } private static Func GetValueBoxer(Type t) { if (boxers.TryGetValue(t, out var value)) { return value; } DynamicMethod dynamicMethod = new DynamicMethod("Il2CppUnbox_" + GeneralExtensions.FullDescription(t), typeof(Object), new Type[1] { typeof(object) }); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); LocalBuilder local = iLGenerator.DeclareLocal(t); FieldInfo field = typeof(Il2CppClassPointerStore<>).MakeGenericType(t).GetField("NativeClassPtr"); iLGenerator.Emit(OpCodes.Ldsfld, field); iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Unbox_Any, t); iLGenerator.Emit(OpCodes.Stloc, local); iLGenerator.Emit(OpCodes.Ldloca, local); iLGenerator.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod("il2cpp_value_box")); iLGenerator.Emit(OpCodes.Newobj, typeof(Object).GetConstructor(new Type[1] { typeof(IntPtr) })); iLGenerator.Emit(OpCodes.Ret); Func func = dynamicMethod.CreateDelegate(typeof(Func)) as Func; boxers[t] = func; return func; } private static Object ManagedToIl2CppObject(object obj) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown Type type = obj.GetType(); if (obj is string text) { return new Object(IL2CPP.ManagedStringToIl2Cpp(text)); } if (type.IsPrimitive) { return GetValueBoxer(type)(obj); } throw new NotSupportedException($"Type {type} cannot be converted directly to an Il2Cpp object"); } } public class ManagedIl2CppEnumerable : IEnumerable { private readonly IEnumerable enumerable; public ManagedIl2CppEnumerable(IEnumerable enumerable) { this.enumerable = enumerable ?? throw new ArgumentNullException("enumerable"); } public IEnumerator GetEnumerator() { return new ManagedIl2CppEnumerator(enumerable.GetEnumerator()); } } public class ManagedIl2CppEnumerator : IEnumerator { private static readonly Func moveNext = AccessTools.Method(typeof(IEnumerator), "MoveNext", (Type[])null, (Type[])null)?.CreateDelegate>(); private static readonly Action reset = AccessTools.Method(typeof(IEnumerator), "Reset", (Type[])null, (Type[])null)?.CreateDelegate>(); private readonly IEnumerator enumerator; public object Current => enumerator.Current; public ManagedIl2CppEnumerator(IEnumerator enumerator) { this.enumerator = enumerator ?? throw new ArgumentNullException("enumerator"); } public bool MoveNext() { return moveNext?.Invoke(enumerator) ?? false; } public void Reset() { reset?.Invoke(enumerator); } } } namespace BepInEx.Unity.IL2CPP.UnityEngine { public enum KeyCode { None = 0, Backspace = 8, Delete = 127, Tab = 9, Clear = 12, Return = 13, Pause = 19, Escape = 27, Space = 32, Keypad0 = 256, Keypad1 = 257, Keypad2 = 258, Keypad3 = 259, Keypad4 = 260, Keypad5 = 261, Keypad6 = 262, Keypad7 = 263, Keypad8 = 264, Keypad9 = 265, KeypadPeriod = 266, KeypadDivide = 267, KeypadMultiply = 268, KeypadMinus = 269, KeypadPlus = 270, KeypadEnter = 271, KeypadEquals = 272, UpArrow = 273, DownArrow = 274, RightArrow = 275, LeftArrow = 276, Insert = 277, Home = 278, End = 279, PageUp = 280, PageDown = 281, F1 = 282, F2 = 283, F3 = 284, F4 = 285, F5 = 286, F6 = 287, F7 = 288, F8 = 289, F9 = 290, F10 = 291, F11 = 292, F12 = 293, F13 = 294, F14 = 295, F15 = 296, Alpha0 = 48, Alpha1 = 49, Alpha2 = 50, Alpha3 = 51, Alpha4 = 52, Alpha5 = 53, Alpha6 = 54, Alpha7 = 55, Alpha8 = 56, Alpha9 = 57, Exclaim = 33, DoubleQuote = 34, Hash = 35, Dollar = 36, Percent = 37, Ampersand = 38, Quote = 39, LeftParen = 40, RightParen = 41, Asterisk = 42, Plus = 43, Comma = 44, Minus = 45, Period = 46, Slash = 47, Colon = 58, Semicolon = 59, Less = 60, Equals = 61, Greater = 62, Question = 63, At = 64, LeftBracket = 91, Backslash = 92, RightBracket = 93, Caret = 94, Underscore = 95, BackQuote = 96, A = 97, B = 98, C = 99, D = 100, E = 101, F = 102, G = 103, H = 104, I = 105, J = 106, K = 107, L = 108, M = 109, N = 110, O = 111, P = 112, Q = 113, R = 114, S = 115, T = 116, U = 117, V = 118, W = 119, X = 120, Y = 121, Z = 122, LeftCurlyBracket = 123, Pipe = 124, RightCurlyBracket = 125, Tilde = 126, Numlock = 300, CapsLock = 301, ScrollLock = 302, RightShift = 303, LeftShift = 304, RightControl = 305, LeftControl = 306, RightAlt = 307, LeftAlt = 308, LeftCommand = 310, LeftApple = 310, LeftWindows = 311, RightCommand = 309, RightApple = 309, RightWindows = 312, AltGr = 313, Help = 315, Print = 316, SysReq = 317, Break = 318, Menu = 319, Mouse0 = 323, Mouse1 = 324, Mouse2 = 325, Mouse3 = 326, Mouse4 = 327, Mouse5 = 328, Mouse6 = 329, JoystickButton0 = 330, JoystickButton1 = 331, JoystickButton2 = 332, JoystickButton3 = 333, JoystickButton4 = 334, JoystickButton5 = 335, JoystickButton6 = 336, JoystickButton7 = 337, JoystickButton8 = 338, JoystickButton9 = 339, JoystickButton10 = 340, JoystickButton11 = 341, JoystickButton12 = 342, JoystickButton13 = 343, JoystickButton14 = 344, JoystickButton15 = 345, JoystickButton16 = 346, JoystickButton17 = 347, JoystickButton18 = 348, JoystickButton19 = 349, Joystick1Button0 = 350, Joystick1Button1 = 351, Joystick1Button2 = 352, Joystick1Button3 = 353, Joystick1Button4 = 354, Joystick1Button5 = 355, Joystick1Button6 = 356, Joystick1Button7 = 357, Joystick1Button8 = 358, Joystick1Button9 = 359, Joystick1Button10 = 360, Joystick1Button11 = 361, Joystick1Button12 = 362, Joystick1Button13 = 363, Joystick1Button14 = 364, Joystick1Button15 = 365, Joystick1Button16 = 366, Joystick1Button17 = 367, Joystick1Button18 = 368, Joystick1Button19 = 369, Joystick2Button0 = 370, Joystick2Button1 = 371, Joystick2Button2 = 372, Joystick2Button3 = 373, Joystick2Button4 = 374, Joystick2Button5 = 375, Joystick2Button6 = 376, Joystick2Button7 = 377, Joystick2Button8 = 378, Joystick2Button9 = 379, Joystick2Button10 = 380, Joystick2Button11 = 381, Joystick2Button12 = 382, Joystick2Button13 = 383, Joystick2Button14 = 384, Joystick2Button15 = 385, Joystick2Button16 = 386, Joystick2Button17 = 387, Joystick2Button18 = 388, Joystick2Button19 = 389, Joystick3Button0 = 390, Joystick3Button1 = 391, Joystick3Button2 = 392, Joystick3Button3 = 393, Joystick3Button4 = 394, Joystick3Button5 = 395, Joystick3Button6 = 396, Joystick3Button7 = 397, Joystick3Button8 = 398, Joystick3Button9 = 399, Joystick3Button10 = 400, Joystick3Button11 = 401, Joystick3Button12 = 402, Joystick3Button13 = 403, Joystick3Button14 = 404, Joystick3Button15 = 405, Joystick3Button16 = 406, Joystick3Button17 = 407, Joystick3Button18 = 408, Joystick3Button19 = 409, Joystick4Button0 = 410, Joystick4Button1 = 411, Joystick4Button2 = 412, Joystick4Button3 = 413, Joystick4Button4 = 414, Joystick4Button5 = 415, Joystick4Button6 = 416, Joystick4Button7 = 417, Joystick4Button8 = 418, Joystick4Button9 = 419, Joystick4Button10 = 420, Joystick4Button11 = 421, Joystick4Button12 = 422, Joystick4Button13 = 423, Joystick4Button14 = 424, Joystick4Button15 = 425, Joystick4Button16 = 426, Joystick4Button17 = 427, Joystick4Button18 = 428, Joystick4Button19 = 429, Joystick5Button0 = 430, Joystick5Button1 = 431, Joystick5Button2 = 432, Joystick5Button3 = 433, Joystick5Button4 = 434, Joystick5Button5 = 435, Joystick5Button6 = 436, Joystick5Button7 = 437, Joystick5Button8 = 438, Joystick5Button9 = 439, Joystick5Button10 = 440, Joystick5Button11 = 441, Joystick5Button12 = 442, Joystick5Button13 = 443, Joystick5Button14 = 444, Joystick5Button15 = 445, Joystick5Button16 = 446, Joystick5Button17 = 447, Joystick5Button18 = 448, Joystick5Button19 = 449, Joystick6Button0 = 450, Joystick6Button1 = 451, Joystick6Button2 = 452, Joystick6Button3 = 453, Joystick6Button4 = 454, Joystick6Button5 = 455, Joystick6Button6 = 456, Joystick6Button7 = 457, Joystick6Button8 = 458, Joystick6Button9 = 459, Joystick6Button10 = 460, Joystick6Button11 = 461, Joystick6Button12 = 462, Joystick6Button13 = 463, Joystick6Button14 = 464, Joystick6Button15 = 465, Joystick6Button16 = 466, Joystick6Button17 = 467, Joystick6Button18 = 468, Joystick6Button19 = 469, Joystick7Button0 = 470, Joystick7Button1 = 471, Joystick7Button2 = 472, Joystick7Button3 = 473, Joystick7Button4 = 474, Joystick7Button5 = 475, Joystick7Button6 = 476, Joystick7Button7 = 477, Joystick7Button8 = 478, Joystick7Button9 = 479, Joystick7Button10 = 480, Joystick7Button11 = 481, Joystick7Button12 = 482, Joystick7Button13 = 483, Joystick7Button14 = 484, Joystick7Button15 = 485, Joystick7Button16 = 486, Joystick7Button17 = 487, Joystick7Button18 = 488, Joystick7Button19 = 489, Joystick8Button0 = 490, Joystick8Button1 = 491, Joystick8Button2 = 492, Joystick8Button3 = 493, Joystick8Button4 = 494, Joystick8Button5 = 495, Joystick8Button6 = 496, Joystick8Button7 = 497, Joystick8Button8 = 498, Joystick8Button9 = 499, Joystick8Button10 = 500, Joystick8Button11 = 501, Joystick8Button12 = 502, Joystick8Button13 = 503, Joystick8Button14 = 504, Joystick8Button15 = 505, Joystick8Button16 = 506, Joystick8Button17 = 507, Joystick8Button18 = 508, Joystick8Button19 = 509 } public static class Input { private delegate bool GetKeyIntDelegate(KeyCode key); private static readonly GetKeyIntDelegate GetKeyInt_Value = IL2CPP.ResolveICall("UnityEngine.Input::GetKeyInt(UnityEngine.KeyCode)"); public static bool GetKeyInt(KeyCode key) { return GetKeyInt_Value(key); } } } namespace BepInEx.Unity.IL2CPP.Logging { internal class BepInExLoggerProvider : ILoggerProvider, IDisposable { private class EmptyScope : IDisposable { public void Dispose() { } } private class BepInExLogger : ILogSource, IDisposable, ILogger { public string SourceName { get; init; } public event EventHandler LogEvent; public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown string text = state.ToString() ?? string.Empty; if (exception != null) { text += $"\nException: {exception}"; } this.LogEvent?.Invoke(this, new LogEventArgs((object)text, MSLogLevelToBepInExLogLevel(logLevel), (ILogSource)(object)this)); } public bool IsEnabled(LogLevel logLevel) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 return (MSLogLevelToBepInExLogLevel(logLevel) & Logger.ListenedLogLevels) > 0; } public IDisposable BeginScope(TState state) { return new EmptyScope(); } public void Dispose() { Logger.Sources.Remove((ILogSource)(object)this); } private static LogLevel MSLogLevelToBepInExLogLevel(LogLevel logLevel) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) return (LogLevel)(logLevel switch { LogLevel.Trace => 32, LogLevel.Debug => 32, LogLevel.Information => 16, LogLevel.Warning => 4, LogLevel.Error => 2, LogLevel.Critical => 1, LogLevel.None => 0, _ => throw new ArgumentOutOfRangeException("logLevel", logLevel, null), }); } } private readonly List loggers = new List(); public void Dispose() { foreach (BepInExLogger logger in loggers) { logger.Dispose(); } loggers.Clear(); } public ILogger CreateLogger(string categoryName) { BepInExLogger bepInExLogger = new BepInExLogger { SourceName = categoryName }; Logger.Sources.Add((ILogSource)(object)bepInExLogger); loggers.Add(bepInExLogger); return bepInExLogger; } } public class IL2CPPLogSource : ILogSource, IDisposable { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void IL2CPPLogCallbackDelegate([In][MarshalAs(UnmanagedType.LPStr)] string message); public string SourceName { get; } = "IL2CPP"; public event EventHandler LogEvent; public IL2CPPLogSource() { IL2CPP.il2cpp_register_log_callback(Marshal.GetFunctionPointerForDelegate(IL2CPPLogCallback)); } public void Dispose() { } private void IL2CPPLogCallback(string message) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown this.LogEvent?.Invoke(this, new LogEventArgs((object)message.Trim(), (LogLevel)8, (ILogSource)(object)this)); } } public class IL2CPPUnityLogSource : ILogSource, IDisposable { private delegate IntPtr SetLogCallbackDefinedDelegate(bool defined); public string SourceName { get; } = "Unity"; public event EventHandler LogEvent; public IL2CPPUnityLogSource() { Application.s_LogCallbackHandler = LogCallback.op_Implicit((Action)UnityLogCallback); IL2CPP.ResolveICall("UnityEngine.Application::SetLogCallbackDefined")(defined: true); } public void Dispose() { } public void UnityLogCallback(string logLine, string exception, LogType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected I4, but got Unknown //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown this.LogEvent?.Invoke(this, new LogEventArgs((object)logLine, (LogLevel)((int)type switch { 0 => 2, 1 => 32, 2 => 4, 3 => 8, 4 => 2, _ => 8, }), (ILogSource)(object)this)); } } } namespace BepInEx.Unity.IL2CPP.Hook { internal abstract class BaseNativeDetour : INativeDetour, IDetour, IDisposable where T : BaseNativeDetour { protected static readonly ManualLogSource Logger = Logger.CreateLogSource(typeof(T).Name); public bool IsPrepared { get; protected set; } protected MethodInfo TrampolineMethod { get; set; } protected Delegate DetourMethod { get; set; } public nint OriginalMethodPtr { get; } public nint DetourMethodPtr { get; } public nint TrampolinePtr { get; protected set; } public bool IsValid { get; private set; } = true; public bool IsApplied { get; private set; } protected BaseNativeDetour(nint originalMethodPtr, Delegate detourMethod) { OriginalMethodPtr = originalMethodPtr; DetourMethod = detourMethod; DetourMethodPtr = Marshal.GetFunctionPointerForDelegate(detourMethod); } public void Dispose() { if (IsValid) { Undo(); Free(); } } public void Apply() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) if (!IsApplied) { Prepare(); ApplyImpl(); LogLevel val = (LogLevel)32; LogLevel val2 = val; bool flag = default(bool); BepInExLogInterpolatedStringHandler val3 = new BepInExLogInterpolatedStringHandler(32, 3, val, ref flag); if (flag) { val3.AppendLiteral("Original: "); val3.AppendFormatted((IntPtr)OriginalMethodPtr, "X"); val3.AppendLiteral(", Trampoline: "); val3.AppendFormatted((IntPtr)TrampolinePtr, "X"); val3.AppendLiteral(", diff: "); val3.AppendFormatted((IntPtr)Math.Abs(OriginalMethodPtr - TrampolinePtr), "X"); } Logger.Log(val2, val3); IsApplied = true; } } public void Undo() { if (IsApplied && IsPrepared) { UndoImpl(); } } public void Free() { FreeImpl(); IsValid = false; } public MethodBase GenerateTrampoline(MethodBase signature = null) { if (TrampolineMethod == null) { Prepare(); TrampolineMethod = DetourHelper.GenerateNativeProxy((IntPtr)TrampolinePtr, signature); } return TrampolineMethod; } public TDelegate GenerateTrampoline() where TDelegate : Delegate { if (!typeof(Delegate).IsAssignableFrom(typeof(TDelegate))) { throw new InvalidOperationException($"Type {typeof(TDelegate)} not a delegate type."); } GenerateTrampoline(typeof(TDelegate).GetMethod("Invoke")); return Marshal.GetDelegateForFunctionPointer(TrampolinePtr); } protected abstract void ApplyImpl(); private void Prepare() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown if (!IsPrepared) { bool flag = default(bool); BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(30, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Preparing detour from 0x"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted((IntPtr)OriginalMethodPtr, "X2"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" to 0x"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted((IntPtr)DetourMethodPtr, "X2"); } Logger.LogDebug(val); PrepareImpl(); val = new BepInExDebugLogInterpolatedStringHandler(31, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Prepared detour; Trampoline: 0x"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted((IntPtr)TrampolinePtr, "X2"); } Logger.LogDebug(val); IsPrepared = true; } } protected abstract void PrepareImpl(); protected abstract void UndoImpl(); protected abstract void FreeImpl(); } internal class Il2CppInteropDetourProvider : IDetourProvider { public IDetour Create(nint original, TDelegate target) where TDelegate : Delegate { return (IDetour)(object)new Il2CppInteropDetour(INativeDetour.Create(original, target)); } } internal class Il2CppInteropDetour : IDetour, IDisposable { private readonly INativeDetour detour; public nint Target => detour.OriginalMethodPtr; public nint Detour => detour.DetourMethodPtr; public nint OriginalTrampoline => detour.TrampolinePtr; public Il2CppInteropDetour(INativeDetour detour) { this.detour = detour; } public void Dispose() { detour.Dispose(); } public void Apply() { ((IDetour)detour).Apply(); } public T GenerateTrampoline() where T : Delegate { return ((IDetour)detour).GenerateTrampoline(); } } public interface INativeDetour : IDetour, IDisposable { private class CacheDetourWrapper : INativeDetour, IDetour, IDisposable { private readonly INativeDetour _wrapped; private List _cache = new List(); public bool IsValid => ((IDetour)_wrapped).IsValid; public bool IsApplied => ((IDetour)_wrapped).IsApplied; public nint OriginalMethodPtr => _wrapped.OriginalMethodPtr; public nint DetourMethodPtr => _wrapped.DetourMethodPtr; public nint TrampolinePtr => _wrapped.TrampolinePtr; public CacheDetourWrapper(INativeDetour wrapped, Delegate target) { _wrapped = wrapped; _cache.Add(target); } public void Dispose() { _wrapped.Dispose(); _cache.Clear(); } public void Apply() { ((IDetour)_wrapped).Apply(); } public void Undo() { ((IDetour)_wrapped).Undo(); } public void Free() { ((IDetour)_wrapped).Free(); } public MethodBase GenerateTrampoline(MethodBase signature = null) { return ((IDetour)_wrapped).GenerateTrampoline(signature); } public T GenerateTrampoline() where T : Delegate { T val = ((IDetour)_wrapped).GenerateTrampoline(); _cache.Add(val); return val; } } internal enum DetourProvider { Default, Dobby, Funchook } private static readonly ConfigEntry DetourProviderType = ConfigFile.CoreConfig.Bind("Detours", "DetourProviderType", DetourProvider.Default, "The native provider to use for managed detours"); nint OriginalMethodPtr { get; } nint DetourMethodPtr { get; } nint TrampolinePtr { get; } private static INativeDetour CreateDefault(nint original, T target) where T : Delegate { return new DobbyDetour(original, target); } static INativeDetour Create(nint original, T target) where T : Delegate { INativeDetour nativeDetour = DetourProviderType.Value switch { DetourProvider.Dobby => new DobbyDetour(original, target), DetourProvider.Funchook => new FunchookDetour(original, target), _ => CreateDefault(original, target), }; if (!ReflectionHelper.IsMono) { return new CacheDetourWrapper(nativeDetour, target); } return nativeDetour; } static INativeDetour CreateAndApply(nint from, T to, out T original) where T : Delegate { INativeDetour nativeDetour = Create(from, to); original = ((IDetour)nativeDetour).GenerateTrampoline(); ((IDetour)nativeDetour).Apply(); return nativeDetour; } } } namespace BepInEx.Unity.IL2CPP.Hook.Funchook { internal class FunchookDetour : BaseNativeDetour { private readonly nint funchookInstance; public FunchookDetour(nint originalMethodPtr, Delegate detourMethod) : base(originalMethodPtr, detourMethod) { funchookInstance = FunchookLib.Create(); } protected override void ApplyImpl() { EnsureSuccess(FunchookLib.Install(funchookInstance, 0), "FunchookLib.Install(funchookInstance, 0)"); } protected unsafe override void PrepareImpl() { nint originalMethodPtr = base.OriginalMethodPtr; EnsureSuccess(FunchookLib.Prepare(funchookInstance, &originalMethodPtr, base.DetourMethodPtr), "FunchookLib.Prepare(funchookInstance, &trampolinePtr, DetourMethodPtr)"); base.TrampolinePtr = originalMethodPtr; } protected override void UndoImpl() { EnsureSuccess(FunchookLib.Uninstall(funchookInstance, 0), "FunchookLib.Uninstall(funchookInstance, 0)"); } protected override void FreeImpl() { EnsureSuccess(FunchookLib.Destroy(funchookInstance), "FunchookLib.Destroy(funchookInstance)"); } private string GetErrorMessage() { return FunchookLib.ErrorMessage(funchookInstance); } private void EnsureSuccess(FunchookResult result, [CallerArgumentExpression("result")] string methodName = null) { if (result == FunchookResult.Success) { return; } string errorMessage = GetErrorMessage(); if (result == FunchookResult.OutOfMemory) { throw new OutOfMemoryException(methodName + " failed: " + errorMessage); } throw new Exception($"{methodName} failed with result {result}: {errorMessage}"); } } internal enum FunchookResult { InternalError = -1, Success, OutOfMemory, AlreadyInstalled, Disassembly, IPRelativeOffset, CannotFixIPRelative, FoundBackJump, TooShortInstructions, MemoryAllocation, MemoryFunction, NotInstalled, NoAvailableRegisters } internal static class FunchookLib { [DllImport("funchook", CallingConvention = CallingConvention.Cdecl, EntryPoint = "funchook_create")] public static extern nint Create(); [DllImport("funchook", CallingConvention = CallingConvention.Cdecl, EntryPoint = "funchook_destroy")] public static extern FunchookResult Destroy(nint handle); [DllImport("funchook", CallingConvention = CallingConvention.Cdecl, EntryPoint = "funchook_prepare")] public unsafe static extern FunchookResult Prepare(nint handle, nint* target, nint hook); [DllImport("funchook", CallingConvention = CallingConvention.Cdecl, EntryPoint = "funchook_install")] public static extern FunchookResult Install(nint handle, int flags); [DllImport("funchook", CallingConvention = CallingConvention.Cdecl, EntryPoint = "funchook_uninstall")] public static extern FunchookResult Uninstall(nint handle, int flags); [DllImport("funchook", CallingConvention = CallingConvention.Cdecl, EntryPoint = "funchook_error_message")] public static extern string ErrorMessage(nint handle); [DllImport("funchook", CallingConvention = CallingConvention.Cdecl, EntryPoint = "funchook_set_debug_file")] public static extern FunchookResult SetDebugFile([MarshalAs(UnmanagedType.LPStr)] string name); } } namespace BepInEx.Unity.IL2CPP.Hook.Dobby { internal class DobbyDetour : BaseNativeDetour { public DobbyDetour(nint originalMethodPtr, Delegate detourMethod) : base(originalMethodPtr, detourMethod) { } protected override void ApplyImpl() { DobbyLib.Commit(base.OriginalMethodPtr); } protected unsafe override void PrepareImpl() { nint trampolinePtr = 0; DobbyLib.Prepare(base.OriginalMethodPtr, base.DetourMethodPtr, &trampolinePtr); base.TrampolinePtr = trampolinePtr; } protected override void UndoImpl() { DobbyLib.Destroy(base.OriginalMethodPtr); } protected override void FreeImpl() { } } internal static class DobbyLib { [DllImport("dobby", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DobbyHook")] public unsafe static extern int Hook(nint target, nint replacement, nint* originalCall); [DllImport("dobby", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DobbyPrepare")] public unsafe static extern int Prepare(nint target, nint replacement, nint* originalCall); [DllImport("dobby", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DobbyCommit")] public static extern int Commit(nint target); [DllImport("dobby", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DobbyDestroy")] public static extern int Destroy(nint target); } }