From 2177062d7145b3a95db3ea28faa22911620b2d65 Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Wed, 4 Sep 2024 21:19:42 -0600 Subject: [PATCH 01/20] Fixed an issue with Il2CppInterop Assembly Resolving when Il2Cpp Prefixing is used --- CHANGELOG.md | 1 + MelonLoader/Core.cs | 12 +- MelonLoader/Fixes/Il2CppInteropFixes.cs | 185 +++++++++++++++++++++++- 3 files changed, 192 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13c67b6a2..cbffdd8dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ 28. Fixed an issue with MelonLaunchOptions failing to parse arguments if an `=` sign is used instead of a space 29. Fixed an issue with MelonLaunchOptions failing to parse options if a `-` prefix is used instead of `--` 30. Fixed an issue with Command Line Arguments not being logged +31. Fixed an issue with Il2CppInterop Assembly Resolving when Il2Cpp Prefixing is used --- diff --git a/MelonLoader/Core.cs b/MelonLoader/Core.cs index f5e24392d..d6c35af24 100644 --- a/MelonLoader/Core.cs +++ b/MelonLoader/Core.cs @@ -173,26 +173,28 @@ internal static void WelcomeMessage() var archString = MelonUtils.IsGame32Bit() ? "x86" : "x64"; MelonLogger.MsgDirect($"Game Arch: {archString}"); MelonLogger.MsgDirect("------------------------------"); - MelonLogger.MsgDirect($"CommandLine: {string.Join(" ", MelonLaunchOptions.CommandLineArgs)}"); + MelonLogger.MsgDirect($"Command-Line: {string.Join(" ", MelonLaunchOptions.CommandLineArgs)}"); MelonLogger.MsgDirect("------------------------------"); - MelonEnvironment.PrintEnvironment(); } - internal static void Quit() { - MelonDebug.Msg("[ML Core] Received Quit from Support Module. Shutting down..."); + MelonDebug.Msg("[ML Core] Received Quit Request! Shutting down..."); MelonPreferences.Save(); HarmonyInstance.UnpatchSelf(); bHapticsManager.Disconnect(); +#if NET6_0_OR_GREATER + Fixes.Il2CppInteropFixes.Shutdown(); +#endif + MelonLogger.Flush(); //MelonLogger.Close(); - + Thread.Sleep(200); if (MelonLaunchOptions.Core.QuitFix) diff --git a/MelonLoader/Fixes/Il2CppInteropFixes.cs b/MelonLoader/Fixes/Il2CppInteropFixes.cs index b9e8a2899..cc57a5923 100644 --- a/MelonLoader/Fixes/Il2CppInteropFixes.cs +++ b/MelonLoader/Fixes/Il2CppInteropFixes.cs @@ -15,6 +15,8 @@ using HarmonyLib; using System.IO; using MelonLoader.Utils; +using Il2CppInterop.Generator.Contexts; +using AsmResolver.DotNet; namespace MelonLoader.Fixes { @@ -24,6 +26,7 @@ namespace MelonLoader.Fixes // reverts: https://github.com/BepInEx/Il2CppInterop/commit/18e58ef5db42a71d6012ab0387b107a4132101eb internal unsafe static class Il2CppInteropFixes { + private static Dictionary> _assemblyLookup = new(); private static Dictionary _typeLookup = new(); private static MethodInfo _getType; @@ -47,6 +50,14 @@ internal unsafe static class Il2CppInteropFixes private static MethodInfo _fixedFindAbstractMethods; private static MethodInfo _emitObjectToPointer; private static MethodInfo _emitObjectToPointer_Prefix; + private static MethodInfo _rewriteGlobalContext_AddAssemblyContext; + private static MethodInfo _rewriteGlobalContext_AddAssemblyContext_Postfix; + private static MethodInfo _rewriteGlobalContext_Dispose; + private static MethodInfo _rewriteGlobalContext_Dispose_Prefix; + private static MethodInfo _rewriteGlobalContext_GetNewAssemblyForOriginal; + private static MethodInfo _rewriteGlobalContext_GetNewAssemblyForOriginal_Prefix; + private static MethodInfo _rewriteGlobalContext_TryGetNewTypeForOriginal; + private static MethodInfo _rewriteGlobalContext_TryGetNewTypeForOriginal_Prefix; internal static void Install() { @@ -56,6 +67,7 @@ internal static void Install() Type thisType = typeof(Il2CppInteropFixes); Type classInjectorType = typeof(ClassInjector); Type ilGeneratorEx = typeof(ILGeneratorEx); + Type rewriteGlobalContextType = typeof(RewriteGlobalContext); Type injectorHelpersType = classInjectorType.Assembly.GetType("Il2CppInterop.Runtime.Injection.InjectorHelpers"); if (injectorHelpersType == null) @@ -105,6 +117,26 @@ internal static void Install() if (_get_IsByRef == null) throw new Exception("Failed to get Type.IsByRef.get"); + _rewriteGlobalContext_AddAssemblyContext = rewriteGlobalContextType.GetMethod("AddAssemblyContext", + BindingFlags.NonPublic | BindingFlags.Instance); + if (_rewriteGlobalContext_AddAssemblyContext == null) + throw new Exception("Failed to get RewriteGlobalContext.AddAssemblyContext"); + + _rewriteGlobalContext_Dispose = rewriteGlobalContextType.GetMethod("Dispose", + BindingFlags.Public | BindingFlags.Instance); + if (_rewriteGlobalContext_Dispose == null) + throw new Exception("Failed to get RewriteGlobalContext.Dispose"); + + _rewriteGlobalContext_GetNewAssemblyForOriginal = rewriteGlobalContextType.GetMethod("GetNewAssemblyForOriginal", + BindingFlags.Public | BindingFlags.Instance); + if (_rewriteGlobalContext_GetNewAssemblyForOriginal == null) + throw new Exception("Failed to get RewriteGlobalContext.GetNewAssemblyForOriginal"); + + _rewriteGlobalContext_TryGetNewTypeForOriginal = rewriteGlobalContextType.GetMethod("TryGetNewTypeForOriginal", + BindingFlags.Public | BindingFlags.Instance); + if (_rewriteGlobalContext_TryGetNewTypeForOriginal == null) + throw new Exception("Failed to get RewriteGlobalContext.TryGetNewTypeForOriginal"); + _fixedFindType = thisType.GetMethod(nameof(FixedFindType), BindingFlags.NonPublic | BindingFlags.Static); _fixedAddTypeToLookup = thisType.GetMethod(nameof(FixedAddTypeToLookup), BindingFlags.NonPublic | BindingFlags.Static); _fixedIsByRef = thisType.GetMethod(nameof(FixedIsByRef), BindingFlags.NonPublic | BindingFlags.Static); @@ -116,6 +148,10 @@ internal static void Install() _isTypeSupported_Transpiler = thisType.GetMethod(nameof(IsTypeSupported_Transpiler), BindingFlags.NonPublic | BindingFlags.Static); _convertMethodInfo_Transpiler = thisType.GetMethod(nameof(ConvertMethodInfo_Transpiler), BindingFlags.NonPublic | BindingFlags.Static); _emitObjectToPointer_Prefix = thisType.GetMethod(nameof(EmitObjectToPointer_Prefix), BindingFlags.NonPublic | BindingFlags.Static); + _rewriteGlobalContext_AddAssemblyContext_Postfix = thisType.GetMethod(nameof(RewriteGlobalContext_AddAssemblyContext_Postfix), BindingFlags.NonPublic | BindingFlags.Static); + _rewriteGlobalContext_Dispose_Prefix = thisType.GetMethod(nameof(RewriteGlobalContext_Dispose_Prefix), BindingFlags.NonPublic | BindingFlags.Static); + _rewriteGlobalContext_GetNewAssemblyForOriginal_Prefix = thisType.GetMethod(nameof(RewriteGlobalContext_GetNewAssemblyForOriginal_Prefix), BindingFlags.NonPublic | BindingFlags.Static); + _rewriteGlobalContext_TryGetNewTypeForOriginal_Prefix = thisType.GetMethod(nameof(RewriteGlobalContext_TryGetNewTypeForOriginal_Prefix), BindingFlags.NonPublic | BindingFlags.Static); MelonDebug.Msg("Patching Il2CppInterop ClassInjector.SystemTypeFromIl2CppType..."); Core.HarmonyInstance.Patch(_systemTypeFromIl2CppType, @@ -148,6 +184,22 @@ internal static void Install() MelonDebug.Msg("Patching Il2CppInterop ILGeneratorEx.EmitObjectToPointer..."); Core.HarmonyInstance.Patch(_emitObjectToPointer, new HarmonyMethod(_emitObjectToPointer_Prefix)); + + MelonDebug.Msg("Patching Il2CppInterop RewriteGlobalContext.AddAssemblyContext..."); + Core.HarmonyInstance.Patch(_rewriteGlobalContext_AddAssemblyContext, + null, new HarmonyMethod(_rewriteGlobalContext_AddAssemblyContext_Postfix)); + + MelonDebug.Msg("Patching Il2CppInterop RewriteGlobalContext.Dispose..."); + Core.HarmonyInstance.Patch(_rewriteGlobalContext_Dispose, + new HarmonyMethod(_rewriteGlobalContext_Dispose_Prefix)); + + MelonDebug.Msg("Patching Il2CppInterop RewriteGlobalContext.GetNewAssemblyForOriginal..."); + Core.HarmonyInstance.Patch(_rewriteGlobalContext_GetNewAssemblyForOriginal, + new HarmonyMethod(_rewriteGlobalContext_GetNewAssemblyForOriginal_Prefix)); + + MelonDebug.Msg("Patching Il2CppInterop RewriteGlobalContext.TryGetNewTypeForOriginal..."); + Core.HarmonyInstance.Patch(_rewriteGlobalContext_TryGetNewTypeForOriginal, + new HarmonyMethod(_rewriteGlobalContext_TryGetNewTypeForOriginal_Prefix)); } catch (Exception e) { @@ -155,11 +207,34 @@ internal static void Install() } } + internal static void Shutdown() + { + if (_assemblyLookup != null) + { + if (_assemblyLookup.Count > 0) + { + foreach (Dictionary dict in _assemblyLookup.Values) + dict.Clear(); + _assemblyLookup.Clear(); + } + _assemblyLookup = null; + } + + if (_typeLookup != null) + { + if (_typeLookup.Count > 0) + _typeLookup.Clear(); + _typeLookup = null; + } + } + private static bool FixedIsByRef(Type type) - => type.IsByRef || type.IsPointer; + => (type != null) && (type.IsByRef || type.IsPointer); private static Type FixedFindType(string il2CppTypeFullName) { + if (string.IsNullOrEmpty(il2CppTypeFullName)) + return null; Type returnType = Type.GetType($"Il2Cpp.{il2CppTypeFullName}"); if (returnType == null) returnType = Type.GetType($"Il2Cpp{il2CppTypeFullName}"); @@ -170,6 +245,10 @@ private static Type FixedFindType(string il2CppTypeFullName) private static void FixedAddTypeToLookup(Type type, IntPtr typePointer) { + if ((type == null) + || (typePointer == IntPtr.Zero)) + return; + _injectorHelpers_AddTypeToLookup.Invoke(null, [type, typePointer]); typePointer = IL2CPP.il2cpp_class_get_type(typePointer); @@ -186,6 +265,9 @@ private static bool EmitObjectToPointer_Prefix(bool __7, ref bool __8) private static bool RewriteType_Prefix(Type __0, ref Type __result) { + if (__0 == null) + return true; + if (__0 == typeof(void*)) { __result = __0; @@ -195,6 +277,106 @@ private static bool RewriteType_Prefix(Type __0, ref Type __result) return true; } + private static void RewriteGlobalContext_AddAssemblyContext_Postfix(RewriteGlobalContext __instance, + AssemblyRewriteContext __1) + { + if ((__instance == null) + || (__1 == null) + || __1.OriginalAssembly == null) + return; + + if (!_assemblyLookup.TryGetValue(__instance, out Dictionary contexts) + || (contexts == null)) + contexts = _assemblyLookup[__instance] = new(); + + string assemblyName = __1.OriginalAssembly.Name; + if (string.IsNullOrEmpty(assemblyName)) + return; + + contexts[assemblyName] = __1; + //MelonDebug.Msg($"[RewriteGlobalContext] Added: {assemblyName}"); + } + + private static bool RewriteGlobalContext_Dispose_Prefix(RewriteGlobalContext __instance) + { + if ((__instance == null) + || !_assemblyLookup.ContainsKey(__instance) + || !_assemblyLookup.Remove(__instance, out Dictionary contexts) + || (contexts == null)) + return true; + + contexts.Clear(); + return true; + } + + private static bool RewriteGlobalContext_GetNewAssemblyForOriginal_Prefix(RewriteGlobalContext __instance, + AssemblyDefinition __0, + ref AssemblyRewriteContext __result) + { + if ((__instance == null) + || (__0 == null) + || !_assemblyLookup.TryGetValue(__instance, out Dictionary contexts) + || (contexts == null)) + return true; + + string assemblyName = __0.Name; + if (contexts.TryGetValue(assemblyName, out __result)) + { + //MelonDebug.Msg($"[RewriteGlobalContext] Found: {assemblyName}"); + return false; + } + + if (assemblyName.StartsWith("Il2Cpp")) + assemblyName = assemblyName.Remove(0, 6); + else + assemblyName = $"Il2Cpp{assemblyName}"; + + if (contexts.TryGetValue(assemblyName, out __result)) + { + //MelonDebug.Msg($"[RewriteGlobalContext] Found: {assemblyName}"); + return false; + } + + return true; + } + + private static bool RewriteGlobalContext_TryGetNewTypeForOriginal_Prefix(RewriteGlobalContext __instance, + TypeDefinition __0, + ref TypeRewriteContext? __result) + { + if ((__instance == null) + || (__0 == null) + || (__0.Module == null) + || (__0.Module.Assembly == null) + || !_assemblyLookup.TryGetValue(__instance, out Dictionary contexts) + || (contexts == null)) + return true; + + string assemblyName = __0.Module.Assembly.Name; + if (string.IsNullOrEmpty(assemblyName)) + return false; + + AssemblyRewriteContext rewriteContext = null; + if (contexts.TryGetValue(assemblyName, out rewriteContext)) + { + //MelonDebug.Msg($"[RewriteGlobalContext] Found: {assemblyName}"); + __result = rewriteContext.TryGetContextForOriginalType(__0); + return false; + } + + if (assemblyName.StartsWith("Il2Cpp")) + assemblyName = assemblyName.Remove(0, 6); + else + assemblyName = $"Il2Cpp{assemblyName}"; + if (contexts.TryGetValue(assemblyName, out rewriteContext)) + { + //MelonDebug.Msg($"[RewriteGlobalContext] Found: {assemblyName}"); + __result = rewriteContext.TryGetContextForOriginalType(__0); + return false; + } + + return true; + } private static bool SystemTypeFromIl2CppType_Prefix(Il2CppTypeStruct* __0, ref Type __result) { @@ -279,6 +461,7 @@ private static IEnumerable SystemTypeFromIl2CppType_Transpiler( found = true; instruction.opcode = OpCodes.Call; instruction.operand = _fixedFindType; + MelonDebug.Msg("Patched Il2CppInterop ClassInjector.SystemTypeFromIl2CppType -> Type.GetType"); } From 4291bd1ffae980e8ed37d39b93c3dea16e4c7420 Mon Sep 17 00:00:00 2001 From: HAHOOS Date: Sat, 7 Sep 2024 13:30:02 +0200 Subject: [PATCH 02/20] Add MsgPastel --- MelonLoader/Utils/MelonLogger.cs | 139 ++++++++++++++++++++++++++++--- 1 file changed, 129 insertions(+), 10 deletions(-) diff --git a/MelonLoader/Utils/MelonLogger.cs b/MelonLoader/Utils/MelonLogger.cs index 9f0110c01..35bfa98fe 100644 --- a/MelonLoader/Utils/MelonLogger.cs +++ b/MelonLoader/Utils/MelonLogger.cs @@ -6,6 +6,7 @@ using System.Text; using static MelonLoader.Utils.LoggerUtils; using System.Collections.Generic; +using System.Text.RegularExpressions; namespace MelonLoader { @@ -97,34 +98,68 @@ internal static void WriteLogToFile(string message) internal static void MsgDirect(string txt) => NativeMsg(DefaultMelonColor, DefaultTextColor, null, txt, true); public static void Msg(object obj) => NativeMsg(DefaultMelonColor, DefaultTextColor, null, obj.ToString()); + public static void Msg(string txt) => NativeMsg(DefaultMelonColor, DefaultTextColor, null, txt); + public static void Msg(string txt, params object[] args) => NativeMsg(DefaultMelonColor, DefaultTextColor, null, string.Format(txt, args)); public static void Msg(ConsoleColor txt_color, object obj) => NativeMsg(DefaultMelonColor, ConsoleColorToDrawingColor(txt_color), null, obj.ToString()); + public static void Msg(ConsoleColor txt_color, string txt) => NativeMsg(DefaultMelonColor, ConsoleColorToDrawingColor(txt_color), null, txt); + public static void Msg(ConsoleColor txt_color, string txt, params object[] args) => NativeMsg(DefaultMelonColor, ConsoleColorToDrawingColor(txt_color), null, string.Format(txt, args)); //Identical to Msg(Color, string) except it skips walking the stack to find a melon public static void MsgDirect(Color txt_color, string txt) => NativeMsg(DefaultMelonColor, txt_color, null, txt, true); - + public static void Msg(Color txt_color, object obj) => NativeMsg(DefaultMelonColor, txt_color, null, obj.ToString()); + public static void Msg(Color txt_color, string txt) => NativeMsg(DefaultMelonColor, txt_color, null, txt); + public static void Msg(Color txt_color, string txt, params object[] args) => NativeMsg(DefaultMelonColor, txt_color, null, string.Format(txt, args)); + //Identical to MsgPastel(string) except it skips walking the stack to find a melon + internal static void MsgPastelDirect(string txt) => NativePastelMsg(DefaultMelonColor, DefaultTextColor, null, txt, true); + + public static void MsgPastel(object obj) => NativePastelMsg(DefaultMelonColor, DefaultTextColor, null, obj.ToString()); + + public static void MsgPastel(string txt) => NativePastelMsg(DefaultMelonColor, DefaultTextColor, null, txt); + + public static void MsgPastel(string txt, params object[] args) => NativePastelMsg(DefaultMelonColor, DefaultTextColor, null, string.Format(txt, args)); + + public static void MsgPastel(ConsoleColor txt_color, object obj) => NativePastelMsg(DefaultMelonColor, ConsoleColorToDrawingColor(txt_color), null, obj.ToString()); + + public static void MsgPastel(ConsoleColor txt_color, string txt) => NativePastelMsg(DefaultMelonColor, ConsoleColorToDrawingColor(txt_color), null, txt); + + public static void MsgPastel(ConsoleColor txt_color, string txt, params object[] args) => NativePastelMsg(DefaultMelonColor, ConsoleColorToDrawingColor(txt_color), null, string.Format(txt, args)); + + //Identical to MsgPastel(Color, string) except it skips walking the stack to find a melon + public static void MsgPastelDirect(Color txt_color, string txt) => NativePastelMsg(DefaultMelonColor, txt_color, null, txt, true); + + public static void MsgPastel(Color txt_color, object obj) => NativePastelMsg(DefaultMelonColor, txt_color, null, obj.ToString()); + + public static void MsgPastel(Color txt_color, string txt) => NativePastelMsg(DefaultMelonColor, txt_color, null, txt); + + public static void MsgPastel(Color txt_color, string txt, params object[] args) => NativePastelMsg(DefaultMelonColor, txt_color, null, string.Format(txt, args)); public static void Warning(object obj) => NativeWarning(null, obj.ToString()); + public static void Warning(string txt) => NativeWarning(null, txt); - public static void Warning(string txt, params object[] args) => NativeWarning(null, string.Format(txt, args)); + public static void Warning(string txt, params object[] args) => NativeWarning(null, string.Format(txt, args)); public static void Error(object obj) => NativeError(null, obj.ToString()); + public static void Error(string txt) => NativeError(null, txt); + public static void Error(string txt, params object[] args) => NativeError(null, string.Format(txt, args)); + public static void Error(string txt, Exception ex) => NativeError(null, $"{txt}\n{ex}"); public static void WriteLine(int length = 30) => MsgDirect(new string('-', length)); + public static void WriteLine(Color color, int length = 30) => MsgDirect(color, new string('-', length)); - + private static void NativeMsg(Color namesection_color, Color txt_color, string namesection, string txt, bool skipStackWalk = false) { if (string.IsNullOrEmpty(namesection)) @@ -141,6 +176,22 @@ private static void NativeMsg(Color namesection_color, Color txt_color, string n RunMsgCallbacks(namesection_color, txt_color, namesection, txt ?? "null"); } + private static void NativePastelMsg(Color namesection_color, Color txt_color, string namesection, string txt, bool skipStackWalk = false) + { + if (string.IsNullOrEmpty(namesection)) + { + MelonBase melon = MelonUtils.GetMelonFromStackTrace(); + if (melon != null) + { + namesection = melon.Info?.Name?.Replace(" ", "_"); + namesection_color = melon.ConsoleColor; + } + } + + Internal_PastelMsg(namesection_color, txt_color, namesection, txt ?? "null"); + RunMsgCallbacks(namesection_color, txt_color, namesection, txt ?? "null"); + } + private static void NativeWarning(string namesection, string txt) { namesection ??= MelonUtils.GetMelonFromStackTrace()?.Info?.Name?.Replace(" ", "_"); @@ -177,50 +228,89 @@ internal static void RunMsgCallbacks(Color namesection_color, Color txt_color, s public static event Action MsgCallbackHandler; public static event Action MsgDrawingCallbackHandler; + internal static void RunWarningCallbacks(string namesection, string txt) => WarningCallbackHandler?.Invoke(namesection, txt); + public static event Action WarningCallbackHandler; + internal static void RunErrorCallbacks(string namesection, string txt) => ErrorCallbackHandler?.Invoke(namesection, txt); + public static event Action ErrorCallbackHandler; public class Instance { private string Name = null; + [Obsolete("Color is obsolete. Please use DrawingColor for full Color support.")] private ConsoleColor Color { get => DrawingColorToConsoleColor(DrawingColor); set => DrawingColor = ConsoleColorToDrawingColor(value); } + private Color DrawingColor = DefaultMelonColor; public Instance(string name) => Name = name?.Replace(" ", "_"); + [Obsolete("ConsoleColor is obsolete, use the (string, Color) constructor instead.")] public Instance(string name, ConsoleColor color) : this(name) => Color = color; + public Instance(string name, Color color) : this(name) => DrawingColor = color; + public void Msg(object obj) => NativeMsg(DrawingColor, DefaultTextColor, Name, obj.ToString()); + public void Msg(string txt) => NativeMsg(DrawingColor, DefaultTextColor, Name, txt); - public void Msg(string txt, params object[] args) => NativeMsg(DrawingColor, DefaultTextColor, Name, string.Format(txt, args)); + public void Msg(string txt, params object[] args) => NativeMsg(DrawingColor, DefaultTextColor, Name, string.Format(txt, args)); public void Msg(ConsoleColor txt_color, object obj) => NativeMsg(DrawingColor, ConsoleColorToDrawingColor(txt_color), Name, obj.ToString()); + public void Msg(ConsoleColor txt_color, string txt) => NativeMsg(DrawingColor, ConsoleColorToDrawingColor(txt_color), Name, txt); + public void Msg(ConsoleColor txt_color, string txt, params object[] args) => NativeMsg(DrawingColor, ConsoleColorToDrawingColor(txt_color), Name, string.Format(txt, args)); public void Msg(Color txt_color, object obj) => NativeMsg(DrawingColor, txt_color, Name, obj.ToString()); + public void Msg(Color txt_color, string txt) => NativeMsg(DrawingColor, txt_color, Name, txt); + public void Msg(Color txt_color, string txt, params object[] args) => NativeMsg(DrawingColor, txt_color, Name, string.Format(txt, args)); + public void MsgPastel(object obj) => NativePastelMsg(DrawingColor, DefaultTextColor, Name, obj.ToString()); + + public void MsgPastel(string txt) => NativePastelMsg(DrawingColor, DefaultTextColor, Name, txt); + + public void MsgPastel(string txt, params object[] args) => NativePastelMsg(DrawingColor, DefaultTextColor, Name, string.Format(txt, args)); + + public void MsgPastel(ConsoleColor txt_color, object obj) => NativePastelMsg(DrawingColor, ConsoleColorToDrawingColor(txt_color), Name, obj.ToString()); + + public void MsgPastel(ConsoleColor txt_color, string txt) => NativePastelMsg(DrawingColor, ConsoleColorToDrawingColor(txt_color), Name, txt); + + public void MsgPastel(ConsoleColor txt_color, string txt, params object[] args) => NativePastelMsg(DrawingColor, ConsoleColorToDrawingColor(txt_color), Name, string.Format(txt, args)); + + public void MsgPastel(Color txt_color, object obj) => NativePastelMsg(DrawingColor, txt_color, Name, obj.ToString()); + + public void MsgPastel(Color txt_color, string txt) => NativePastelMsg(DrawingColor, txt_color, Name, txt); + + public void MsgPastel(Color txt_color, string txt, params object[] args) => NativePastelMsg(DrawingColor, txt_color, Name, string.Format(txt, args)); + public void Warning(object obj) => NativeWarning(Name, obj.ToString()); + public void Warning(string txt) => NativeWarning(Name, txt); + public void Warning(string txt, params object[] args) => NativeWarning(Name, string.Format(txt, args)); public void Error(object obj) => NativeError(Name, obj.ToString()); + public void Error(string txt) => NativeError(Name, txt); + public void Error(string txt, params object[] args) => NativeError(Name, string.Format(txt, args)); + public void Error(string txt, Exception ex) => NativeError(Name, $"{txt}\n{ex}"); - + public void WriteSpacer() => MelonLogger.WriteSpacer(); + public void WriteLine(int length = 30) => MelonLogger.WriteLine(length); + public void WriteLine(Color color, int length = 30) => MelonLogger.WriteLine(color, length); public void BigError(string txt) => MelonLogger.BigError(Name, txt); @@ -245,6 +335,29 @@ internal static void Internal_Msg(Color namesection_color, Color txt_color, stri Utils.MelonConsole.WriteLine(builder.ToString()); } + internal static void Internal_PastelMsg(Color namesection_color, Color txt_color, string namesection, string txt) + { + // Regex to check for ANSI + string fileTxt = txt; + fileTxt = Regex.Replace(fileTxt, @"(\x1B|\e|\033)\[(.*?)m", ""); + + WriteLogToFile($"[{GetTimeStamp()}] {(namesection is null ? "" : $"[{namesection}] ")}{fileTxt}"); + + StringBuilder builder = new StringBuilder(); + + builder.Append(GetTimestamp(namesection_color == Color.IndianRed && txt_color == Color.IndianRed)); + + if (namesection is not null) + { + builder.Append("[".Pastel(Color.LightGray)); + builder.Append(namesection.Pastel(namesection_color)); + builder.Append("] ".Pastel(Color.LightGray)); + } + + builder.Append(txt.Pastel(txt_color)); + Utils.MelonConsole.WriteLine(builder.ToString()); + } + internal static string GetTimestamp(bool error) { StringBuilder builder = new StringBuilder(); @@ -271,13 +384,10 @@ internal static void Internal_Warning(string namesection, string txt) Internal_Msg(Color.Yellow, Color.Yellow, namesection, txt); } - internal static void Internal_Error(string namesection, string txt) => Internal_Msg(Color.IndianRed, Color.IndianRed, namesection, txt); - internal static void ThrowInternalFailure(string txt) => Assertion.ThrowInternalFailure(txt); - internal static void WriteSpacer() { WriteLogToFile(); @@ -297,7 +407,8 @@ internal static void Internal_PrintModName(Color meloncolor, Color authorcolor, builder.Append(GetTimestamp(false)); builder.Append($"by {author}".Pastel(authorcolor)); - if (additionalCredits is not null) { + if (additionalCredits is not null) + { builder.AppendLine(); builder.Append(GetTimestamp(false)); builder.Append($"Additional credits: {additionalCredits}"); @@ -318,25 +429,33 @@ internal static void Close() CachedLogWriter.Close(); } - [Obsolete("Log is obsolete. Please use Msg instead.")] public static void Log(string txt) => Msg(txt); + [Obsolete("Log is obsolete. Please use Msg instead.")] public static void Log(string txt, params object[] args) => Msg(txt, args); + [Obsolete("Log is obsolete. Please use Msg instead.")] public static void Log(object obj) => Msg(obj); + [Obsolete("Log is obsolete. Please use Msg instead.")] public static void Log(ConsoleColor color, string txt) => Msg(color, txt); + [Obsolete("Log is obsolete. Please use Msg instead.")] public static void Log(ConsoleColor color, string txt, params object[] args) => Msg(color, txt, args); + [Obsolete("Log is obsolete. Please use Msg instead.")] public static void Log(ConsoleColor color, object obj) => Msg(color, obj); + [Obsolete("LogWarning is obsolete. Please use Warning instead.")] public static void LogWarning(string txt) => Warning(txt); + [Obsolete("LogWarning is obsolete. Please use Warning instead.")] public static void LogWarning(string txt, params object[] args) => Warning(txt, args); + [Obsolete("LogError is obsolete. Please use Error instead.")] public static void LogError(string txt) => Error(txt); + [Obsolete("LogError is obsolete. Please use Error instead.")] public static void LogError(string txt, params object[] args) => Error(txt, args); } From 0454b9226c2d78a25305c0d1bf259cde1ab4b9d4 Mon Sep 17 00:00:00 2001 From: HAHOOS Date: Sat, 7 Sep 2024 13:39:23 +0200 Subject: [PATCH 03/20] Small change --- MelonLoader/Utils/MelonLogger.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MelonLoader/Utils/MelonLogger.cs b/MelonLoader/Utils/MelonLogger.cs index 35bfa98fe..117a69372 100644 --- a/MelonLoader/Utils/MelonLogger.cs +++ b/MelonLoader/Utils/MelonLogger.cs @@ -338,8 +338,7 @@ internal static void Internal_Msg(Color namesection_color, Color txt_color, stri internal static void Internal_PastelMsg(Color namesection_color, Color txt_color, string namesection, string txt) { // Regex to check for ANSI - string fileTxt = txt; - fileTxt = Regex.Replace(fileTxt, @"(\x1B|\e|\033)\[(.*?)m", ""); + string fileTxt = Regex.Replace(txt, @"(\x1B|\e|\033)\[(.*?)m", ""); WriteLogToFile($"[{GetTimeStamp()}] {(namesection is null ? "" : $"[{namesection}] ")}{fileTxt}"); From 679d4688f3991333f2541ba01573af460363b56b Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Wed, 11 Sep 2024 20:25:00 -0600 Subject: [PATCH 04/20] Fixed Dependency Graph Assembly Resolving --- MelonLoader/InternalUtils/DependencyGraph.cs | 22 +++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/MelonLoader/InternalUtils/DependencyGraph.cs b/MelonLoader/InternalUtils/DependencyGraph.cs index 5b4ae8de7..db9b81f11 100644 --- a/MelonLoader/InternalUtils/DependencyGraph.cs +++ b/MelonLoader/InternalUtils/DependencyGraph.cs @@ -100,6 +100,7 @@ private DependencyGraph(IList melons) dependencyVertex.dependents.Add(melonVertex); } else if (!TryLoad(dependency) + && !TryResolve(dependency) && !optionalDependencies.Contains(dependency.Name) && !missingDependencies.Contains(dependency)) missingDependencies.Add(dependency); @@ -116,6 +117,7 @@ private DependencyGraph(IList melons) dependencyVertex.dependents.Add(melonVertex); } else if (!TryLoad(dependency) + && !TryResolve(dependency) && !missingDependencies.Contains(dependency)) missingDependencies.Add(dependency); } @@ -140,6 +142,24 @@ private DependencyGraph(IList melons) // Returns true if 'assembly' was already loaded or could be loaded, false if the required assembly was missing. private static bool TryLoad(AssemblyName assembly) + { + try + { + Assembly asm = Assembly.Load(assembly); + if (asm == null) + return false; + return true; + } + catch (FileNotFoundException) { return false; } + catch (Exception ex) + { + MelonLogger.Error("Loading Melon Dependency Failed: " + ex); + return false; + } + } + + // Returns true if 'assembly' was already resolved or could be resolved, false if the required assembly was missing. + private static bool TryResolve(AssemblyName assembly) { try { @@ -155,7 +175,7 @@ private static bool TryLoad(AssemblyName assembly) catch (FileNotFoundException) { return false; } catch (Exception ex) { - MelonLogger.Error("Loading Melon Dependency Failed: " + ex); + MelonLogger.Error("Resolving Melon Dependency Failed: " + ex); return false; } } From c75f977a331d0bdaa2c9944f4f85f6654acf3cd4 Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Wed, 11 Sep 2024 20:50:53 -0600 Subject: [PATCH 05/20] Switched to Il2CppInterop 1.4.6-ci.493 --- .../Il2CppAssemblyGenerator.csproj | 6 +++--- Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj | 6 +++--- MelonLoader/Fixes/Il2CppInteropFixes.cs | 2 +- MelonLoader/MelonLoader.csproj | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj index 5bf17fd1a..e59b44cdc 100644 --- a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj +++ b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj @@ -15,9 +15,9 @@ - - - + + + diff --git a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj index cd5fcb09a..3bceaed14 100644 --- a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj +++ b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj @@ -41,9 +41,9 @@ - - - + + + diff --git a/MelonLoader/Fixes/Il2CppInteropFixes.cs b/MelonLoader/Fixes/Il2CppInteropFixes.cs index cc57a5923..f55816620 100644 --- a/MelonLoader/Fixes/Il2CppInteropFixes.cs +++ b/MelonLoader/Fixes/Il2CppInteropFixes.cs @@ -21,9 +21,9 @@ namespace MelonLoader.Fixes { // fixes: https://github.com/BepInEx/Il2CppInterop/pull/103 - // fixes: https://github.com/BepInEx/Il2CppInterop/pull/134 // fixes: https://github.com/BepInEx/Il2CppInterop/issues/135 // reverts: https://github.com/BepInEx/Il2CppInterop/commit/18e58ef5db42a71d6012ab0387b107a4132101eb + // fixes the rest of: https://github.com/BepInEx/Il2CppInterop/pull/134 internal unsafe static class Il2CppInteropFixes { private static Dictionary> _assemblyLookup = new(); diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index 64ab2a96b..4292d16a6 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -49,10 +49,10 @@ - - - - + + + + From e26b2ff2c80cec22b2df2970cffbb45d639c82e3 Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Wed, 11 Sep 2024 22:54:01 -0600 Subject: [PATCH 06/20] Update Core.cs --- Dependencies/Il2CppAssemblyGenerator/Core.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dependencies/Il2CppAssemblyGenerator/Core.cs b/Dependencies/Il2CppAssemblyGenerator/Core.cs index 43b0f986b..8f35de952 100644 --- a/Dependencies/Il2CppAssemblyGenerator/Core.cs +++ b/Dependencies/Il2CppAssemblyGenerator/Core.cs @@ -60,7 +60,7 @@ private static int Run() RemoteAPI.Contact(); cpp2il = new Cpp2IL(); - cpp2il_scrs = new Cpp2IL_StrippedCodeRegSupport(cpp2il); + //cpp2il_scrs = new Cpp2IL_StrippedCodeRegSupport(cpp2il); il2cppinterop = new Packages.Il2CppInterop(); unitydependencies = new UnityDependencies(); @@ -73,7 +73,7 @@ private static int Run() Logger.Msg($"Using Deobfuscation Regex = {(string.IsNullOrEmpty(deobfuscationRegex.Regex) ? "null" : deobfuscationRegex.Regex)}"); if (!cpp2il.Setup() - || !cpp2il_scrs.Setup() + //|| !cpp2il_scrs.Setup() || !il2cppinterop.Setup() || !unitydependencies.Setup() || !deobfuscationMap.Setup()) From 2a1d2310df061ce97af25047e644ac05889fce0e Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Thu, 12 Sep 2024 17:07:20 -0600 Subject: [PATCH 07/20] Fixed an issue with Support Module components not attempting to use direct references on Il2Cpp --- Dependencies/SupportModules/Component.cs | 58 ++++++++++++++++-------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/Dependencies/SupportModules/Component.cs b/Dependencies/SupportModules/Component.cs index d220c80e0..90f034561 100644 --- a/Dependencies/SupportModules/Component.cs +++ b/Dependencies/SupportModules/Component.cs @@ -1,10 +1,10 @@ using System; +using System.Reflection; +using UnityEngine; + #if SM_Il2Cpp using Il2CppInterop.Runtime; -#else -using System.Reflection; #endif -using UnityEngine; namespace MelonLoader.Support { @@ -12,13 +12,14 @@ internal class SM_Component : MonoBehaviour { private bool isQuitting; private static bool hadError; + private static bool useGeneratedAssembly = true; + + private static MethodInfo SetAsLastSiblingMethod; #if SM_Il2Cpp private delegate bool SetAsLastSiblingDelegate(IntPtr transformptr); private static SetAsLastSiblingDelegate SetAsLastSiblingDelegateField; public SM_Component(IntPtr value) : base(value) { } -#else - private static MethodInfo SetAsLastSiblingMethod; #endif static SM_Component() @@ -26,21 +27,21 @@ static SM_Component() try { #if SM_Il2Cpp + SetAsLastSiblingMethod = typeof(Transform).GetMethod("SetAsLastSibling", BindingFlags.Public | BindingFlags.Instance); + if (SetAsLastSiblingMethod != null) + return; + + useGeneratedAssembly = false; SetAsLastSiblingDelegateField = IL2CPP.ResolveICall("UnityEngine.Transform::SetAsLastSibling"); if (SetAsLastSiblingDelegateField == null) throw new Exception("Unable to find Internal Call for UnityEngine.Transform::SetAsLastSibling"); #else SetAsLastSiblingMethod = typeof(Transform).GetMethod("SetAsLastSibling", BindingFlags.Public | BindingFlags.Instance); if (SetAsLastSiblingMethod == null) - throw new Exception("Unable to find Internal Call for UnityEngine.Transform::SetAsLastSibling"); + throw new Exception("Unable to find UnityEngine.Transform::SetAsLastSibling"); #endif } - catch (Exception ex) - { - hadError = true; - MelonLogger.Warning($"Exception while Getting Transform.SetAsLastSibling: {ex}"); - MelonLogger.Warning("Melon Events might run before some MonoBehaviour Events"); - } + catch (Exception ex) { LogError("Getting UnityEngine.Transform::SetAsLastSibling", ex); } } internal static void Create() @@ -51,14 +52,24 @@ internal static void Create() Main.obj = new GameObject(); DontDestroyOnLoad(Main.obj); Main.obj.hideFlags = HideFlags.DontSave; + #if SM_Il2Cpp Main.component = Main.obj.AddComponent(Il2CppType.Of()).TryCast(); #else Main.component = (SM_Component)Main.obj.AddComponent(typeof(SM_Component)); #endif + Main.component.SiblingFix(); } + private static void LogError(string cat, Exception ex) + { + hadError = true; + useGeneratedAssembly = false; + MelonLogger.Warning($"Exception while {cat}: {ex}"); + MelonLogger.Warning("Melon Events might run before some MonoBehaviour Events"); + } + private void SiblingFix() { if (hadError) @@ -66,19 +77,30 @@ private void SiblingFix() try { + if (useGeneratedAssembly) + { + gameObject.transform.SetAsLastSibling(); + transform.SetAsLastSibling(); + return; + } + #if SM_Il2Cpp SetAsLastSiblingDelegateField(IL2CPP.Il2CppObjectBaseToPtrNotNull(gameObject.transform)); SetAsLastSiblingDelegateField(IL2CPP.Il2CppObjectBaseToPtrNotNull(transform)); -#else - SetAsLastSiblingMethod?.Invoke(gameObject.transform, new object[0]); - SetAsLastSiblingMethod?.Invoke(transform, new object[0]); #endif } catch (Exception ex) { - hadError = true; - MelonLogger.Warning($"Exception while Invoking Transform.SetAsLastSibling: {ex}"); - MelonLogger.Warning("Melon Events might run before some MonoBehaviour Events"); +#if SM_Il2Cpp + if (useGeneratedAssembly) + { + useGeneratedAssembly = false; + SiblingFix(); + return; + } +#endif + + LogError("Invoking UnityEngine.Transform::SetAsLastSibling", ex); } } From 5741f4b94e969eedd806bc37c856eaff23c7b0d9 Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Thu, 12 Sep 2024 23:01:47 -0600 Subject: [PATCH 08/20] Fixed an issue with Unity Injected/Inlined Internal Calls breaking Internal Call Resolve Requests --- Dependencies/SupportModules/Il2Cpp/Main.cs | 12 +++++ MelonLoader/Fixes/Il2CppInteropFixes.cs | 57 +++++++++++++++++++--- MelonLoader/MelonUtils.cs | 19 +++++++- 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/Dependencies/SupportModules/Il2Cpp/Main.cs b/Dependencies/SupportModules/Il2Cpp/Main.cs index c604ace89..acb58a23a 100644 --- a/Dependencies/SupportModules/Il2Cpp/Main.cs +++ b/Dependencies/SupportModules/Il2Cpp/Main.cs @@ -13,6 +13,8 @@ using Il2CppInterop.Common; using Il2CppInterop.Runtime.InteropTypes; using Microsoft.Extensions.Logging; +using MelonLoader.Utils; +using System.IO; namespace MelonLoader.Support { @@ -29,6 +31,16 @@ internal static class Main private static ISupportModule_To Initialize(ISupportModule_From interface_from) { Interface = interface_from; + + foreach (var file in Directory.GetFiles(MelonEnvironment.Il2CppAssembliesDirectory, "*.dll")) + { + try + { + Assembly.LoadFrom(file); + } + catch { } + } + UnityMappers.RegisterMappers(); Il2CppInteropRuntime runtime = Il2CppInteropRuntime.Create(new() diff --git a/MelonLoader/Fixes/Il2CppInteropFixes.cs b/MelonLoader/Fixes/Il2CppInteropFixes.cs index f55816620..6a7ffe209 100644 --- a/MelonLoader/Fixes/Il2CppInteropFixes.cs +++ b/MelonLoader/Fixes/Il2CppInteropFixes.cs @@ -17,6 +17,7 @@ using MelonLoader.Utils; using Il2CppInterop.Generator.Contexts; using AsmResolver.DotNet; +using MonoMod.Utils; namespace MelonLoader.Fixes { @@ -28,6 +29,7 @@ internal unsafe static class Il2CppInteropFixes { private static Dictionary> _assemblyLookup = new(); private static Dictionary _typeLookup = new(); + private static Dictionary _typeNameLookup = new(); private static MethodInfo _getType; private static MethodInfo _fixedFindType; @@ -58,6 +60,8 @@ internal unsafe static class Il2CppInteropFixes private static MethodInfo _rewriteGlobalContext_GetNewAssemblyForOriginal_Prefix; private static MethodInfo _rewriteGlobalContext_TryGetNewTypeForOriginal; private static MethodInfo _rewriteGlobalContext_TryGetNewTypeForOriginal_Prefix; + private static MethodInfo _il2cpp_resolve_icall; + private static MethodInfo _il2cpp_resolve_icall_Postfix; internal static void Install() { @@ -68,6 +72,7 @@ internal static void Install() Type classInjectorType = typeof(ClassInjector); Type ilGeneratorEx = typeof(ILGeneratorEx); Type rewriteGlobalContextType = typeof(RewriteGlobalContext); + Type il2cppType = typeof(IL2CPP); Type injectorHelpersType = classInjectorType.Assembly.GetType("Il2CppInterop.Runtime.Injection.InjectorHelpers"); if (injectorHelpersType == null) @@ -137,6 +142,10 @@ internal static void Install() if (_rewriteGlobalContext_TryGetNewTypeForOriginal == null) throw new Exception("Failed to get RewriteGlobalContext.TryGetNewTypeForOriginal"); + _il2cpp_resolve_icall = il2cppType.GetMethod("il2cpp_resolve_icall", BindingFlags.Public | BindingFlags.Static); + if (_il2cpp_resolve_icall == null) + throw new Exception("Failed to get IL2CPP.il2cpp_resolve_icall"); + _fixedFindType = thisType.GetMethod(nameof(FixedFindType), BindingFlags.NonPublic | BindingFlags.Static); _fixedAddTypeToLookup = thisType.GetMethod(nameof(FixedAddTypeToLookup), BindingFlags.NonPublic | BindingFlags.Static); _fixedIsByRef = thisType.GetMethod(nameof(FixedIsByRef), BindingFlags.NonPublic | BindingFlags.Static); @@ -152,6 +161,7 @@ internal static void Install() _rewriteGlobalContext_Dispose_Prefix = thisType.GetMethod(nameof(RewriteGlobalContext_Dispose_Prefix), BindingFlags.NonPublic | BindingFlags.Static); _rewriteGlobalContext_GetNewAssemblyForOriginal_Prefix = thisType.GetMethod(nameof(RewriteGlobalContext_GetNewAssemblyForOriginal_Prefix), BindingFlags.NonPublic | BindingFlags.Static); _rewriteGlobalContext_TryGetNewTypeForOriginal_Prefix = thisType.GetMethod(nameof(RewriteGlobalContext_TryGetNewTypeForOriginal_Prefix), BindingFlags.NonPublic | BindingFlags.Static); + _il2cpp_resolve_icall_Postfix = thisType.GetMethod(nameof(il2cpp_resolve_icall_Postfix), BindingFlags.NonPublic | BindingFlags.Static); MelonDebug.Msg("Patching Il2CppInterop ClassInjector.SystemTypeFromIl2CppType..."); Core.HarmonyInstance.Patch(_systemTypeFromIl2CppType, @@ -200,6 +210,10 @@ internal static void Install() MelonDebug.Msg("Patching Il2CppInterop RewriteGlobalContext.TryGetNewTypeForOriginal..."); Core.HarmonyInstance.Patch(_rewriteGlobalContext_TryGetNewTypeForOriginal, new HarmonyMethod(_rewriteGlobalContext_TryGetNewTypeForOriginal_Prefix)); + + MelonDebug.Msg("Patching Il2CppInterop IL2CPP.il2cpp_resolve_icall..."); + Core.HarmonyInstance.Patch(_il2cpp_resolve_icall, + null, new HarmonyMethod(_il2cpp_resolve_icall_Postfix)); } catch (Exception e) { @@ -235,12 +249,29 @@ private static Type FixedFindType(string il2CppTypeFullName) { if (string.IsNullOrEmpty(il2CppTypeFullName)) return null; - Type returnType = Type.GetType($"Il2Cpp.{il2CppTypeFullName}"); - if (returnType == null) - returnType = Type.GetType($"Il2Cpp{il2CppTypeFullName}"); - if (returnType == null) - returnType = Type.GetType(il2CppTypeFullName); - return returnType; + + //if (_typeNameLookup.TryGetValue(il2CppTypeFullName, out Type result)) + // return result; + + foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) + { + if (a == null) + continue; + + Type result = a.GetValidType($"Il2Cpp.{il2CppTypeFullName}"); + if (result == null) + result = a.GetValidType($"Il2Cpp{il2CppTypeFullName}"); + if (result == null) + result = a.GetValidType(il2CppTypeFullName); + + if (result != null) + { + //_typeNameLookup[result.FullName] = result; + return result; + } + } + + return null; } private static void FixedAddTypeToLookup(Type type, IntPtr typePointer) @@ -256,6 +287,20 @@ private static void FixedAddTypeToLookup(Type type, IntPtr typePointer) _typeLookup.Add(typePointer, type); } + [DllImport("GameAssembly", EntryPoint = "il2cpp_resolve_icall", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + private static extern IntPtr il2cpp_resolve_icall_original([MarshalAs(UnmanagedType.LPStr)] string name); + private const string _unityInlineInjectedSuffix = "_Injected"; + private static void il2cpp_resolve_icall_Postfix(string __0, ref IntPtr __result) + { + if (__result != IntPtr.Zero) + return; + + if (__0.EndsWith(_unityInlineInjectedSuffix)) + __result = il2cpp_resolve_icall_original(__0.Substring(0, __0.Length - _unityInlineInjectedSuffix.Length)); + else + __result = il2cpp_resolve_icall_original($"{__0}{_unityInlineInjectedSuffix}"); + } + private static bool EmitObjectToPointer_Prefix(bool __7, ref bool __8) { __8 = __7; diff --git a/MelonLoader/MelonUtils.cs b/MelonLoader/MelonUtils.cs index e6c897668..d2b03e8e5 100644 --- a/MelonLoader/MelonUtils.cs +++ b/MelonLoader/MelonUtils.cs @@ -302,7 +302,7 @@ public static IEnumerable GetValidTypes(this Assembly asm, LemonFunc GetValidTypes(this Assembly asm, LemonFunc (x != null) && (predicate == null || predicate(x))); } + public static Type GetValidType(this Assembly asm, string typeName) + => GetValidType(asm, typeName, null); + + public static Type GetValidType(this Assembly asm, string typeName, LemonFunc predicate) + { + Type x = null; + try { x = asm.GetType(typeName); } + catch (Exception ex) + { + MelonLogger.Error($"Failed to get type {typeName} from assembly {asm.FullName} due to: {ex.Message}", ex); + x = null; + } + if ((x != null) && (predicate == null || predicate(x))) + return x; + return null; + } + public static bool IsNotImplemented(this MethodBase methodBase) { if (methodBase == null) From c83a511ec4e9cc04d428d68094f2eab6612a4aa8 Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Fri, 13 Sep 2024 11:21:44 -0600 Subject: [PATCH 09/20] Revert and Cleanup --- Dependencies/SupportModules/Il2Cpp/Main.cs | 9 --- .../Fixes/DotnetAssemblyLoadContextFix.cs | 6 +- MelonLoader/Fixes/Il2CppInteropFixes.cs | 59 +++---------------- 3 files changed, 12 insertions(+), 62 deletions(-) diff --git a/Dependencies/SupportModules/Il2Cpp/Main.cs b/Dependencies/SupportModules/Il2Cpp/Main.cs index acb58a23a..a9a9942e2 100644 --- a/Dependencies/SupportModules/Il2Cpp/Main.cs +++ b/Dependencies/SupportModules/Il2Cpp/Main.cs @@ -32,15 +32,6 @@ private static ISupportModule_To Initialize(ISupportModule_From interface_from) { Interface = interface_from; - foreach (var file in Directory.GetFiles(MelonEnvironment.Il2CppAssembliesDirectory, "*.dll")) - { - try - { - Assembly.LoadFrom(file); - } - catch { } - } - UnityMappers.RegisterMappers(); Il2CppInteropRuntime runtime = Il2CppInteropRuntime.Create(new() diff --git a/MelonLoader/Fixes/DotnetAssemblyLoadContextFix.cs b/MelonLoader/Fixes/DotnetAssemblyLoadContextFix.cs index 0884b85fb..a5ee6745d 100644 --- a/MelonLoader/Fixes/DotnetAssemblyLoadContextFix.cs +++ b/MelonLoader/Fixes/DotnetAssemblyLoadContextFix.cs @@ -42,8 +42,8 @@ internal static void Install() public static bool PreAssemblyLoad(byte[] rawAssembly, byte[] rawSymbolStore, ref Assembly __result) { - if(MelonDebug.IsEnabled() && !Environment.StackTrace.Contains("HarmonyLib")) - MelonDebug.Msg($"[.NET AssemblyLoadContext Fix] Redirecting Assembly.Load call with {rawAssembly.Length}-byte assembly to AssemblyLoadContext.Default. Mod Devs: You may wish to use this explictly."); + //if(MelonDebug.IsEnabled() && !Environment.StackTrace.Contains("HarmonyLib")) + // MelonDebug.Msg($"[.NET AssemblyLoadContext Fix] Redirecting Assembly.Load call with {rawAssembly.Length}-byte assembly to AssemblyLoadContext.Default. Mod Devs: You may wish to use this explictly."); var (ok, reason) = AssemblyVerifier.VerifyByteArray(rawAssembly); if (!ok) @@ -59,7 +59,7 @@ public static bool PreAssemblyLoad(byte[] rawAssembly, byte[] rawSymbolStore, re public static bool PreAssemblyLoadFile(string path, ref Assembly __result) { - MelonDebug.Msg($"[.NET AssemblyLoadContext Fix] Redirecting Assembly.LoadFile({path}) call to AssemblyLoadContext.Default.LoadFromAssemblyPath. Mod Devs: You may wish to use this explictly."); + //MelonDebug.Msg($"[.NET AssemblyLoadContext Fix] Redirecting Assembly.LoadFile({path}) call to AssemblyLoadContext.Default.LoadFromAssemblyPath. Mod Devs: You may wish to use this explictly."); string normalizedPath = Path.GetFullPath(path); diff --git a/MelonLoader/Fixes/Il2CppInteropFixes.cs b/MelonLoader/Fixes/Il2CppInteropFixes.cs index 6a7ffe209..7f3be6977 100644 --- a/MelonLoader/Fixes/Il2CppInteropFixes.cs +++ b/MelonLoader/Fixes/Il2CppInteropFixes.cs @@ -60,8 +60,6 @@ internal unsafe static class Il2CppInteropFixes private static MethodInfo _rewriteGlobalContext_GetNewAssemblyForOriginal_Prefix; private static MethodInfo _rewriteGlobalContext_TryGetNewTypeForOriginal; private static MethodInfo _rewriteGlobalContext_TryGetNewTypeForOriginal_Prefix; - private static MethodInfo _il2cpp_resolve_icall; - private static MethodInfo _il2cpp_resolve_icall_Postfix; internal static void Install() { @@ -72,7 +70,6 @@ internal static void Install() Type classInjectorType = typeof(ClassInjector); Type ilGeneratorEx = typeof(ILGeneratorEx); Type rewriteGlobalContextType = typeof(RewriteGlobalContext); - Type il2cppType = typeof(IL2CPP); Type injectorHelpersType = classInjectorType.Assembly.GetType("Il2CppInterop.Runtime.Injection.InjectorHelpers"); if (injectorHelpersType == null) @@ -142,10 +139,6 @@ internal static void Install() if (_rewriteGlobalContext_TryGetNewTypeForOriginal == null) throw new Exception("Failed to get RewriteGlobalContext.TryGetNewTypeForOriginal"); - _il2cpp_resolve_icall = il2cppType.GetMethod("il2cpp_resolve_icall", BindingFlags.Public | BindingFlags.Static); - if (_il2cpp_resolve_icall == null) - throw new Exception("Failed to get IL2CPP.il2cpp_resolve_icall"); - _fixedFindType = thisType.GetMethod(nameof(FixedFindType), BindingFlags.NonPublic | BindingFlags.Static); _fixedAddTypeToLookup = thisType.GetMethod(nameof(FixedAddTypeToLookup), BindingFlags.NonPublic | BindingFlags.Static); _fixedIsByRef = thisType.GetMethod(nameof(FixedIsByRef), BindingFlags.NonPublic | BindingFlags.Static); @@ -161,8 +154,7 @@ internal static void Install() _rewriteGlobalContext_Dispose_Prefix = thisType.GetMethod(nameof(RewriteGlobalContext_Dispose_Prefix), BindingFlags.NonPublic | BindingFlags.Static); _rewriteGlobalContext_GetNewAssemblyForOriginal_Prefix = thisType.GetMethod(nameof(RewriteGlobalContext_GetNewAssemblyForOriginal_Prefix), BindingFlags.NonPublic | BindingFlags.Static); _rewriteGlobalContext_TryGetNewTypeForOriginal_Prefix = thisType.GetMethod(nameof(RewriteGlobalContext_TryGetNewTypeForOriginal_Prefix), BindingFlags.NonPublic | BindingFlags.Static); - _il2cpp_resolve_icall_Postfix = thisType.GetMethod(nameof(il2cpp_resolve_icall_Postfix), BindingFlags.NonPublic | BindingFlags.Static); - + MelonDebug.Msg("Patching Il2CppInterop ClassInjector.SystemTypeFromIl2CppType..."); Core.HarmonyInstance.Patch(_systemTypeFromIl2CppType, new HarmonyMethod(_systemTypeFromIl2CppType_Prefix), @@ -210,10 +202,6 @@ internal static void Install() MelonDebug.Msg("Patching Il2CppInterop RewriteGlobalContext.TryGetNewTypeForOriginal..."); Core.HarmonyInstance.Patch(_rewriteGlobalContext_TryGetNewTypeForOriginal, new HarmonyMethod(_rewriteGlobalContext_TryGetNewTypeForOriginal_Prefix)); - - MelonDebug.Msg("Patching Il2CppInterop IL2CPP.il2cpp_resolve_icall..."); - Core.HarmonyInstance.Patch(_il2cpp_resolve_icall, - null, new HarmonyMethod(_il2cpp_resolve_icall_Postfix)); } catch (Exception e) { @@ -249,29 +237,14 @@ private static Type FixedFindType(string il2CppTypeFullName) { if (string.IsNullOrEmpty(il2CppTypeFullName)) return null; - - //if (_typeNameLookup.TryGetValue(il2CppTypeFullName, out Type result)) - // return result; - - foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) - { - if (a == null) - continue; - - Type result = a.GetValidType($"Il2Cpp.{il2CppTypeFullName}"); - if (result == null) - result = a.GetValidType($"Il2Cpp{il2CppTypeFullName}"); - if (result == null) - result = a.GetValidType(il2CppTypeFullName); - - if (result != null) - { - //_typeNameLookup[result.FullName] = result; - return result; - } - } - - return null; + + Type returnType = Type.GetType($"Il2Cpp.{il2CppTypeFullName}"); + if (returnType == null) + returnType = Type.GetType($"Il2Cpp{il2CppTypeFullName}"); + if (returnType == null) + returnType = Type.GetType(il2CppTypeFullName); + + return returnType; } private static void FixedAddTypeToLookup(Type type, IntPtr typePointer) @@ -287,20 +260,6 @@ private static void FixedAddTypeToLookup(Type type, IntPtr typePointer) _typeLookup.Add(typePointer, type); } - [DllImport("GameAssembly", EntryPoint = "il2cpp_resolve_icall", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - private static extern IntPtr il2cpp_resolve_icall_original([MarshalAs(UnmanagedType.LPStr)] string name); - private const string _unityInlineInjectedSuffix = "_Injected"; - private static void il2cpp_resolve_icall_Postfix(string __0, ref IntPtr __result) - { - if (__result != IntPtr.Zero) - return; - - if (__0.EndsWith(_unityInlineInjectedSuffix)) - __result = il2cpp_resolve_icall_original(__0.Substring(0, __0.Length - _unityInlineInjectedSuffix.Length)); - else - __result = il2cpp_resolve_icall_original($"{__0}{_unityInlineInjectedSuffix}"); - } - private static bool EmitObjectToPointer_Prefix(bool __7, ref bool __8) { __8 = __7; From 1c72d4a6e9ec5423fc150f0653a5528ccb0fba3f Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Fri, 13 Sep 2024 22:43:37 -0600 Subject: [PATCH 10/20] Updated Il2CppInterop to 1.4.6-ci.516 --- .../Il2CppAssemblyGenerator.csproj | 6 +++--- Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj | 6 +++--- MelonLoader/MelonLoader.csproj | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj index e59b44cdc..94aa39b41 100644 --- a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj +++ b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj @@ -15,9 +15,9 @@ - - - + + + diff --git a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj index 3bceaed14..90183bb14 100644 --- a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj +++ b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj @@ -41,9 +41,9 @@ - - - + + + diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index 4292d16a6..890df5548 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -49,10 +49,10 @@ - - - - + + + + From ee2b4a2e081712fb8333b4aefd613d6dbc9c24f0 Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Sat, 14 Sep 2024 14:28:19 -0600 Subject: [PATCH 11/20] Fixed an issue with Unity Injected ICalls that caused breakage in Mods/Plugins when attempting to manually resolve --- Dependencies/SupportModules/Il2Cpp/Main.cs | 11 +- MelonLoader/Core.cs | 2 + MelonLoader/Fixes/Il2CppInteropFixes.cs | 41 ++-- MelonLoader/Fixes/InjectedInternalCalls.cs | 258 +++++++++++++++++++++ 4 files changed, 298 insertions(+), 14 deletions(-) create mode 100644 MelonLoader/Fixes/InjectedInternalCalls.cs diff --git a/Dependencies/SupportModules/Il2Cpp/Main.cs b/Dependencies/SupportModules/Il2Cpp/Main.cs index a9a9942e2..bf4bf2ef2 100644 --- a/Dependencies/SupportModules/Il2Cpp/Main.cs +++ b/Dependencies/SupportModules/Il2Cpp/Main.cs @@ -30,7 +30,16 @@ internal static class Main private static ISupportModule_To Initialize(ISupportModule_From interface_from) { - Interface = interface_from; + Interface = interface_from; + + foreach (var file in Directory.GetFiles(MelonEnvironment.Il2CppAssembliesDirectory, "*.dll")) + { + try + { + Assembly.LoadFrom(file); + } + catch { } + } UnityMappers.RegisterMappers(); diff --git a/MelonLoader/Core.cs b/MelonLoader/Core.cs index d6c35af24..b69f6e4d8 100644 --- a/MelonLoader/Core.cs +++ b/MelonLoader/Core.cs @@ -94,6 +94,7 @@ internal static int Initialize() #if NET6_0_OR_GREATER Fixes.Il2CppInteropFixes.Install(); + Fixes.InjectedInternalCalls.Install(); #endif PatchShield.Install(); @@ -190,6 +191,7 @@ internal static void Quit() #if NET6_0_OR_GREATER Fixes.Il2CppInteropFixes.Shutdown(); + Fixes.InjectedInternalCalls.Shutdown(); #endif MelonLogger.Flush(); diff --git a/MelonLoader/Fixes/Il2CppInteropFixes.cs b/MelonLoader/Fixes/Il2CppInteropFixes.cs index 7f3be6977..73eed5036 100644 --- a/MelonLoader/Fixes/Il2CppInteropFixes.cs +++ b/MelonLoader/Fixes/Il2CppInteropFixes.cs @@ -17,7 +17,6 @@ using MelonLoader.Utils; using Il2CppInterop.Generator.Contexts; using AsmResolver.DotNet; -using MonoMod.Utils; namespace MelonLoader.Fixes { @@ -70,6 +69,7 @@ internal static void Install() Type classInjectorType = typeof(ClassInjector); Type ilGeneratorEx = typeof(ILGeneratorEx); Type rewriteGlobalContextType = typeof(RewriteGlobalContext); + Type il2cppType = typeof(IL2CPP); Type injectorHelpersType = classInjectorType.Assembly.GetType("Il2CppInterop.Runtime.Injection.InjectorHelpers"); if (injectorHelpersType == null) @@ -137,7 +137,7 @@ internal static void Install() _rewriteGlobalContext_TryGetNewTypeForOriginal = rewriteGlobalContextType.GetMethod("TryGetNewTypeForOriginal", BindingFlags.Public | BindingFlags.Instance); if (_rewriteGlobalContext_TryGetNewTypeForOriginal == null) - throw new Exception("Failed to get RewriteGlobalContext.TryGetNewTypeForOriginal"); + throw new Exception("Failed to get RewriteGlobalContext.TryGetNewTypeForOriginal"); _fixedFindType = thisType.GetMethod(nameof(FixedFindType), BindingFlags.NonPublic | BindingFlags.Static); _fixedAddTypeToLookup = thisType.GetMethod(nameof(FixedAddTypeToLookup), BindingFlags.NonPublic | BindingFlags.Static); @@ -154,7 +154,7 @@ internal static void Install() _rewriteGlobalContext_Dispose_Prefix = thisType.GetMethod(nameof(RewriteGlobalContext_Dispose_Prefix), BindingFlags.NonPublic | BindingFlags.Static); _rewriteGlobalContext_GetNewAssemblyForOriginal_Prefix = thisType.GetMethod(nameof(RewriteGlobalContext_GetNewAssemblyForOriginal_Prefix), BindingFlags.NonPublic | BindingFlags.Static); _rewriteGlobalContext_TryGetNewTypeForOriginal_Prefix = thisType.GetMethod(nameof(RewriteGlobalContext_TryGetNewTypeForOriginal_Prefix), BindingFlags.NonPublic | BindingFlags.Static); - + MelonDebug.Msg("Patching Il2CppInterop ClassInjector.SystemTypeFromIl2CppType..."); Core.HarmonyInstance.Patch(_systemTypeFromIl2CppType, new HarmonyMethod(_systemTypeFromIl2CppType_Prefix), @@ -233,18 +233,33 @@ internal static void Shutdown() private static bool FixedIsByRef(Type type) => (type != null) && (type.IsByRef || type.IsPointer); - private static Type FixedFindType(string il2CppTypeFullName) + internal static Type FixedFindType(string typeFullName) { - if (string.IsNullOrEmpty(il2CppTypeFullName)) + if (string.IsNullOrEmpty(typeFullName)) return null; - - Type returnType = Type.GetType($"Il2Cpp.{il2CppTypeFullName}"); - if (returnType == null) - returnType = Type.GetType($"Il2Cpp{il2CppTypeFullName}"); - if (returnType == null) - returnType = Type.GetType(il2CppTypeFullName); - - return returnType; + + if (_typeNameLookup.TryGetValue(typeFullName, out Type result)) + return result; + + foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) + { + if (a == null) + continue; + + result = a.GetValidType($"Il2Cpp.{typeFullName}"); + if (result == null) + result = a.GetValidType($"Il2Cpp{typeFullName}"); + if (result == null) + result = a.GetValidType(typeFullName); + + if (result != null) + { + _typeNameLookup[result.FullName] = result; + return result; + } + } + + return null; } private static void FixedAddTypeToLookup(Type type, IntPtr typePointer) diff --git a/MelonLoader/Fixes/InjectedInternalCalls.cs b/MelonLoader/Fixes/InjectedInternalCalls.cs new file mode 100644 index 000000000..f60fe2f3a --- /dev/null +++ b/MelonLoader/Fixes/InjectedInternalCalls.cs @@ -0,0 +1,258 @@ +#if NET6_0_OR_GREATER + +using HarmonyLib; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.InteropTypes; +using MonoMod.RuntimeDetour; +using MonoMod.Utils; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +//using System.Runtime.InteropServices; + +namespace MelonLoader.Fixes +{ + internal static class InjectedInternalCalls + { + private static Dictionary _lookup = new(); + private const string _unityInjectedSuffix = "_Injected"; + private static MethodInfo _il2cpp_resolve_icall; + private static MethodInfo _il2cpp_resolve_icall_Postfix; + + //[DllImport("GameAssembly", EntryPoint = "il2cpp_resolve_icall", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + //private static extern IntPtr il2cpp_resolve_icall_original([MarshalAs(UnmanagedType.LPStr)] string name); + + internal static void Install() + { + try + { + Type il2cppType = typeof(IL2CPP); + Type thisType = typeof(InjectedInternalCalls); + + _il2cpp_resolve_icall = il2cppType.GetMethod("il2cpp_resolve_icall", BindingFlags.Public | BindingFlags.Static); + if (_il2cpp_resolve_icall == null) + throw new Exception("Failed to get IL2CPP.il2cpp_resolve_icall"); + + _il2cpp_resolve_icall_Postfix = thisType.GetMethod(nameof(il2cpp_resolve_icall_Postfix), BindingFlags.NonPublic | BindingFlags.Static); + + MelonDebug.Msg("Patching Il2CppInterop IL2CPP.il2cpp_resolve_icall..."); + Core.HarmonyInstance.Patch(_il2cpp_resolve_icall, + null, new HarmonyMethod(_il2cpp_resolve_icall_Postfix)); + } + catch (Exception e) + { + MelonLogger.Error(e); + } + } + + internal static void Shutdown() + { + if (_lookup != null) + { + if (_lookup.Count > 0) + _lookup.Clear(); + _lookup = null; + } + } + + private static void il2cpp_resolve_icall_Postfix(string __0, ref IntPtr __result) + { + // Found the ICall + if (__result != IntPtr.Zero) + return; + + // Needs Resolving + __result = Resolve(__0); + } + + /* + private static IntPtr Resolve(IntPtr signature) + { + // Convert Pointer to String + string signatureStr = IL2CPP.Il2CppStringToManaged(signature); + if (string.IsNullOrEmpty(signatureStr)) + return IntPtr.Zero; + + // Resolve to Function Pointer + return Resolve(signatureStr); + } + */ + + private static IntPtr Resolve(string signature) + { + // Check Cache + if (_lookup.TryGetValue(signature, out var result)) + return result.Item2; + + // Run Original + // To-Do: Implement when Harmony Patch is replaced with Native Hook + /* + IntPtr originalResult = IntPtr.Zero; + if (originalResult != IntPtr.Zero) + { + // Cache Original Result + _lookup[signature] = (null, originalResult); + return originalResult; + } + */ + + // Check if Injection is Needed + if (!ShouldInject(signature, out MethodInfo unityShimMethod)) + return IntPtr.Zero; + + // Create Injected Function + var pair = _lookup[signature] = GenerateTrampoline(unityShimMethod); + return pair.Item2; + } + + private static bool ShouldInject(string signature, out MethodInfo unityShimMethod) + { + unityShimMethod = null; + + // Split the Signature + string[] split = signature.Split("::"); + string typeName = split[0]; + + // Find Managed Type + Type newType = Il2CppInteropFixes.FixedFindType(typeName); + if (newType == null) + return false; + + // Check if ICall was reworked + string methodName = split[1]; + if (newType.FindMethod($"{methodName}{_unityInjectedSuffix}") == null) + return false; + + // Find Managed Method + MethodInfo method = newType.FindMethod(methodName); + if (method == null) + return false; + + // ICall needs Injecting + unityShimMethod = method; + return true; + } + + private static (MethodInfo, IntPtr) GenerateTrampoline(MethodInfo unityShimMethod) + { + // Convert Method Parameters to Native Parameters + var methodParams = unityShimMethod.GetParameters(); + int offset = unityShimMethod.IsStatic ? 0 : 1; + Type[] paramTypes = new Type[methodParams.Length + offset]; + if (!unityShimMethod.IsStatic) + paramTypes[0] = typeof(IntPtr); + for (int i = offset; i < methodParams.Length + offset; i++) + { + if ((methodParams[i].ParameterType != typeof(string)) + && methodParams[i].ParameterType.IsValueType) + paramTypes[i] = methodParams[i].ParameterType; + else + paramTypes[i] = typeof(IntPtr); + } + + // Convert Return Type + Type returnType = unityShimMethod.ReturnType; + if ((returnType == typeof(string)) + || !returnType.IsValueType) + returnType = typeof(IntPtr); + + // Create New Injected ICall Method + string newMethodName = $"{unityShimMethod.Name}_INative"; + var trampoline = new DynamicMethodDefinition( + newMethodName, + returnType, + paramTypes); + var bodyBuilder = trampoline.GetILGenerator(); + + // Begin Try-Catch + var tryLabel = bodyBuilder.BeginExceptionBlock(); + + // Convert Method Parameters to Managed Objects + for (var i = 0; i < methodParams.Length; i++) + { + // Emit Arg Index + bodyBuilder.Emit(OpCodes.Ldarg, i); + + // Create Managed Object + var parameterType = methodParams[i].ParameterType; + if (parameterType == typeof(string)) + { + bodyBuilder.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.Il2CppStringToManaged))!); + } + else if (!parameterType.IsValueType) + { + var labelNull = bodyBuilder.DefineLabel(); + var labelDone = bodyBuilder.DefineLabel(); + bodyBuilder.Emit(OpCodes.Brfalse, labelNull); + bodyBuilder.Emit(OpCodes.Ldarg, i); + bodyBuilder.Emit(OpCodes.Newobj, parameterType.GetConstructor(new[] { typeof(IntPtr) })!); + bodyBuilder.Emit(OpCodes.Br, labelDone); + bodyBuilder.MarkLabel(labelNull); + bodyBuilder.Emit(OpCodes.Ldnull); + bodyBuilder.MarkLabel(labelDone); + } + } + + // Call Existing Method + bodyBuilder.Emit(OpCodes.Call, unityShimMethod); + + // Convert Managed Return + var oldreturnType = unityShimMethod.ReturnType; + if (oldreturnType == typeof(string)) + { + bodyBuilder.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.ManagedStringToIl2Cpp))!); + } + else if (!oldreturnType.IsValueType) + { + var labelNull = bodyBuilder.DefineLabel(); + var labelDone = bodyBuilder.DefineLabel(); + bodyBuilder.Emit(OpCodes.Dup); + bodyBuilder.Emit(OpCodes.Brfalse, labelNull); + bodyBuilder.Emit(OpCodes.Call, + typeof(Il2CppObjectBase).GetProperty(nameof(Il2CppObjectBase.Pointer))!.GetMethod); + bodyBuilder.Emit(OpCodes.Br, labelDone); + bodyBuilder.MarkLabel(labelNull); + bodyBuilder.Emit(OpCodes.Pop); + bodyBuilder.Emit(OpCodes.Ldc_I4_0); + bodyBuilder.Emit(OpCodes.Conv_I); + bodyBuilder.MarkLabel(labelDone); + } + + // Cache Return Value in Lcal + LocalBuilder returnLocal = null; + if (returnType != typeof(void)) + { + returnLocal = bodyBuilder.DeclareLocal(returnType); + bodyBuilder.Emit(OpCodes.Stloc, returnLocal); + } + + // Handle Try-Catch thrown Exceptions + var exceptionLocal = bodyBuilder.DeclareLocal(typeof(Exception)); + bodyBuilder.BeginCatchBlock(typeof(Exception)); + bodyBuilder.Emit(OpCodes.Stloc, exceptionLocal); + bodyBuilder.Emit(OpCodes.Ldstr, "Exception in IL2CPP Injected ICall: "); + bodyBuilder.Emit(OpCodes.Ldloc, exceptionLocal); + bodyBuilder.Emit(OpCodes.Callvirt, typeof(object).GetMethod(nameof(ToString))!); + bodyBuilder.Emit(OpCodes.Call, + typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) })!); + bodyBuilder.Emit(OpCodes.Call, typeof(MelonLogger).GetMethod(nameof(MelonLogger.Error), BindingFlags.Static | BindingFlags.Public, [typeof(string)])!); + + // End Try-Catch + bodyBuilder.EndExceptionBlock(); + + // Restore Return Value from Local + if (returnLocal != null) + bodyBuilder.Emit(OpCodes.Ldloc, returnLocal); + + // Return even if there is no Return Value + bodyBuilder.Emit(OpCodes.Ret); + + // Return the New Method + MethodInfo newMethod = trampoline.Generate(); + return (newMethod, newMethod.GetNativeStart()); + } + } +} + +#endif \ No newline at end of file From 6b3ceecac2fd7a33f86d8ce9a1be10e1718513ae Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Sat, 14 Sep 2024 23:15:22 -0600 Subject: [PATCH 12/20] Improved Injected ICall Wrapping Rewrote Trampoline Generation Fixed an issue with certain parameters not being translated properly when generating a trampoline Replaced il2cpp_resolve_icall Patch with NativeHook Get and Verify used Types and Methods before Hooking --- MelonLoader/Fixes/InjectedInternalCalls.cs | 318 +++++++++++++-------- MelonLoader/MelonUtils.cs | 5 +- 2 files changed, 198 insertions(+), 125 deletions(-) diff --git a/MelonLoader/Fixes/InjectedInternalCalls.cs b/MelonLoader/Fixes/InjectedInternalCalls.cs index f60fe2f3a..3e6f43283 100644 --- a/MelonLoader/Fixes/InjectedInternalCalls.cs +++ b/MelonLoader/Fixes/InjectedInternalCalls.cs @@ -1,44 +1,93 @@ #if NET6_0_OR_GREATER -using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; +using MelonLoader.NativeUtils; using MonoMod.RuntimeDetour; using MonoMod.Utils; using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; -//using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace MelonLoader.Fixes { internal static class InjectedInternalCalls { - private static Dictionary _lookup = new(); private const string _unityInjectedSuffix = "_Injected"; - private static MethodInfo _il2cpp_resolve_icall; - private static MethodInfo _il2cpp_resolve_icall_Postfix; - //[DllImport("GameAssembly", EntryPoint = "il2cpp_resolve_icall", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - //private static extern IntPtr il2cpp_resolve_icall_original([MarshalAs(UnmanagedType.LPStr)] string name); + private static Dictionary _lookup = new(); - internal static void Install() + private delegate IntPtr dil2cpp_resolve_icall(IntPtr signature); + private static dil2cpp_resolve_icall il2cpp_resolve_icall_original; + private static NativeHook il2cpp_resolve_icall_hook; + + private static Type _stringType; + private static Type _intPtrType; + private static Type _exceptionType; + private static Type _il2CppObjectBaseType; + + private static MethodInfo _stringConcat; + private static MethodInfo _objectToString; + private static MethodInfo _melonLoggerError; + private static MethodInfo _stringToIl2CppPtr; + private static MethodInfo _il2CppPtrToString; + private static MethodInfo _il2CppObjectBaseGetPointer; + + internal static unsafe void Install() { try { + Type objectType = typeof(object); Type il2cppType = typeof(IL2CPP); - Type thisType = typeof(InjectedInternalCalls); - - _il2cpp_resolve_icall = il2cppType.GetMethod("il2cpp_resolve_icall", BindingFlags.Public | BindingFlags.Static); - if (_il2cpp_resolve_icall == null) - throw new Exception("Failed to get IL2CPP.il2cpp_resolve_icall"); - - _il2cpp_resolve_icall_Postfix = thisType.GetMethod(nameof(il2cpp_resolve_icall_Postfix), BindingFlags.NonPublic | BindingFlags.Static); - - MelonDebug.Msg("Patching Il2CppInterop IL2CPP.il2cpp_resolve_icall..."); - Core.HarmonyInstance.Patch(_il2cpp_resolve_icall, - null, new HarmonyMethod(_il2cpp_resolve_icall_Postfix)); + Type melonLoggerType = typeof(MelonLogger); + + _il2CppObjectBaseType = typeof(Il2CppObjectBase); + _exceptionType = typeof(Exception); + _intPtrType = typeof(IntPtr); + _stringType = typeof(string); + + _stringConcat = _stringType.GetMethod(nameof(string.Concat), [_stringType, _stringType]); + if (_stringConcat == null) + throw new Exception("Failed to get string.Concat"); + + _objectToString = objectType.GetMethod(nameof(ToString)); + if (_objectToString == null) + throw new Exception("Failed to get object.ToString"); + + _stringToIl2CppPtr = il2cppType.GetMethod(nameof(IL2CPP.ManagedStringToIl2Cpp)); + if (_stringToIl2CppPtr == null) + throw new Exception("Failed to get IL2CPP.ManagedStringToIl2Cpp"); + + _melonLoggerError = melonLoggerType.GetMethod(nameof(MelonLogger.Error), + BindingFlags.Static | BindingFlags.Public, + [_stringType]); + if (_melonLoggerError == null) + throw new Exception("Failed to get MelonLogger.Error"); + + _il2CppPtrToString = il2cppType.GetMethod(nameof(IL2CPP.Il2CppStringToManaged)); + if (_il2CppPtrToString == null) + throw new Exception("Failed to get IL2CPP.Il2CppStringToManaged"); + + PropertyInfo pointerProp = _il2CppObjectBaseType.GetProperty(nameof(Il2CppObjectBase.Pointer)); + if (_il2CppPtrToString == null) + throw new Exception("Failed to get Il2CppObjectBase.Pointer"); + _il2CppObjectBaseGetPointer = pointerProp.GetMethod; + + string gameAssemblyName = "GameAssembly"; + NativeLibrary gameAssemblyLib = NativeLibrary.Load(gameAssemblyName); + if (gameAssemblyLib == null) + throw new Exception($"Failed to load {gameAssemblyName} Native Library"); + + IntPtr il2cpp_resolve_icall = gameAssemblyLib.GetExport(nameof(il2cpp_resolve_icall)); + if (il2cpp_resolve_icall == IntPtr.Zero) + throw new Exception($"Failed to get {nameof(il2cpp_resolve_icall)} Native Export"); + + MelonDebug.Msg("Patching il2cpp_resolve_icall..."); + IntPtr detourPtr = Marshal.GetFunctionPointerForDelegate((dil2cpp_resolve_icall)il2cpp_resolve_icall_Detour); + il2cpp_resolve_icall_hook = new NativeHook(il2cpp_resolve_icall, detourPtr); + il2cpp_resolve_icall_hook.Attach(); } catch (Exception e) { @@ -48,6 +97,13 @@ internal static void Install() internal static void Shutdown() { + if (il2cpp_resolve_icall_hook != null) + { + if (il2cpp_resolve_icall_hook.IsHooked) + il2cpp_resolve_icall_hook.Detach(); + il2cpp_resolve_icall_hook = null; + } + if (_lookup != null) { if (_lookup.Count > 0) @@ -56,60 +112,45 @@ internal static void Shutdown() } } - private static void il2cpp_resolve_icall_Postfix(string __0, ref IntPtr __result) - { - // Found the ICall - if (__result != IntPtr.Zero) - return; - - // Needs Resolving - __result = Resolve(__0); - } - - /* - private static IntPtr Resolve(IntPtr signature) + private static IntPtr il2cpp_resolve_icall_Detour(IntPtr signature) { // Convert Pointer to String - string signatureStr = IL2CPP.Il2CppStringToManaged(signature); + string signatureStr = Marshal.PtrToStringAnsi(signature); if (string.IsNullOrEmpty(signatureStr)) return IntPtr.Zero; - // Resolve to Function Pointer - return Resolve(signatureStr); - } - */ - - private static IntPtr Resolve(string signature) - { // Check Cache - if (_lookup.TryGetValue(signature, out var result)) - return result.Item2; + if (_lookup.TryGetValue(signatureStr, out var result)) + return result.Item3; // Run Original - // To-Do: Implement when Harmony Patch is replaced with Native Hook - /* - IntPtr originalResult = IntPtr.Zero; + IntPtr originalResult = il2cpp_resolve_icall_hook.Trampoline(signature); if (originalResult != IntPtr.Zero) { // Cache Original Result - _lookup[signature] = (null, originalResult); + _lookup[signatureStr] = (null, null, originalResult); return originalResult; } - */ // Check if Injection is Needed - if (!ShouldInject(signature, out MethodInfo unityShimMethod)) + if (!ShouldInject(signatureStr, out MethodInfo unityShimMethod)) return IntPtr.Zero; // Create Injected Function - var pair = _lookup[signature] = GenerateTrampoline(unityShimMethod); - return pair.Item2; + var pair = _lookup[signatureStr] = GenerateTrampoline(unityShimMethod); + return pair.Item3; } private static bool ShouldInject(string signature, out MethodInfo unityShimMethod) { unityShimMethod = null; + // Check if the Unity Injected Shim ICall Exists + IntPtr unityInjectedShimResult = il2cpp_resolve_icall_hook.Trampoline( + Marshal.StringToHGlobalAnsi($"{signature}{_unityInjectedSuffix}")); + if (unityInjectedShimResult == IntPtr.Zero) + return false; + // Split the Signature string[] split = signature.Split("::"); string typeName = split[0]; @@ -119,43 +160,39 @@ private static bool ShouldInject(string signature, out MethodInfo unityShimMetho if (newType == null) return false; - // Check if ICall was reworked - string methodName = split[1]; - if (newType.FindMethod($"{methodName}{_unityInjectedSuffix}") == null) - return false; - // Find Managed Method + string methodName = split[1]; MethodInfo method = newType.FindMethod(methodName); if (method == null) return false; - // ICall needs Injecting + // Inject ICall unityShimMethod = method; return true; } - private static (MethodInfo, IntPtr) GenerateTrampoline(MethodInfo unityShimMethod) + private static (DynamicMethodDefinition, MethodInfo, IntPtr) GenerateTrampoline(MethodInfo unityShimMethod) { // Convert Method Parameters to Native Parameters var methodParams = unityShimMethod.GetParameters(); int offset = unityShimMethod.IsStatic ? 0 : 1; Type[] paramTypes = new Type[methodParams.Length + offset]; if (!unityShimMethod.IsStatic) - paramTypes[0] = typeof(IntPtr); - for (int i = offset; i < methodParams.Length + offset; i++) + paramTypes[0] = _intPtrType; + for (int i = 0; i < methodParams.Length; i++) { - if ((methodParams[i].ParameterType != typeof(string)) + if ((methodParams[i].ParameterType != _stringType) && methodParams[i].ParameterType.IsValueType) - paramTypes[i] = methodParams[i].ParameterType; + paramTypes[i + offset] = methodParams[i].ParameterType; else - paramTypes[i] = typeof(IntPtr); + paramTypes[i + offset] = _intPtrType; } // Convert Return Type Type returnType = unityShimMethod.ReturnType; - if ((returnType == typeof(string)) + if ((returnType == _stringType) || !returnType.IsValueType) - returnType = typeof(IntPtr); + returnType = _intPtrType; // Create New Injected ICall Method string newMethodName = $"{unityShimMethod.Name}_INative"; @@ -163,94 +200,127 @@ private static (MethodInfo, IntPtr) GenerateTrampoline(MethodInfo unityShimMetho newMethodName, returnType, paramTypes); - var bodyBuilder = trampoline.GetILGenerator(); + var ilGenerator = trampoline.GetILGenerator(); // Begin Try-Catch - var tryLabel = bodyBuilder.BeginExceptionBlock(); + ilGenerator.BeginExceptionBlock(); + + // Emit This Object + if (!unityShimMethod.IsStatic) + ilGenerator.EmitPtrArgToManagedObject(0, unityShimMethod.DeclaringType); // Convert Method Parameters to Managed Objects for (var i = 0; i < methodParams.Length; i++) { - // Emit Arg Index - bodyBuilder.Emit(OpCodes.Ldarg, i); - - // Create Managed Object - var parameterType = methodParams[i].ParameterType; - if (parameterType == typeof(string)) - { - bodyBuilder.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.Il2CppStringToManaged))!); - } - else if (!parameterType.IsValueType) - { - var labelNull = bodyBuilder.DefineLabel(); - var labelDone = bodyBuilder.DefineLabel(); - bodyBuilder.Emit(OpCodes.Brfalse, labelNull); - bodyBuilder.Emit(OpCodes.Ldarg, i); - bodyBuilder.Emit(OpCodes.Newobj, parameterType.GetConstructor(new[] { typeof(IntPtr) })!); - bodyBuilder.Emit(OpCodes.Br, labelDone); - bodyBuilder.MarkLabel(labelNull); - bodyBuilder.Emit(OpCodes.Ldnull); - bodyBuilder.MarkLabel(labelDone); - } + var param = methodParams[i]; + var paramType = param.ParameterType; + if (paramType == _stringType) + ilGenerator.EmitPtrArgToString(i + offset); + else if (paramType.IsValueType) + ilGenerator.EmitArg(i + offset); + else + ilGenerator.EmitPtrArgToManagedObject(i + offset, paramType); } // Call Existing Method - bodyBuilder.Emit(OpCodes.Call, unityShimMethod); + ilGenerator.Emit(OpCodes.Call, unityShimMethod); // Convert Managed Return var oldreturnType = unityShimMethod.ReturnType; - if (oldreturnType == typeof(string)) - { - bodyBuilder.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.ManagedStringToIl2Cpp))!); - } - else if (!oldreturnType.IsValueType) - { - var labelNull = bodyBuilder.DefineLabel(); - var labelDone = bodyBuilder.DefineLabel(); - bodyBuilder.Emit(OpCodes.Dup); - bodyBuilder.Emit(OpCodes.Brfalse, labelNull); - bodyBuilder.Emit(OpCodes.Call, - typeof(Il2CppObjectBase).GetProperty(nameof(Il2CppObjectBase.Pointer))!.GetMethod); - bodyBuilder.Emit(OpCodes.Br, labelDone); - bodyBuilder.MarkLabel(labelNull); - bodyBuilder.Emit(OpCodes.Pop); - bodyBuilder.Emit(OpCodes.Ldc_I4_0); - bodyBuilder.Emit(OpCodes.Conv_I); - bodyBuilder.MarkLabel(labelDone); - } + if (oldreturnType == _stringType) + ilGenerator.EmitStringToPtr(); + else if ((oldreturnType == _il2CppObjectBaseType) + || oldreturnType.IsSubclassOf(_il2CppObjectBaseType)) + ilGenerator.EmitIl2CppObjectBaseToPtr(); // Cache Return Value in Lcal LocalBuilder returnLocal = null; if (returnType != typeof(void)) { - returnLocal = bodyBuilder.DeclareLocal(returnType); - bodyBuilder.Emit(OpCodes.Stloc, returnLocal); + returnLocal = ilGenerator.DeclareLocal(returnType); + ilGenerator.Emit(OpCodes.Stloc, returnLocal); } - // Handle Try-Catch thrown Exceptions - var exceptionLocal = bodyBuilder.DeclareLocal(typeof(Exception)); - bodyBuilder.BeginCatchBlock(typeof(Exception)); - bodyBuilder.Emit(OpCodes.Stloc, exceptionLocal); - bodyBuilder.Emit(OpCodes.Ldstr, "Exception in IL2CPP Injected ICall: "); - bodyBuilder.Emit(OpCodes.Ldloc, exceptionLocal); - bodyBuilder.Emit(OpCodes.Callvirt, typeof(object).GetMethod(nameof(ToString))!); - bodyBuilder.Emit(OpCodes.Call, - typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) })!); - bodyBuilder.Emit(OpCodes.Call, typeof(MelonLogger).GetMethod(nameof(MelonLogger.Error), BindingFlags.Static | BindingFlags.Public, [typeof(string)])!); - // End Try-Catch - bodyBuilder.EndExceptionBlock(); + ilGenerator.EmitExceptionCatch(); // Restore Return Value from Local if (returnLocal != null) - bodyBuilder.Emit(OpCodes.Ldloc, returnLocal); + ilGenerator.Emit(OpCodes.Ldloc, returnLocal); // Return even if there is no Return Value - bodyBuilder.Emit(OpCodes.Ret); + ilGenerator.Emit(OpCodes.Ret); // Return the New Method - MethodInfo newMethod = trampoline.Generate(); - return (newMethod, newMethod.GetNativeStart()); + MethodInfo newMethod = trampoline.Generate().Pin(); + return (trampoline, newMethod, newMethod.GetNativeStart()); + } + + private static void EmitArg(this ILGenerator ilGenerator, + int index) + => ilGenerator.Emit(OpCodes.Ldarg, index); + + private static void EmitPtrArgToString(this ILGenerator ilGenerator, + int argIndex) + { + ilGenerator.EmitArg(argIndex); + ilGenerator.Emit(OpCodes.Call, _il2CppPtrToString); + } + + private static void EmitStringToPtr(this ILGenerator ilGenerator) + => ilGenerator.Emit(OpCodes.Call, _stringToIl2CppPtr); + + private static void EmitPtrArgToManagedObject(this ILGenerator ilGenerator, + int argIndex, + Type managedType) + { + ilGenerator.EmitArg(argIndex); + + var labelNull = ilGenerator.DefineLabel(); + var labelDone = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Brfalse, labelNull); + ilGenerator.EmitArg(argIndex); + + ilGenerator.Emit(OpCodes.Newobj, + managedType.GetConstructor([ _intPtrType ])); + + ilGenerator.Emit(OpCodes.Br, labelDone); + ilGenerator.MarkLabel(labelNull); + ilGenerator.Emit(OpCodes.Ldnull); + ilGenerator.MarkLabel(labelDone); + } + + private static void EmitIl2CppObjectBaseToPtr(this ILGenerator ilGenerator) + { + var labelNull = ilGenerator.DefineLabel(); + var labelDone = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Dup); + ilGenerator.Emit(OpCodes.Brfalse, labelNull); + + ilGenerator.Emit(OpCodes.Call, _il2CppObjectBaseGetPointer); + + ilGenerator.Emit(OpCodes.Br, labelDone); + ilGenerator.MarkLabel(labelNull); + ilGenerator.Emit(OpCodes.Pop); + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.Emit(OpCodes.Conv_I); + ilGenerator.MarkLabel(labelDone); + } + + private static void EmitExceptionCatch(this ILGenerator ilGenerator) + { + var exceptionLocal = ilGenerator.DeclareLocal(_exceptionType); + ilGenerator.BeginCatchBlock(_exceptionType); + + ilGenerator.Emit(OpCodes.Stloc, exceptionLocal); + ilGenerator.Emit(OpCodes.Ldstr, "Exception in IL2CPP Injected ICall: "); + ilGenerator.Emit(OpCodes.Ldloc, exceptionLocal); + + ilGenerator.Emit(OpCodes.Callvirt, _objectToString); + ilGenerator.Emit(OpCodes.Call, _stringConcat); + ilGenerator.Emit(OpCodes.Call, _melonLoggerError); + + ilGenerator.EndExceptionBlock(); } } } diff --git a/MelonLoader/MelonUtils.cs b/MelonLoader/MelonUtils.cs index d2b03e8e5..ff2203a38 100644 --- a/MelonLoader/MelonUtils.cs +++ b/MelonLoader/MelonUtils.cs @@ -487,10 +487,13 @@ public static string GetFileProductName(string filepath) internal static void SetupWineCheck() { - if (MelonUtils.IsUnix || MelonUtils.IsMac) + if (IsUnix || IsMac) return; IntPtr dll = NativeLibrary.LoadLib("ntdll.dll"); + if (dll == IntPtr.Zero) + return; + IntPtr wine_get_version_proc = NativeLibrary.AgnosticGetProcAddress(dll, "wine_get_version"); if (wine_get_version_proc == IntPtr.Zero) return; From 184cd1cc090c2f7c0ca39236c09a57a081310e69 Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Sat, 14 Sep 2024 23:16:49 -0600 Subject: [PATCH 13/20] Update InjectedInternalCalls.cs --- MelonLoader/Fixes/InjectedInternalCalls.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MelonLoader/Fixes/InjectedInternalCalls.cs b/MelonLoader/Fixes/InjectedInternalCalls.cs index 3e6f43283..6971c443e 100644 --- a/MelonLoader/Fixes/InjectedInternalCalls.cs +++ b/MelonLoader/Fixes/InjectedInternalCalls.cs @@ -16,11 +16,11 @@ namespace MelonLoader.Fixes internal static class InjectedInternalCalls { private const string _unityInjectedSuffix = "_Injected"; + private const string _customICallSuffix = "_INative"; private static Dictionary _lookup = new(); private delegate IntPtr dil2cpp_resolve_icall(IntPtr signature); - private static dil2cpp_resolve_icall il2cpp_resolve_icall_original; private static NativeHook il2cpp_resolve_icall_hook; private static Type _stringType; @@ -195,7 +195,7 @@ private static (DynamicMethodDefinition, MethodInfo, IntPtr) GenerateTrampoline( returnType = _intPtrType; // Create New Injected ICall Method - string newMethodName = $"{unityShimMethod.Name}_INative"; + string newMethodName = $"{unityShimMethod.Name}{_customICallSuffix}"; var trampoline = new DynamicMethodDefinition( newMethodName, returnType, From ebf109a2c3ec4444576ab6504ae803f972e675a7 Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Sun, 15 Sep 2024 00:44:58 -0600 Subject: [PATCH 14/20] General Cleanup, Added Logging to Il2CppICallInjector --- MelonLoader/Core.cs | 4 +- ...nternalCalls.cs => Il2CppICallInjector.cs} | 41 +++++++++++++------ 2 files changed, 30 insertions(+), 15 deletions(-) rename MelonLoader/Fixes/{InjectedInternalCalls.cs => Il2CppICallInjector.cs} (90%) diff --git a/MelonLoader/Core.cs b/MelonLoader/Core.cs index b69f6e4d8..d688efc31 100644 --- a/MelonLoader/Core.cs +++ b/MelonLoader/Core.cs @@ -94,7 +94,7 @@ internal static int Initialize() #if NET6_0_OR_GREATER Fixes.Il2CppInteropFixes.Install(); - Fixes.InjectedInternalCalls.Install(); + Fixes.Il2CppICallInjector.Install(); #endif PatchShield.Install(); @@ -191,7 +191,7 @@ internal static void Quit() #if NET6_0_OR_GREATER Fixes.Il2CppInteropFixes.Shutdown(); - Fixes.InjectedInternalCalls.Shutdown(); + Fixes.Il2CppICallInjector.Shutdown(); #endif MelonLogger.Flush(); diff --git a/MelonLoader/Fixes/InjectedInternalCalls.cs b/MelonLoader/Fixes/Il2CppICallInjector.cs similarity index 90% rename from MelonLoader/Fixes/InjectedInternalCalls.cs rename to MelonLoader/Fixes/Il2CppICallInjector.cs index 6971c443e..2c90e9145 100644 --- a/MelonLoader/Fixes/InjectedInternalCalls.cs +++ b/MelonLoader/Fixes/Il2CppICallInjector.cs @@ -13,9 +13,8 @@ namespace MelonLoader.Fixes { - internal static class InjectedInternalCalls + internal static class Il2CppICallInjector { - private const string _unityInjectedSuffix = "_Injected"; private const string _customICallSuffix = "_INative"; private static Dictionary _lookup = new(); @@ -23,6 +22,9 @@ internal static class InjectedInternalCalls private delegate IntPtr dil2cpp_resolve_icall(IntPtr signature); private static NativeHook il2cpp_resolve_icall_hook; + private delegate void dil2cpp_add_internal_call(IntPtr signature, IntPtr funcPtr); + private static dil2cpp_add_internal_call il2cpp_add_internal_call; + private static Type _stringType; private static Type _intPtrType; private static Type _exceptionType; @@ -35,10 +37,15 @@ internal static class InjectedInternalCalls private static MethodInfo _il2CppPtrToString; private static MethodInfo _il2CppObjectBaseGetPointer; + private static MelonLogger.Instance _logger; + internal static unsafe void Install() { try { + _logger = new MelonLogger.Instance(nameof(Il2CppICallInjector)); + + Type thisType = typeof(Il2CppICallInjector); Type objectType = typeof(object); Type il2cppType = typeof(IL2CPP); Type melonLoggerType = typeof(MelonLogger); @@ -60,8 +67,8 @@ internal static unsafe void Install() if (_stringToIl2CppPtr == null) throw new Exception("Failed to get IL2CPP.ManagedStringToIl2Cpp"); - _melonLoggerError = melonLoggerType.GetMethod(nameof(MelonLogger.Error), - BindingFlags.Static | BindingFlags.Public, + _melonLoggerError = thisType.GetMethod(nameof(LogError), + BindingFlags.Static | BindingFlags.NonPublic, [_stringType]); if (_melonLoggerError == null) throw new Exception("Failed to get MelonLogger.Error"); @@ -84,6 +91,10 @@ internal static unsafe void Install() if (il2cpp_resolve_icall == IntPtr.Zero) throw new Exception($"Failed to get {nameof(il2cpp_resolve_icall)} Native Export"); + il2cpp_add_internal_call = gameAssemblyLib.GetExport(nameof(il2cpp_add_internal_call)); + if (il2cpp_add_internal_call == null) + throw new Exception($"Failed to get {nameof(il2cpp_add_internal_call)} Native Export"); + MelonDebug.Msg("Patching il2cpp_resolve_icall..."); IntPtr detourPtr = Marshal.GetFunctionPointerForDelegate((dil2cpp_resolve_icall)il2cpp_resolve_icall_Detour); il2cpp_resolve_icall_hook = new NativeHook(il2cpp_resolve_icall, detourPtr); @@ -91,7 +102,7 @@ internal static unsafe void Install() } catch (Exception e) { - MelonLogger.Error(e); + LogError(e.ToString()); } } @@ -112,6 +123,9 @@ internal static void Shutdown() } } + private static void LogError(string msg) + => _logger.Error(msg); + private static IntPtr il2cpp_resolve_icall_Detour(IntPtr signature) { // Convert Pointer to String @@ -136,8 +150,15 @@ private static IntPtr il2cpp_resolve_icall_Detour(IntPtr signature) if (!ShouldInject(signatureStr, out MethodInfo unityShimMethod)) return IntPtr.Zero; - // Create Injected Function - var pair = _lookup[signatureStr] = GenerateTrampoline(unityShimMethod); + // Create Injected Function and Cache Return + var pair = + _lookup[signatureStr] = GenerateTrampoline(unityShimMethod); + + // Add New ICall to Il2Cpp Domain + il2cpp_add_internal_call(signature, pair.Item3); + _logger.Msg($"Registered mono icall {signatureStr} in il2cpp domain"); + + // Return New Function Pointer return pair.Item3; } @@ -145,12 +166,6 @@ private static bool ShouldInject(string signature, out MethodInfo unityShimMetho { unityShimMethod = null; - // Check if the Unity Injected Shim ICall Exists - IntPtr unityInjectedShimResult = il2cpp_resolve_icall_hook.Trampoline( - Marshal.StringToHGlobalAnsi($"{signature}{_unityInjectedSuffix}")); - if (unityInjectedShimResult == IntPtr.Zero) - return false; - // Split the Signature string[] split = signature.Split("::"); string typeName = split[0]; From 7ccd998bba7c89c52fb4894283bf60624c6eb44e Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Sun, 15 Sep 2024 00:45:20 -0600 Subject: [PATCH 15/20] Update Il2CppICallInjector.cs --- MelonLoader/Fixes/Il2CppICallInjector.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MelonLoader/Fixes/Il2CppICallInjector.cs b/MelonLoader/Fixes/Il2CppICallInjector.cs index 2c90e9145..1d5a83e3c 100644 --- a/MelonLoader/Fixes/Il2CppICallInjector.cs +++ b/MelonLoader/Fixes/Il2CppICallInjector.cs @@ -48,7 +48,6 @@ internal static unsafe void Install() Type thisType = typeof(Il2CppICallInjector); Type objectType = typeof(object); Type il2cppType = typeof(IL2CPP); - Type melonLoggerType = typeof(MelonLogger); _il2CppObjectBaseType = typeof(Il2CppObjectBase); _exceptionType = typeof(Exception); From 8e64711fb0245037ccef86256f57423b7e608e3b Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Sat, 21 Sep 2024 20:52:41 -0600 Subject: [PATCH 16/20] Updated Il2CppInterop to 1.4.6-ci.516 --- .../Il2CppAssemblyGenerator.csproj | 6 +++--- Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj | 6 +++--- MelonLoader/MelonLoader.csproj | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj index 94aa39b41..963968d97 100644 --- a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj +++ b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj @@ -15,9 +15,9 @@ - - - + + + diff --git a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj index 90183bb14..8f8a89e8c 100644 --- a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj +++ b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj @@ -41,9 +41,9 @@ - - - + + + diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index 890df5548..466dfc7a7 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -49,10 +49,10 @@ - - - - + + + + From 0e442cd32030c7a6fe8e0ccce3eaf57be57d8e49 Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Sat, 21 Sep 2024 21:13:52 -0600 Subject: [PATCH 17/20] Update README and CHANGELOG --- CHANGELOG.md | 31 +++++++++++++++++-------------- README.md | 2 +- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbffdd8dc..1e37da04b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,20 +39,20 @@ ### v0.6.5 -1. Replaced [BepInEx/Il2CppInterop](https://github.com/BepInEx/Il2CppInterop) with [ds5678/Il2CppInterop](https://github.com/ds5678/Il2CppInterop) -2. Updated Il2CppInterop to 1.5.4 -3. Updated Cpp2IL to 2022.1.0-pre-release.18 -4. Updated AsmResolver to 6.0.0-beta.1 -5. Updated AssetRipper.VersionUtilities to 1.5.0 -6. Updated AssetsTools.NET to 3.0.0 -7. Updated UnityEngine.Il2CppAssetBundleManager for latest compatibility -8. Updated UnityEngine.Il2CppImageConversionManager for latest compatibility -9. Implemented `--cpp2il.callanalyzer` launch option to enable Cpp2IL's CallAnalyzer processor -10. Implemented `--cpp2il.nativemethoddetector` launch option to enable Cpp2IL's NativeMethodDetector processor -11. Implemented several fixes for Il2CppInterop related issues -12. Implemented Cpp2IL's StrippedCodeRegSupport plugin -13. Implemented `ExternalArguments` Dictionary for MelonLaunchOptions (Credits to [HAHOOS](https://github.com/HAHOOS) :P) -14. Implemented `Peek` Method for LemonEnumerator +1. Updated Il2CppInterop to 1.4.6-ci.516 +2. Updated Cpp2IL to 2022.1.0-pre-release.18 +3. Updated AsmResolver to 6.0.0-beta.1 +4. Updated AssetRipper.VersionUtilities to 1.5.0 +5. Updated AssetsTools.NET to 3.0.0 +6. Updated UnityEngine.Il2CppAssetBundleManager for latest compatibility +7. Updated UnityEngine.Il2CppImageConversionManager for latest compatibility +8. Implemented `--cpp2il.callanalyzer` launch option to enable Cpp2IL's CallAnalyzer processor +9. Implemented `--cpp2il.nativemethoddetector` launch option to enable Cpp2IL's NativeMethodDetector processor +10. Implemented several fixes for Il2CppInterop related issues +11. Implemented `ExternalArguments` Dictionary for MelonLaunchOptions (Credits to [HAHOOS](https://github.com/HAHOOS) :P) +12. Implemented `MsgPastel` Method for MelonLogger (Credits to [HAHOOS](https://github.com/HAHOOS) :P) +13. Implemented `Peek` Method for LemonEnumerator +14. Implemented ICallInjector for handling when Unity strips or renames Internal Calls 15. Fixed an accidental regression with LemonSHA256 16. Fixed an issue with Native logs using the wrong Colors 17. Fixed an issue with Preload module not replacing Mono libraries on Older Mono Games @@ -70,6 +70,9 @@ 29. Fixed an issue with MelonLaunchOptions failing to parse options if a `-` prefix is used instead of `--` 30. Fixed an issue with Command Line Arguments not being logged 31. Fixed an issue with Il2CppInterop Assembly Resolving when Il2Cpp Prefixing is used +32. Fixed an issue with .NET Executables not being Resolved properly when used as Dependencies +33. Fixed an issue with Dependency Graph failing to Resolve Assemblies properly +34. Fixed an issue with Il2Cpp Support Module not attempting to use direct references --- diff --git a/README.md b/README.md index 7cb122f5e..f8b4b8f79 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,7 @@ Third-party Libraries used as Source Code and/or bundled in Binary Form: - [ValueTupleBridge](https://github.com/OrangeCube/MinimumAsyncBridge) is licensed under the MIT License. See [LICENSE](https://github.com/OrangeCube/MinimumAsyncBridge/blob/master/LICENSE) for the full License. - [WebSocketDotNet](https://github.com/SamboyCoding/WebSocketDotNet) is licensed under the MIT License. See [LICENSE](https://github.com/SamboyCoding/WebSocketDotNet/blob/master/LICENSE) for the full License. - [Pastel](https://github.com/silkfire/Pastel) is licensed under the MIT License. See [LICENSE](https://github.com/silkfire/Pastel/blob/master/LICENSE) for the full License. -- [Il2CppInterop](https://github.com/ds5678/Il2CppInterop) is licensed under LGPLv3 License. See [LICENSE](https://github.com/ds5678/Il2CppInterop/blob/master/LICENSE) for the full License. +- [Il2CppInterop](https://github.com/BepInEx/Il2CppInterop) is licensed under LGPLv3 License. See [LICENSE](https://github.com/BepInEx/Il2CppInterop/blob/master/LICENSE) for the full License. External Libraries and Tools that are downloaded and used at Runtime: From 8867139ee7c8a03721da8ad7fe4597592e8a9dfd Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Sat, 21 Sep 2024 21:15:36 -0600 Subject: [PATCH 18/20] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e37da04b..f201054ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,8 +66,8 @@ 25. Fixed an issue with EOS Support Module not being properly error handled 26. Fixed an issue with NativeStackWalk not unregistering addresses 27. Fixed an issue with Errors being Spammed if `UnityEngine.Transform::SetAsLastSibling` fails to resolve -28. Fixed an issue with MelonLaunchOptions failing to parse arguments if an `=` sign is used instead of a space -29. Fixed an issue with MelonLaunchOptions failing to parse options if a `-` prefix is used instead of `--` +28. Fixed an issue with MelonLaunchOptions failing to parse options if a `-` prefix is used instead of `--` +29. Fixed an issue with MelonLaunchOptions failing to parse option arguments if an `=` sign is used instead of a space 30. Fixed an issue with Command Line Arguments not being logged 31. Fixed an issue with Il2CppInterop Assembly Resolving when Il2Cpp Prefixing is used 32. Fixed an issue with .NET Executables not being Resolved properly when used as Dependencies From 9dd558d70df9f53efb4ef74825ea61dc74b2168c Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Sat, 21 Sep 2024 21:16:30 -0600 Subject: [PATCH 19/20] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f201054ed..a4c377a40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,7 @@ ### v0.6.5 -1. Updated Il2CppInterop to 1.4.6-ci.516 +1. Updated Il2CppInterop to 1.4.6-ci.545 2. Updated Cpp2IL to 2022.1.0-pre-release.18 3. Updated AsmResolver to 6.0.0-beta.1 4. Updated AssetRipper.VersionUtilities to 1.5.0 From 957b4242859b73aeadbd3328e2554cd0bb24ec4e Mon Sep 17 00:00:00 2001 From: Herp Derpinstine Date: Sat, 21 Sep 2024 21:21:16 -0600 Subject: [PATCH 20/20] Update MelonLoader.csproj --- MelonLoader/MelonLoader.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index 466dfc7a7..8e81c7a15 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -45,7 +45,7 @@ - +