diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0d01a2094..d2da10f66 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -180,7 +180,7 @@ jobs: xcopy BaseLibs\net6 Output\Debug\x64\MelonLoader\net6\ /E /H /Y echo. echo Copying Dobby x64... - xcopy BaseLibs\dobby_x64.dll Output\Debug\x64\dobby.dll* + xcopy BaseLibs\dobby_x64.dll Output\Debug\x64\MelonLoader\Dependencies\dobby.dll* echo. echo Copying documentation files... copy NOTICE.txt Output\Debug\x64 @@ -189,6 +189,8 @@ jobs: copy LICENSE.md Output\Debug\x64\MelonLoader\Documentation\ copy NOTICE.txt Output\Debug\x64\MelonLoader\Documentation\ copy README.md Output\Debug\x64\MelonLoader\Documentation\ + del Output\Debug\x64\MelonLoader\net6\MelonStartScreen.dll + del Output\Debug\x64\MelonLoader\net6\MelonStartScreen.deps.json - uses: actions/upload-artifact@v4 name: Upload Zip | Windows - x64 with: @@ -224,7 +226,7 @@ jobs: xcopy BaseLibs\net6 Output\Debug\x86\MelonLoader\net6\ /E /H /Y echo. echo Copying Dobby x86... - xcopy BaseLibs\dobby_x86.dll Output\Debug\x86\dobby.dll* + xcopy BaseLibs\dobby_x86.dll Output\Debug\x86\MelonLoader\Dependencies\dobby.dll* echo. echo Copying documentation files... copy NOTICE.txt Output\Debug\x86 @@ -233,6 +235,8 @@ jobs: copy LICENSE.md Output\Debug\x86\MelonLoader\Documentation\ copy NOTICE.txt Output\Debug\x86\MelonLoader\Documentation\ copy README.md Output\Debug\x86\MelonLoader\Documentation\ + del Output\Debug\x86\MelonLoader\net6\MelonStartScreen.dll + del Output\Debug\x86\MelonLoader\net6\MelonStartScreen.deps.json - uses: actions/upload-artifact@v4 name: Upload Zip | Windows - x86 with: @@ -268,7 +272,7 @@ jobs: xcopy BaseLibs\net6 Output\Release\x64\MelonLoader\net6\ /E /H /Y echo. echo Copying Dobby x64... - xcopy BaseLibs\dobby_x64.dll Output\Release\x64\dobby.dll* + xcopy BaseLibs\dobby_x64.dll Output\Release\x64\MelonLoader\Dependencies\dobby.dll* echo. echo Copying documentation files... copy NOTICE.txt Output\Release\x64 @@ -277,6 +281,8 @@ jobs: copy LICENSE.md Output\Release\x64\MelonLoader\Documentation\ copy NOTICE.txt Output\Release\x64\MelonLoader\Documentation\ copy README.md Output\Release\x64\MelonLoader\Documentation\ + del Output\Release\x64\MelonLoader\net6\MelonStartScreen.dll + del Output\Release\x64\MelonLoader\net6\MelonStartScreen.deps.json - uses: actions/upload-artifact@v4 name: Upload Zip | Windows - x64 with: @@ -312,7 +318,7 @@ jobs: xcopy BaseLibs\net6 Output\Release\x86\MelonLoader\net6\ /E /H /Y echo. echo Copying Dobby x86... - xcopy BaseLibs\dobby_x86.dll Output\Release\x86\dobby.dll* + xcopy BaseLibs\dobby_x86.dll Output\Release\x86\MelonLoader\Dependencies\dobby.dll* echo. echo Copying documentation files... copy NOTICE.txt Output\Release\x86 @@ -321,6 +327,8 @@ jobs: copy LICENSE.md Output\Release\x86\MelonLoader\Documentation\ copy NOTICE.txt Output\Release\x86\MelonLoader\Documentation\ copy README.md Output\Release\x86\MelonLoader\Documentation\ + del Output\Release\x86\MelonLoader\net6\MelonStartScreen.dll + del Output\Release\x86\MelonLoader\net6\MelonStartScreen.deps.json - uses: actions/upload-artifact@v4 name: Upload Zip | Windows - x86 with: @@ -362,6 +370,8 @@ jobs: copy LICENSE.md Output\Debug\x64\MelonLoader\Documentation\ copy NOTICE.txt Output\Debug\x64\MelonLoader\Documentation\ copy README.md Output\Debug\x64\MelonLoader\Documentation\ + del Output\Debug\x64\MelonLoader\net6\MelonStartScreen.dll + del Output\Debug\x64\MelonLoader\net6\MelonStartScreen.deps.json - uses: actions/upload-artifact@v4 name: Upload Zip | Linux - x64 with: @@ -403,6 +413,8 @@ jobs: copy LICENSE.md Output\Release\x64\MelonLoader\Documentation\ copy NOTICE.txt Output\Release\x64\MelonLoader\Documentation\ copy README.md Output\Release\x64\MelonLoader\Documentation\ + del Output\Release\x64\MelonLoader\net6\MelonStartScreen.dll + del Output\Release\x64\MelonLoader\net6\MelonStartScreen.deps.json - uses: actions/upload-artifact@v4 name: Upload Zip | Linux - x64 with: diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 8fad867a9..78042b0af 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -23,4 +23,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: MelonLoaderNuGetPackage - path: MelonLoader/Output/Release/MelonLoader/LavaGang.MelonLoader.0.6.5.nupkg \ No newline at end of file + path: MelonLoader/Output/Release/MelonLoader/LavaGang.MelonLoader.0.6.6.nupkg \ No newline at end of file diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..e52ebba49 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,41 @@ +# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. +# +# You can adjust the behavior by modifying this file. +# For more information, see: +# https://github.com/actions/stale +name: Close inactive issues + +on: + schedule: + - cron: '4 20 * * *' + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +concurrency: + group: stale + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@main + with: + days-before-issue-stale: 120 + days-before-issue-close: 14 + stale-issue-message: "This issue is stale because it has been open for 4 months with no activity. If this is still an issue then please leave a comment, or else this will close in 2 weeks." + close-issue-message: "This issue was closed because it has been 2 weeks since being marked as stale." + days-before-pr-stale: -1 + days-before-pr-close: -1 + exempt-issue-labels: "Work-in-Progress,Planned,Improvement" + operations-per-run: 100 + lock: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v5.0.1 + with: + issue-inactive-days: '365' + issue-lock-reason: 'resolved' + process-only: 'issues' diff --git a/CHANGELOG.md b/CHANGELOG.md index a4c377a40..da8e3655b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ | Versions: | | - | +| [v0.6.6](#v066) | | [v0.6.5](#v065) | | [v0.6.4](#v064) | | [v0.6.3](#v063) | @@ -37,6 +38,31 @@ --- +### v0.6.6 + +1. Updated Il2CppInterop to 1.4.6-ci.579 +2. Implemented a RegisterTypeInIl2CppWithInterfaces attribute +3. Implemented Recursive Melon Folders with extended UserLib Resolving +4. Reimplemented NetFramework Variant of Cpp2IL as fallback +5. Standardized Assembly Searching and Resolving to work on both Mono and Il2Cpp Games +6. Temporarily removed Start Screen for being broken in most cases +7. Modified Command-Line Argument Logging to show Internal Arguments Only +8. Added UserLibs folders to Native Library Search Directories +9. Moved `dobby.dll` to `MelonLoader\Dependencies` +10. Moved Assembly Resolver Related Classes to `MelonLoader.Resolver` Namespace +11. Moved `MonoLibrary` class to `MelonLoader.Utils` Namespace +12. Removed Useless TODO Warning from Il2CppAssemblyGenerator +13. Removed EOS Compatibility Layer for being Unneeded +14. Fixed Regression with LemonMD5, LemonSHA256, and LemonSHA512 +15. Fixed an issue with older Cpp2IL versions causing a download failure +16. Fixed an issue with Il2CppInterop not properly logging Trampoline Exceptions +17. Fixed an issue with Il2Cpp Class Injection Attributes causing exceptions to be thrown on Mono games +18. Fixed an issue with the Bootstrap not reading `--melonloader.basedir` correctly +19. Fixed an issue with Loading `dobby.dll` in some rare cases +20. Fixed an issue with Compatibility Layers getting Garbage Collected while still in use + +--- + ### v0.6.5 1. Updated Il2CppInterop to 1.4.6-ci.545 diff --git a/Dependencies/CompatibilityLayers/EOS/EOS.csproj b/Dependencies/CompatibilityLayers/EOS/EOS.csproj deleted file mode 100644 index cc4d68bae..000000000 --- a/Dependencies/CompatibilityLayers/EOS/EOS.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - MelonLoader.CompatibilityLayers - net35 - Latest - true - false - false - $(SolutionDir)Output\$(Configuration)\MelonLoader\Dependencies\CompatibilityLayers\ - true - embedded - - - - - \ No newline at end of file diff --git a/Dependencies/CompatibilityLayers/EOS/Module.cs b/Dependencies/CompatibilityLayers/EOS/Module.cs deleted file mode 100644 index ff15413c0..000000000 --- a/Dependencies/CompatibilityLayers/EOS/Module.cs +++ /dev/null @@ -1,84 +0,0 @@ -using MelonLoader.Modules; -using MelonLoader.NativeUtils; -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)] -[assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)] -[assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)] -[assembly: AssemblyProduct(MelonLoader.BuildInfo.Name)] -[assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)] -[assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)] -[assembly: Guid("5100810A-9842-4073-9658-E5841FDF9D73")] -[assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)] -[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] -[assembly: MelonLoader.PatchShield] - -namespace MelonLoader.CompatibilityLayers -{ - internal class EOS_Module : MelonModule - { - private delegate IntPtr LoadLibraryDetour(IntPtr path); - private NativeHook _hookWin; - - public override void OnInitialize() - { - var platform = Environment.OSVersion.Platform; - switch (platform) - { - case PlatformID.Win32S: - case PlatformID.Win32Windows: - case PlatformID.Win32NT: - case PlatformID.WinCE: - NativeLibrary lib = NativeLibrary.Load("kernel32"); - if (lib != null) - { - IntPtr loadLibraryWPtr = lib.GetExport("LoadLibraryW"); - if (loadLibraryWPtr != IntPtr.Zero) - { - IntPtr detourPtr = Marshal.GetFunctionPointerForDelegate((LoadLibraryDetour)DetourWin); - _hookWin = new NativeHook(loadLibraryWPtr, detourPtr); - _hookWin.Attach(); - } - } - break; - - case PlatformID.Unix: - case PlatformID.MacOSX: - - // TO-DO - - // libdl.so.2 - // dlopen - - break; - } - } - - private IntPtr DetourWin(IntPtr path) - { - if (path == IntPtr.Zero) - return _hookWin.Trampoline(path); - - var pathString = Marshal.PtrToStringUni(path); - if (string.IsNullOrEmpty(pathString)) - return _hookWin.Trampoline(path); - - if (pathString.EndsWith("EOSOVH-Win64-Shipping.dll") - || pathString.EndsWith("EOSOVH-Win32-Shipping.dll")) - { - _hookWin.Detach(); - return IntPtr.Zero; - } - - return _hookWin.Trampoline(path); - } - - ~EOS_Module() - { - _hookWin?.Detach(); - _hookWin = null; - } - } -} diff --git a/Dependencies/CompatibilityLayers/IPA/Module.cs b/Dependencies/CompatibilityLayers/IPA/Module.cs index 9a8dcce16..018263c95 100644 --- a/Dependencies/CompatibilityLayers/IPA/Module.cs +++ b/Dependencies/CompatibilityLayers/IPA/Module.cs @@ -2,11 +2,10 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using MelonLoader.MonoInternals; using IllusionPlugin; -using System.IO; using IllusionInjector; using MelonLoader.Modules; +using MelonLoader.Resolver; namespace MelonLoader.CompatibilityLayers { @@ -26,7 +25,7 @@ public override void OnInitialize() }; Assembly base_assembly = typeof(IPA_Module).Assembly; foreach (string assemblyName in assembly_list) - MonoResolveManager.GetAssemblyResolveInfo(assemblyName).Override = base_assembly; + MelonAssemblyResolver.GetAssemblyResolveInfo(assemblyName).Override = base_assembly; MelonAssembly.CustomMelonResolvers += Resolve; } diff --git a/Dependencies/CompatibilityLayers/Muse_Dash_Mono/Module.cs b/Dependencies/CompatibilityLayers/Muse_Dash_Mono/Module.cs index 6a0e9d428..0551542a4 100644 --- a/Dependencies/CompatibilityLayers/Muse_Dash_Mono/Module.cs +++ b/Dependencies/CompatibilityLayers/Muse_Dash_Mono/Module.cs @@ -3,8 +3,8 @@ using System.Linq; using System.Reflection; using MelonLoader.Modules; -using MelonLoader.MonoInternals; using ModHelper; +using MelonLoader.Resolver; namespace MelonLoader.CompatibilityLayers { @@ -24,7 +24,7 @@ public override void OnInitialize() }; Assembly base_assembly = typeof(Muse_Dash_Mono_Module).Assembly; foreach (string assemblyName in assembly_list) - MonoResolveManager.GetAssemblyResolveInfo(assemblyName).Override = base_assembly; + MelonAssemblyResolver.GetAssemblyResolveInfo(assemblyName).Override = base_assembly; MelonAssembly.CustomMelonResolvers += Resolve; } diff --git a/Dependencies/Il2CppAssemblyGenerator/Core.cs b/Dependencies/Il2CppAssemblyGenerator/Core.cs index 573ce9031..2c6d1608c 100644 --- a/Dependencies/Il2CppAssemblyGenerator/Core.cs +++ b/Dependencies/Il2CppAssemblyGenerator/Core.cs @@ -7,6 +7,7 @@ using System.Reflection; using JNISharp.NativeInterface; using MelonLoader.Il2CppAssemblyGenerator.Packages; +using MelonLoader.Il2CppAssemblyGenerator.Packages.Models; using MelonLoader.Modules; using MelonLoader.Utils; @@ -20,7 +21,7 @@ internal class Core : MelonModule internal static HttpClient webClient = null; - internal static Packages.Cpp2IL cpp2il = null; + internal static ExecutablePackage cpp2il = null; internal static Cpp2IL_StrippedCodeRegSupport cpp2il_scrs = null; internal static Packages.Il2CppInterop il2cppinterop = null; @@ -58,7 +59,13 @@ private static int Run() if (!MelonLaunchOptions.Il2CppAssemblyGenerator.OfflineMode) RemoteAPI.Contact(); - cpp2il = new Packages.Cpp2IL(); + Cpp2IL cpp2IL_netcore = new Cpp2IL(); + if (MelonUtils.IsWindows + && (cpp2IL_netcore.VersionSem < Cpp2IL.NetCoreMinVersion)) + cpp2il = new Cpp2IL_NetFramework(); + else + cpp2il = cpp2IL_netcore; + //cpp2il_scrs = new Cpp2IL_StrippedCodeRegSupport(cpp2il); il2cppinterop = new Packages.Il2CppInterop(); diff --git a/Dependencies/Il2CppAssemblyGenerator/Packages/Cpp2IL.cs b/Dependencies/Il2CppAssemblyGenerator/Packages/Cpp2IL.cs index 58472e221..2ef929497 100644 --- a/Dependencies/Il2CppAssemblyGenerator/Packages/Cpp2IL.cs +++ b/Dependencies/Il2CppAssemblyGenerator/Packages/Cpp2IL.cs @@ -21,9 +21,8 @@ namespace MelonLoader.Il2CppAssemblyGenerator.Packages { internal class Cpp2IL : Models.ExecutablePackage { - internal static SemVersion NetCoreMinVersion = SemVersion.Parse("2022.1.0-pre-release.17"); - private static SemVersion NewExecutionMinVersion = SemVersion.Parse("2022.0.999"); - private SemVersion VersionSem; + internal static SemVersion NetCoreMinVersion = SemVersion.Parse("2022.1.0-pre-release.18"); + internal SemVersion VersionSem; private string BaseFolder; private static string ReleaseName => diff --git a/Dependencies/Il2CppAssemblyGenerator/Packages/Cpp2IL_NetFramework.cs b/Dependencies/Il2CppAssemblyGenerator/Packages/Cpp2IL_NetFramework.cs new file mode 100644 index 000000000..f3ff1922e --- /dev/null +++ b/Dependencies/Il2CppAssemblyGenerator/Packages/Cpp2IL_NetFramework.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using Semver; + +namespace MelonLoader.Il2CppAssemblyGenerator.Packages +{ + internal class Cpp2IL_NetFramework : Models.ExecutablePackage + { + private static string ReleaseName => "Windows-Netframework472"; + internal Cpp2IL_NetFramework() + { + Version = MelonLaunchOptions.Il2CppAssemblyGenerator.ForceVersion_Dumper; +#if !DEBUG + if (string.IsNullOrEmpty(Version) || Version.Equals("0.0.0.0")) + Version = RemoteAPI.Info.ForceDumperVersion; +#endif + if (string.IsNullOrEmpty(Version) || Version.Equals("0.0.0.0")) + Version = $"2022.1.0-pre-release.15"; + + Name = nameof(Cpp2IL); + Destination = Path.Combine(Core.BasePath, Name); + OutputFolder = Path.Combine(Destination, "cpp2il_out"); + + URL = $"https://github.com/SamboyCoding/{Name}/releases/download/{Version}/{Name}-{Version}-{ReleaseName}.zip"; + ExeFilePath = Path.Combine(Destination, $"{Name}.exe"); + FilePath = Path.Combine(Core.BasePath, $"{Name}_{Version}.zip"); + } + + internal override bool ShouldSetup() + => string.IsNullOrEmpty(Config.Values.DumperVersion) + || !Config.Values.DumperVersion.Equals(Version); + + internal override void Cleanup() { } + + internal override void Save() + => Save(ref Config.Values.DumperVersion); + + internal override bool Execute() + { + if (SemVersion.Parse(Version) <= SemVersion.Parse("2022.0.999")) + return ExecuteOld(); + return ExecuteNew(); + } + + private bool ExecuteNew() + { + if (Execute([ + MelonDebug.IsEnabled() ? "--verbose" : string.Empty, + + "--game-path", + "\"" + Path.GetDirectoryName(Core.GameAssemblyPath) + "\"", + + "--exe-name", + "\"" + Process.GetCurrentProcess().ProcessName + "\"", + + "--output-as", + "dummydll", + + "--use-processor", + "attributeanalyzer", + "attributeinjector", + MelonLaunchOptions.Cpp2IL.CallAnalyzer ? "callanalyzer" : string.Empty, + MelonLaunchOptions.Cpp2IL.NativeMethodDetector ? "nativemethoddetector" : string.Empty, + //"deobfmap", + //"stablenamer", + + ], false, new Dictionary() { + {"NO_COLOR", "1"} + })) + return true; + + return false; + } + + private bool ExecuteOld() + { + if (Execute([ + MelonDebug.IsEnabled() ? "--verbose" : string.Empty, + + "--game-path", + "\"" + Path.GetDirectoryName(Core.GameAssemblyPath) + "\"", + + "--exe-name", + "\"" + Process.GetCurrentProcess().ProcessName + "\"", + + "--skip-analysis", + "--skip-metadata-txts", + "--disable-registration-prompts" + + ], false, new Dictionary() { + {"NO_COLOR", "1"} + })) + return true; + + return false; + } + } +} \ No newline at end of file diff --git a/Dependencies/Il2CppAssemblyGenerator/Packages/Models/ExecutablePackage.cs b/Dependencies/Il2CppAssemblyGenerator/Packages/Models/ExecutablePackage.cs index c477d7903..67fb72fab 100644 --- a/Dependencies/Il2CppAssemblyGenerator/Packages/Models/ExecutablePackage.cs +++ b/Dependencies/Il2CppAssemblyGenerator/Packages/Models/ExecutablePackage.cs @@ -104,7 +104,7 @@ internal bool Execute(string[] args, bool parenthesize_args = true, Dictionary + + + + + Libs\Il2Cppmscorlib.dll diff --git a/Dependencies/SupportModules/Il2Cpp/InteropInterface.cs b/Dependencies/SupportModules/Il2Cpp/InteropInterface.cs index 60ad60927..06668b909 100644 --- a/Dependencies/SupportModules/Il2Cpp/InteropInterface.cs +++ b/Dependencies/SupportModules/Il2Cpp/InteropInterface.cs @@ -3,7 +3,6 @@ using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; -using Il2CppInterop.Runtime.Runtime; using System; using System.Reflection; @@ -28,6 +27,8 @@ public FieldInfo MethodBaseToIl2CppFieldInfo(MethodBase method) public void RegisterTypeInIl2CppDomain(Type type, bool logSuccess) => ClassInjector.RegisterTypeInIl2Cpp(type, new() { LogSuccess = logSuccess }); + public void RegisterTypeInIl2CppDomainWithInterfaces(Type type, Type[] interfaces, bool logSuccess) + => ClassInjector.RegisterTypeInIl2Cpp(type, new() { LogSuccess = logSuccess, Interfaces = interfaces }); public bool IsInheritedFromIl2CppObjectBase(Type type) => (type != null) && type.IsSubclassOf(typeof(Il2CppObjectBase)); diff --git a/Dependencies/SupportModules/Il2Cpp/Main.cs b/Dependencies/SupportModules/Il2Cpp/Main.cs index bf4bf2ef2..49f65d0c1 100644 --- a/Dependencies/SupportModules/Il2Cpp/Main.cs +++ b/Dependencies/SupportModules/Il2Cpp/Main.cs @@ -3,19 +3,17 @@ using Il2CppInterop.Runtime.Startup; using MelonLoader.Support.Preferences; using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Runtime.InteropServices; -using HarmonyLib; using MelonLoader.CoreClrUtils; using UnityEngine; using Il2CppInterop.Common; -using Il2CppInterop.Runtime.InteropTypes; using Microsoft.Extensions.Logging; using MelonLoader.Utils; using System.IO; +#pragma warning disable CS0618 // Type or member is obsolete + namespace MelonLoader.Support { internal static class Main diff --git a/Dependencies/SupportModules/Mono/Mono.csproj b/Dependencies/SupportModules/Mono/Mono.csproj index 7c8204b89..99ac434c8 100644 --- a/Dependencies/SupportModules/Mono/Mono.csproj +++ b/Dependencies/SupportModules/Mono/Mono.csproj @@ -10,6 +10,11 @@ true embedded + + + + + Libs\UnityEngine.dll diff --git a/Dependencies/SupportModules/SceneHandler.cs b/Dependencies/SupportModules/SceneHandler.cs index e959f4b0c..01c4e45f5 100644 --- a/Dependencies/SupportModules/SceneHandler.cs +++ b/Dependencies/SupportModules/SceneHandler.cs @@ -5,6 +5,8 @@ using UnityEngine.Events; #endif +#pragma warning disable CA2013 + namespace MelonLoader.Support { internal static class SceneHandler diff --git a/MelonLoader/Attributes/RegisterTypeInIl2CppWithInterfaces.cs b/MelonLoader/Attributes/RegisterTypeInIl2CppWithInterfaces.cs new file mode 100644 index 000000000..5a1c679d9 --- /dev/null +++ b/MelonLoader/Attributes/RegisterTypeInIl2CppWithInterfaces.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace MelonLoader +{ + [AttributeUsage(AttributeTargets.Class)] + public class RegisterTypeInIl2CppWithInterfaces : Attribute //Naming violation? + { + internal static List registrationQueue = new List(); + internal static bool ready; + internal bool LogSuccess = true; + + internal Type[] Interfaces; + internal bool GetInterfacesFromType; + + public RegisterTypeInIl2CppWithInterfaces() + { + GetInterfacesFromType = true; + } + + public RegisterTypeInIl2CppWithInterfaces(bool logSuccess) + { + LogSuccess = logSuccess; + GetInterfacesFromType = true; + } + + public RegisterTypeInIl2CppWithInterfaces(params Type[] interfaces) + { + Interfaces = interfaces; + } + + public RegisterTypeInIl2CppWithInterfaces(bool logSuccess, params Type[] interfaces) + { + LogSuccess = logSuccess; + Interfaces = interfaces; + } + + public static void RegisterAssembly(Assembly asm) + { + if (!MelonUtils.IsGameIl2Cpp()) + return; + + if (!ready) + { + registrationQueue.Add(asm); + return; + } + + IEnumerable typeTbl = asm.GetValidTypes(); + if ((typeTbl == null) || (typeTbl.Count() <= 0)) + return; + + foreach (Type type in typeTbl) + { + object[] attTbl = type.GetCustomAttributes(typeof(RegisterTypeInIl2CppWithInterfaces), false); + if ((attTbl == null) || (attTbl.Length <= 0)) + continue; + + RegisterTypeInIl2CppWithInterfaces att = (RegisterTypeInIl2CppWithInterfaces)attTbl[0]; + if (att == null) + continue; + + Type[] interfaceArr = att.GetInterfacesFromType + ? type.GetInterfaces() + : att.Interfaces; + + InteropSupport.RegisterTypeInIl2CppDomainWithInterfaces(type, + interfaceArr, + att.LogSuccess); + } + } + + internal static void SetReady() + { + ready = true; + + if (registrationQueue == null) + return; + + foreach (var asm in registrationQueue) + RegisterAssembly(asm); + + registrationQueue = null; + } + } +} \ No newline at end of file diff --git a/MelonLoader/BackwardsCompatibility/Melon/AssemblyResolveInfo.cs b/MelonLoader/BackwardsCompatibility/Melon/AssemblyResolveInfo.cs new file mode 100644 index 000000000..f4ab132f5 --- /dev/null +++ b/MelonLoader/BackwardsCompatibility/Melon/AssemblyResolveInfo.cs @@ -0,0 +1,7 @@ +using System; + +namespace MelonLoader.MonoInternals +{ + [Obsolete("MelonLoader.MonoInternals.AssemblyResolveInfo is Only Here for Compatibility Reasons. Please use MelonLoader.Resolver.AssemblyResolveInfo instead.")] + public class AssemblyResolveInfo : Resolver.AssemblyResolveInfo { } +} diff --git a/MelonLoader/BackwardsCompatibility/Melon/MonoLibrary.cs b/MelonLoader/BackwardsCompatibility/Melon/MonoLibrary.cs new file mode 100644 index 000000000..e6cc27192 --- /dev/null +++ b/MelonLoader/BackwardsCompatibility/Melon/MonoLibrary.cs @@ -0,0 +1,11 @@ +#if !NET6_0_OR_GREATER + +using System; + +namespace MelonLoader.MonoInternals +{ + [Obsolete("MelonLoader.MonoInternals.MonoLibrary is Only Here for Compatibility Reasons. Please use MelonLoader.Utils.MonoLibrary instead.")] + public class MonoLibrary : Utils.MonoLibrary { } +} + +#endif \ No newline at end of file diff --git a/MelonLoader/BackwardsCompatibility/Melon/MonoResolveManager.cs b/MelonLoader/BackwardsCompatibility/Melon/MonoResolveManager.cs new file mode 100644 index 000000000..03c0ba138 --- /dev/null +++ b/MelonLoader/BackwardsCompatibility/Melon/MonoResolveManager.cs @@ -0,0 +1,39 @@ +using System; +using System.Reflection; + +namespace MelonLoader.MonoInternals +{ + [Obsolete("MelonLoader.MonoInternals.MonoResolveManager is Only Here for Compatibility Reasons. Please use MelonLoader.Resolver.MelonAssemblyResolver instead.")] + public static class MonoResolveManager + { + [Obsolete("MelonLoader.MonoInternals.MonoResolveManager.AddSearchDirectory is Only Here for Compatibility Reasons. Please use MelonLoader.Resolver.MelonAssemblyResolver.AddSearchDirectory instead.")] + public static void AddSearchDirectory(string path, int priority = 0) + => Resolver.SearchDirectoryManager.Add(path, priority); + + [Obsolete("MelonLoader.MonoInternals.MonoResolveManager.RemoveSearchDirectory is Only Here for Compatibility Reasons. Please use MelonLoader.Resolver.MelonAssemblyResolver.RemoveSearchDirectory instead.")] + public static void RemoveSearchDirectory(string path) + => Resolver.SearchDirectoryManager.Remove(path); + + [Obsolete("MelonLoader.MonoInternals.MonoResolveManager.OnAssemblyLoadHandler is Only Here for Compatibility Reasons. Please use MelonLoader.Resolver.MelonAssemblyResolver.dOnAssemblyLoad instead.")] + public delegate void OnAssemblyLoadHandler(Assembly assembly); + [Obsolete("MelonLoader.MonoInternals.MonoResolveManager.OnAssemblyLoad is Only Here for Compatibility Reasons. Please use MelonLoader.Resolver.MelonAssemblyResolver.OnAssemblyLoad instead.")] + public static event OnAssemblyLoadHandler OnAssemblyLoad; + internal static void SafeInvoke_OnAssemblyLoad(Assembly assembly) + => OnAssemblyLoad?.Invoke(assembly); + + [Obsolete("MelonLoader.MonoInternals.MonoResolveManager.OnAssemblyResolveHandler is Only Here for Compatibility Reasons. Please use MelonLoader.Resolver.MelonAssemblyResolver.dOnAssemblyResolve instead.")] + public delegate Assembly OnAssemblyResolveHandler(string name, Version version); + [Obsolete("MelonLoader.MonoInternals.MonoResolveManager.OnAssemblyLoad is Only Here for Compatibility Reasons. Please use MelonLoader.Resolver.MelonAssemblyResolver.OnAssemblyLoad instead.")] + public static event OnAssemblyResolveHandler OnAssemblyResolve; + internal static Assembly SafeInvoke_OnAssemblyResolve(string name, Version version) + => OnAssemblyResolve?.Invoke(name, version); + + [Obsolete("MelonLoader.MonoInternals.MonoResolveManager.GetAssemblyResolveInfo is Only Here for Compatibility Reasons. Please use MelonLoader.Resolver.MelonAssemblyResolver.GetAssemblyResolveInfo instead.")] + public static AssemblyResolveInfo GetAssemblyResolveInfo(string name) + => (AssemblyResolveInfo)Resolver.AssemblyManager.GetInfo(name); + + [Obsolete("MelonLoader.MonoInternals.MonoResolveManager.LoadInfoFromAssembly is Only Here for Compatibility Reasons. Please use MelonLoader.Resolver.MelonAssemblyResolver.LoadInfoFromAssembly instead.")] + public static void LoadInfoFromAssembly(Assembly assembly) + => Resolver.AssemblyManager.LoadInfo(assembly); + } +} diff --git a/MelonLoader/CompatibilityLayers/MelonCompatibilityLayer.cs b/MelonLoader/CompatibilityLayers/MelonCompatibilityLayer.cs index 7113e4579..0f380aa48 100644 --- a/MelonLoader/CompatibilityLayers/MelonCompatibilityLayer.cs +++ b/MelonLoader/CompatibilityLayers/MelonCompatibilityLayer.cs @@ -1,7 +1,6 @@ using MelonLoader.Modules; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using MelonLoader.Utils; @@ -13,12 +12,8 @@ public static class MelonCompatibilityLayer private static List layers = new List() { - // Il2Cpp Unity Tls - No longer needed in CoreCLR - // new MelonModule.Info(Path.Combine(baseDirectory, "Il2CppUnityTls.dll"), () => !MelonUtils.IsGameIl2Cpp()), - // Illusion Plugin Architecture new MelonModule.Info(Path.Combine(baseDirectory, "IPA.dll"), MelonUtils.IsGameIl2Cpp), - new MelonModule.Info(Path.Combine(baseDirectory, "EOS.dll"), () => !MelonUtils.IsWindows) }; private static void CheckGameLayerWithPlatform(string name, Func shouldBeIgnored) @@ -64,7 +59,7 @@ internal static void LoadModules() continue; MelonDebug.Msg($"Loading MelonModule '{m.fullPath}'"); - MelonModule.Load(m); + m.moduleGC = MelonModule.Load(m); } foreach (var file in Directory.GetFiles(baseDirectory)) diff --git a/MelonLoader/Core.cs b/MelonLoader/Core.cs index 62e2946d6..56fa6a280 100644 --- a/MelonLoader/Core.cs +++ b/MelonLoader/Core.cs @@ -1,10 +1,6 @@ using System; using System.Diagnostics; using System.Reflection; -using System.Security; -using MelonLoader.InternalUtils; -using MelonLoader.MonoInternals; -using MelonLoader.Utils; using System.IO; using bHapticsLib; using System.Threading; @@ -21,7 +17,7 @@ namespace MelonLoader { - internal static class Core + internal static class Core { private static bool _success = true; @@ -60,10 +56,6 @@ internal static int Initialize() if (MelonUtils.IsUnderWineOrSteamProton()) Pastel.ConsoleExtensions.Disable(); -#if NET6_0_OR_GREATER - Fixes.DotnetLoadFromManagedFolderFix.Install(); -#endif - Fixes.UnhandledException.Install(AppDomain.CurrentDomain); Fixes.ServerCertificateValidation.Install(); Assertions.LemonAssertMapping.Setup(); @@ -72,32 +64,35 @@ internal static int Initialize() BootstrapInterop.SetDefaultConsoleTitleWithGameName(UnityInformationHandler.GameName, UnityInformationHandler.GameVersion); + MelonAssemblyResolver.Setup(); + +#if NET6_0_OR_GREATER + + if (MelonLaunchOptions.Core.UserWantsDebugger && MelonEnvironment.IsDotnetRuntime) + { + MelonLogger.Msg("[Init] User requested debugger, attempting to launch now..."); + Debugger.Launch(); + } + + Environment.SetEnvironmentVariable("IL2CPP_INTEROP_DATABASES_LOCATION", MelonEnvironment.Il2CppAssembliesDirectory); + +#else + try { - if (!MonoLibrary.Setup() - || !MonoResolveManager.Setup()) + if (!MonoLibrary.Setup()) { _success = false; return 1; } } - catch (SecurityException) + catch (Exception ex) { - MelonDebug.Msg("[MonoLibrary] Caught SecurityException, assuming not running under mono and continuing with init"); - } - catch (MissingMethodException) - { - MelonDebug.Msg("[MonoLibrary] Caught MissingMethodException, assuming not running under mono and continuing with init"); - } - -#if NET6_0_OR_GREATER - if (MelonLaunchOptions.Core.UserWantsDebugger && MelonEnvironment.IsDotnetRuntime) - { - MelonLogger.Msg("[Init] User requested debugger, attempting to launch now..."); - Debugger.Launch(); + MelonDebug.Msg($"[MonoLibrary] Caught Exception: {ex}"); + _success = false; + return 1; } - Environment.SetEnvironmentVariable("IL2CPP_INTEROP_DATABASES_LOCATION", MelonEnvironment.Il2CppAssembliesDirectory); #endif MonoMod.Logs.DebugLog.OnLog += (string source, DateTime time, MonoMod.Logs.LogLevel level, string message) => MelonDebug.Msg($"[MonoMod] [{source}] [{level}] {message}"); @@ -128,7 +123,8 @@ internal static int Initialize() MelonCompatibilityLayer.LoadModules(); MelonHandler.LoadUserlibs(MelonEnvironment.UserLibsDirectory); - MelonHandler.LoadMelonsFromDirectory(MelonEnvironment.PluginsDirectory); + MelonHandler.LoadMelonFolders(MelonEnvironment.PluginsDirectory); + MelonEvents.MelonHarmonyEarlyInit.Invoke(); MelonEvents.OnPreInitialization.Invoke(); @@ -157,14 +153,18 @@ internal static int Start() return 1; MelonEvents.OnPreModsLoaded.Invoke(); - MelonHandler.LoadMelonsFromDirectory(MelonEnvironment.ModsDirectory); + MelonHandler.LoadMelonFolders(MelonEnvironment.ModsDirectory); MelonEvents.OnPreSupportModule.Invoke(); if (!SupportModule.Setup()) return 1; AddUnityDebugLog(); + +#if NET6_0_OR_GREATER RegisterTypeInIl2Cpp.SetReady(); + RegisterTypeInIl2CppWithInterfaces.SetReady(); +#endif MelonEvents.MelonHarmonyInit.Invoke(); MelonEvents.OnApplicationStart.Invoke(); @@ -196,9 +196,13 @@ internal static void WelcomeMessage() var archString = MelonUtils.IsGame32Bit() ? "x86" : "x64"; MelonLogger.MsgDirect($"Game Arch: {archString}"); MelonLogger.MsgDirect("------------------------------"); - MelonLogger.MsgDirect($"Command-Line: {string.Join(" ", MelonLaunchOptions.CommandLineArgs)}"); + MelonLogger.MsgDirect("Command-Line: "); + foreach (var pair in MelonLaunchOptions.InternalArguments) + if (string.IsNullOrEmpty(pair.Value)) + MelonLogger.MsgDirect($" {pair.Key}"); + else + MelonLogger.MsgDirect($" {pair.Key} = {pair.Value}"); MelonLogger.MsgDirect("------------------------------"); - MelonEnvironment.PrintEnvironment(); } diff --git a/MelonLoader/Fixes/DotnetLoadFromManagedFolderFix.cs b/MelonLoader/Fixes/DotnetLoadFromManagedFolderFix.cs deleted file mode 100644 index 0b3e69b52..000000000 --- a/MelonLoader/Fixes/DotnetLoadFromManagedFolderFix.cs +++ /dev/null @@ -1,72 +0,0 @@ -#if NET6_0_OR_GREATER -using MelonLoader.Utils; -using System.IO; -using System.Reflection; -using System.Runtime.Loader; - -namespace MelonLoader.Fixes -{ - internal static class DotnetLoadFromManagedFolderFix - { - //TODO Update for non-windows platforms in future, or when updating runtime - private static readonly string OurRuntimeDir = Path.Combine(MelonEnvironment.OurRuntimeDirectory, "runtimes", "win", "lib", "net6.0"); - - internal static void Install() - { - AssemblyLoadContext.Default.Resolving += OnResolve; - } - - private static Assembly TryLoad(AssemblyLoadContext alc, string path) - { - if (File.Exists(path)) - { - MelonDebug.Msg($"[DotnetManagedFolder] Loading from {path}..."); - return alc.LoadFromAssemblyPath(path); - } - return null; - } - - private static Assembly OnResolve(AssemblyLoadContext alc, AssemblyName name) - { - var ret = TryFind(alc, name); - if (ret == null) - MelonDebug.Msg($"[DotnetManagedFolder] Failed to find {name.Name} in any of the known search directories"); - return ret; - } - - internal static Assembly TryFind(AssemblyLoadContext alc, AssemblyName name) - { - // Redirect ModHandler to main MelonLoader dll (us) - if (name.Name == "MelonLoader.ModHandler") - return Assembly.GetExecutingAssembly(); - - var ret = TryLoadFromFolders(alc, name.Name + ".dll"); - if (ret == null) - TryLoadFromFolders(alc, name.Name + ".exe"); - - return ret; - } - - private static Assembly TryLoadFromFolders(AssemblyLoadContext alc, string filename) - { - var osSpecificPath = Path.Combine(OurRuntimeDir, filename); - var il2cppPath = Path.Combine(MelonEnvironment.Il2CppAssembliesDirectory, filename); - var managedPath = Path.Combine(MelonEnvironment.MelonManagedDirectory, filename); - var modsPath = Path.Combine(MelonEnvironment.ModsDirectory, filename); - var userlibsPath = Path.Combine(MelonEnvironment.UserLibsDirectory, filename); - var gameRootPath = Path.Combine(MelonEnvironment.GameRootDirectory, filename); - var runtimeSpecificPath = Path.Combine(MelonEnvironment.OurRuntimeDirectory, filename); - - var ret = TryLoad(alc, osSpecificPath) - ?? TryLoad(alc, il2cppPath) - ?? TryLoad(alc, managedPath) - ?? TryLoad(alc, modsPath) - ?? TryLoad(alc, userlibsPath) - ?? TryLoad(alc, runtimeSpecificPath) - ?? TryLoad(alc, gameRootPath); - - return ret; - } - } -} -#endif \ No newline at end of file diff --git a/MelonLoader/Fixes/Il2CppICallInjector.cs b/MelonLoader/Fixes/Il2CppICallInjector.cs index 3269ab30e..54744971e 100644 --- a/MelonLoader/Fixes/Il2CppICallInjector.cs +++ b/MelonLoader/Fixes/Il2CppICallInjector.cs @@ -163,6 +163,30 @@ private static IntPtr il2cpp_resolve_icall_Detour(IntPtr signature) return pair.Item3; } + private static Type FindType(string typeFullName) + { + if (string.IsNullOrEmpty(typeFullName)) + return null; + + Type result = null; + 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) + break; + } + + return result; + } + private static bool ShouldInject(string signature, out MethodInfo unityShimMethod) { unityShimMethod = null; @@ -172,7 +196,7 @@ private static bool ShouldInject(string signature, out MethodInfo unityShimMetho string typeName = split[0]; // Find Managed Type - Type newType = Il2CppInteropFixes.FixedFindType(typeName); + Type newType = FindType(typeName); if (newType == null) return false; diff --git a/MelonLoader/Fixes/Il2CppInteropFixes.cs b/MelonLoader/Fixes/Il2CppInteropFixes.cs index 73eed5036..ea58d70aa 100644 --- a/MelonLoader/Fixes/Il2CppInteropFixes.cs +++ b/MelonLoader/Fixes/Il2CppInteropFixes.cs @@ -17,6 +17,9 @@ using MelonLoader.Utils; using Il2CppInterop.Generator.Contexts; using AsmResolver.DotNet; +using Il2CppInterop.HarmonySupport; + +#pragma warning disable CS8632 namespace MelonLoader.Fixes { @@ -26,6 +29,8 @@ namespace MelonLoader.Fixes // fixes the rest of: https://github.com/BepInEx/Il2CppInterop/pull/134 internal unsafe static class Il2CppInteropFixes { + private static MelonLogger.Instance _logger = new("Il2CppInterop"); + private static Dictionary> _assemblyLookup = new(); private static Dictionary _typeLookup = new(); private static Dictionary _typeNameLookup = new(); @@ -59,6 +64,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 _reportException; + private static MethodInfo _reportException_Prefix; internal static void Install() { @@ -70,11 +77,16 @@ internal static void Install() Type ilGeneratorEx = typeof(ILGeneratorEx); Type rewriteGlobalContextType = typeof(RewriteGlobalContext); Type il2cppType = typeof(IL2CPP); + Type harmonySupportType = typeof(HarmonySupport); Type injectorHelpersType = classInjectorType.Assembly.GetType("Il2CppInterop.Runtime.Injection.InjectorHelpers"); if (injectorHelpersType == null) throw new Exception("Failed to get InjectorHelpers"); + Type detourMethodPatcherType = harmonySupportType.Assembly.GetType("Il2CppInterop.HarmonySupport.Il2CppDetourMethodPatcher"); + if (detourMethodPatcherType == null) + throw new Exception("Failed to get Il2CppDetourMethodPatcher"); + _systemTypeFromIl2CppType = classInjectorType.GetMethod("SystemTypeFromIl2CppType", BindingFlags.NonPublic | BindingFlags.Static); if (_systemTypeFromIl2CppType == null) throw new Exception("Failed to get ClassInjector.SystemTypeFromIl2CppType"); @@ -137,7 +149,12 @@ 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"); + + _reportException = detourMethodPatcherType.GetMethod("ReportException", + BindingFlags.NonPublic | BindingFlags.Static); + if (_reportException == null) + throw new Exception("Failed to get Il2CppDetourMethodPatcher.ReportException"); _fixedFindType = thisType.GetMethod(nameof(FixedFindType), BindingFlags.NonPublic | BindingFlags.Static); _fixedAddTypeToLookup = thisType.GetMethod(nameof(FixedAddTypeToLookup), BindingFlags.NonPublic | BindingFlags.Static); @@ -154,6 +171,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); + _reportException_Prefix = thisType.GetMethod(nameof(ReportException_Prefix), BindingFlags.NonPublic | BindingFlags.Static); MelonDebug.Msg("Patching Il2CppInterop ClassInjector.SystemTypeFromIl2CppType..."); Core.HarmonyInstance.Patch(_systemTypeFromIl2CppType, @@ -202,6 +220,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 Il2CppDetourMethodPatcher.ReportException..."); + Core.HarmonyInstance.Patch(_reportException, + new HarmonyMethod(_reportException_Prefix)); } catch (Exception e) { @@ -275,6 +297,13 @@ private static void FixedAddTypeToLookup(Type type, IntPtr typePointer) _typeLookup.Add(typePointer, type); } + private static bool ReportException_Prefix(Exception __0) + { + _logger.Error("During invoking native->managed trampoline", __0); + + return false; + } + private static bool EmitObjectToPointer_Prefix(bool __7, ref bool __8) { __8 = __7; diff --git a/MelonLoader/InternalUtils/DependencyGraph.cs b/MelonLoader/InternalUtils/DependencyGraph.cs index db9b81f11..546fbdbaa 100644 --- a/MelonLoader/InternalUtils/DependencyGraph.cs +++ b/MelonLoader/InternalUtils/DependencyGraph.cs @@ -3,16 +3,10 @@ using System.Text; using System.Reflection; using System.IO; +using MelonLoader.Resolver; -#if NET35 - -using MelonLoader.MonoInternals.ResolveInternals; - -#elif NET6_0_OR_GREATER - -using MelonLoader.Fixes; +#if NET6_0_OR_GREATER using System.Runtime.Loader; - #endif namespace MelonLoader.InternalUtils @@ -145,7 +139,12 @@ private static bool TryLoad(AssemblyName assembly) { try { - Assembly asm = Assembly.Load(assembly); +#if NET6_0_OR_GREATER + var asm = AssemblyLoadContext.Default.LoadFromAssemblyName(assembly); +#else + var asm = Assembly.Load(assembly); +#endif + if (asm == null) return false; return true; @@ -163,11 +162,7 @@ private static bool TryResolve(AssemblyName assembly) { try { -#if NET35 Assembly asm = SearchDirectoryManager.Scan(assembly.Name); -#elif NET6_0_OR_GREATER - Assembly asm = DotnetLoadFromManagedFolderFix.TryFind(AssemblyLoadContext.Default, assembly); -#endif if (asm == null) return false; return true; diff --git a/MelonLoader/Lemons/Cryptography/LemonMD5.cs b/MelonLoader/Lemons/Cryptography/LemonMD5.cs index 5e0b37ff8..475b655cd 100644 --- a/MelonLoader/Lemons/Cryptography/LemonMD5.cs +++ b/MelonLoader/Lemons/Cryptography/LemonMD5.cs @@ -6,14 +6,19 @@ namespace MelonLoader.Lemons.Cryptography public class LemonMD5 { private HashAlgorithm algorithm; + private static LemonMD5 static_algorithm = new(); - public LemonMD5() + public LemonMD5() { algorithm = (HashAlgorithm)CryptoConfig.CreateFromName("System.Security.Cryptography.MD5"); algorithm.SetHashSizeValue(256); } - public byte[] ComputeHash(byte[] buffer) => algorithm.ComputeHash(buffer); + public static byte[] ComputeMD5Hash(byte[] buffer) => static_algorithm.ComputeHash(buffer); + public static byte[] ComputeMD5Hash(byte[] buffer, int offset, int count) => static_algorithm.ComputeHash(buffer, offset, count); + public static byte[] ComputeMD5Hash(Stream inputStream) => static_algorithm.ComputeHash(inputStream); + + public byte[] ComputeHash(byte[] buffer) => algorithm.ComputeHash(buffer); public byte[] ComputeHash(byte[] buffer, int offset, int count) => algorithm.ComputeHash(buffer, offset, count); public byte[] ComputeHash(Stream inputStream) => algorithm.ComputeHash(inputStream); } diff --git a/MelonLoader/Lemons/Cryptography/LemonSHA256.cs b/MelonLoader/Lemons/Cryptography/LemonSHA256.cs index 91be6a6eb..be2ab6115 100644 --- a/MelonLoader/Lemons/Cryptography/LemonSHA256.cs +++ b/MelonLoader/Lemons/Cryptography/LemonSHA256.cs @@ -7,14 +7,19 @@ namespace MelonLoader.Lemons.Cryptography public class LemonSHA256 { private HashAlgorithm algorithm; + private static LemonSHA256 static_algorithm = new(); - public LemonSHA256() + public LemonSHA256() { algorithm = (HashAlgorithm)CryptoConfig.CreateFromName("System.Security.Cryptography.SHA256"); algorithm.SetHashSizeValue(256); } - public byte[] ComputeHash(byte[] buffer) => algorithm.ComputeHash(buffer); + public static byte[] ComputeSHA256Hash(byte[] buffer) => static_algorithm.ComputeHash(buffer); + public static byte[] ComputeSHA256Hash(byte[] buffer, int offset, int count) => static_algorithm.ComputeHash(buffer, offset, count); + public static byte[] ComputeSHA256Hash(Stream inputStream) => static_algorithm.ComputeHash(inputStream); + + public byte[] ComputeHash(byte[] buffer) => algorithm.ComputeHash(buffer); public byte[] ComputeHash(byte[] buffer, int offset, int count) => algorithm.ComputeHash(buffer, offset, count); public byte[] ComputeHash(Stream inputStream) => algorithm.ComputeHash(inputStream); } diff --git a/MelonLoader/Lemons/Cryptography/LemonSHA512.cs b/MelonLoader/Lemons/Cryptography/LemonSHA512.cs index 6bec548bb..fac019107 100644 --- a/MelonLoader/Lemons/Cryptography/LemonSHA512.cs +++ b/MelonLoader/Lemons/Cryptography/LemonSHA512.cs @@ -6,12 +6,17 @@ namespace MelonLoader.Lemons.Cryptography public class LemonSHA512 { private HashAlgorithm algorithm; + private static LemonSHA512 static_algorithm = new(); - public LemonSHA512() + public LemonSHA512() { algorithm = (HashAlgorithm)CryptoConfig.CreateFromName("System.Security.Cryptography.SHA512"); algorithm.SetHashSizeValue(512); - } + } + + public static byte[] ComputeSHA512Hash(byte[] buffer) => static_algorithm.ComputeHash(buffer); + public static byte[] ComputeSHA512Hash(byte[] buffer, int offset, int count) => static_algorithm.ComputeHash(buffer, offset, count); + public static byte[] ComputeSHA512Hash(Stream inputStream) => static_algorithm.ComputeHash(inputStream); public byte[] ComputeHash(byte[] buffer) => algorithm.ComputeHash(buffer); public byte[] ComputeHash(byte[] buffer, int offset, int count) => algorithm.ComputeHash(buffer, offset, count); diff --git a/MelonLoader/MelonLaunchOptions.cs b/MelonLoader/MelonLaunchOptions.cs index c1f413ba4..d10efc6dd 100644 --- a/MelonLoader/MelonLaunchOptions.cs +++ b/MelonLoader/MelonLaunchOptions.cs @@ -16,6 +16,7 @@ public static class MelonLaunchOptions /// /// public static Dictionary ExternalArguments { get; private set; } = new Dictionary(); + public static Dictionary InternalArguments { get; private set; } = new Dictionary(); /// /// Array of All Command Line Arguments @@ -64,6 +65,7 @@ internal static void Load() // Parse Argumentless Commands if (WithoutArg.TryGetValue(noPrefixCmd, out Action withoutArgFunc)) { + InternalArguments.Add(noPrefixCmd, null); withoutArgFunc(); continue; } @@ -90,6 +92,7 @@ internal static void Load() // Parse Argument Commands if (WithArg.TryGetValue(noPrefixCmd, out Action withArgFunc)) { + InternalArguments.Add(noPrefixCmd, cmdArg); withArgFunc(cmdArg); continue; } diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index ebe4affbd..c7bf6d119 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -11,7 +11,7 @@ true embedded - 0.6.5 + 0.6.6 LavaGang.MelonLoader modding unity https://github.com/LavaGang/MelonLoader diff --git a/MelonLoader/MelonUtils.cs b/MelonLoader/MelonUtils.cs index 51e15e9c0..5b43368a1 100644 --- a/MelonLoader/MelonUtils.cs +++ b/MelonLoader/MelonUtils.cs @@ -45,6 +45,7 @@ internal static void Setup(AppDomain domain) if (!Directory.Exists(MelonEnvironment.UserLibsDirectory)) Directory.CreateDirectory(MelonEnvironment.UserLibsDirectory); + AddNativeDLLDirectory(MelonEnvironment.UserLibsDirectory); MelonHandler.Setup(); UnityInformationHandler.Setup(); @@ -487,6 +488,21 @@ public static string GetFileProductName(string filepath) return null; } + public static void AddNativeDLLDirectory(string path) + { + if (!IsWindows && !IsUnix) + return; + + path = Path.GetFullPath(path); + if (!Directory.Exists(path)) + return; + + string envName = IsWindows ? "PATH" : "LD_LIBRARY_PATH"; + string envSep = IsWindows ? ";" : ":"; + string envPaths = Environment.GetEnvironmentVariable(envName); + Environment.SetEnvironmentVariable(envName, $"{envPaths}{envSep}{path}"); + } + internal static void SetupWineCheck() { if (IsUnix || IsMac) diff --git a/MelonLoader/Melons/MelonAssembly.cs b/MelonLoader/Melons/MelonAssembly.cs index 18449b19d..8806bdc03 100644 --- a/MelonLoader/Melons/MelonAssembly.cs +++ b/MelonLoader/Melons/MelonAssembly.cs @@ -290,7 +290,10 @@ public void LoadMelons() } } +#if NET6_0_OR_GREATER RegisterTypeInIl2Cpp.RegisterAssembly(Assembly); + RegisterTypeInIl2CppWithInterfaces.RegisterAssembly(Assembly); +#endif if (rottenMelons.Count != 0) { diff --git a/MelonLoader/Melons/MelonFolderHandler.cs b/MelonLoader/Melons/MelonFolderHandler.cs new file mode 100644 index 000000000..49db61987 --- /dev/null +++ b/MelonLoader/Melons/MelonFolderHandler.cs @@ -0,0 +1,176 @@ +using System.Collections.Generic; +using System.Drawing; +using System.IO; + +namespace MelonLoader.Melons +{ + internal class MelonFolderHandler + { + internal static void Scan(string path) where T : MelonTypeBase + { + // Get Full Directory Path + path = Path.GetFullPath(path); + + // Log Loading Message + var loadingMsg = $"Loading {MelonTypeBase.TypeName}s from '{path}'..."; + MelonLogger.WriteSpacer(); + MelonLogger.Msg(loadingMsg); + + // Parse Folders + bool hasWroteLine = false; + List melonAssemblies = new(); + ProcessFolder(path, ref hasWroteLine, ref melonAssemblies); + + // Parse Queue + var melons = new List(); + foreach (var asm in melonAssemblies) + { + // Load Melons from Assembly + asm.LoadMelons(); + + // Parse Loaded Melons + foreach (var m in asm.LoadedMelons) + { + // Validate Type + if (m is T t) + { + melons.Add(t); + continue; + } + + // Log Failure + MelonLogger.Warning($"Failed to load Melon '{m.Info.Name}' from '{path}': The given Melon is a {m.MelonTypeName} and cannot be loaded as a {MelonTypeBase.TypeName}. Make sure it's in the right folder."); + } + } + + // Log + if (hasWroteLine) + MelonLogger.WriteSpacer(); + + // Register and Sort Melons + MelonBase.RegisterSorted(melons); + + // Log + if (hasWroteLine) + MelonLogger.WriteLine(Color.Magenta); + + // Log Melon Count + var count = MelonTypeBase._registeredMelons.Count; + MelonLogger.Msg($"{count} {MelonTypeBase.TypeName.MakePlural(count)} loaded."); + if (MelonHandler.firstSpacer || (typeof(T) == typeof(MelonMod))) + MelonLogger.WriteSpacer(); + MelonHandler.firstSpacer = true; + } + + private static void LoadFolder(string path, + bool addToList, + ref bool hasWroteLine, + ref List melonAssemblies) where T : MelonTypeBase + { + // Get DLLs in Directory + var files = Directory.GetFiles(path, "*.dll", SearchOption.TopDirectoryOnly); + foreach (var f in files) + { + // Log + if (!hasWroteLine) + { + hasWroteLine = true; + MelonLogger.WriteLine(Color.Magenta); + } + + // Load Assembly + var asm = MelonAssembly.LoadMelonAssembly(f, false); + if (asm == null) + continue; + + // Queue Assembly for Melon Parsing + if (addToList) + melonAssemblies.Add(asm); + } + } + + private static bool IsUserLibsFolder(string dirNameLower) + => dirNameLower.StartsWith("userlibs") + || dirNameLower.EndsWith("userlibs"); + + private static bool IsDisabledFolder(string path, + out string dirNameLower) + { + string dirName = new DirectoryInfo(path).Name; + dirNameLower = dirName.ToLowerInvariant(); + return dirNameLower.StartsWith("disabled") + || dirNameLower.EndsWith("disabled") + || dirNameLower.StartsWith("old") + || dirNameLower.EndsWith("old"); + } + + private static void ProcessFolder(string path, + ref bool hasWroteLine, + ref List melonAssemblies) where T : MelonTypeBase + { + // Validate Path + if (!Directory.Exists(path)) + return; + + // Add Base Path to Resolver + Resolver.MelonAssemblyResolver.AddSearchDirectory(path); + + // Get Directories + var directories = Directory.GetDirectories(path, "*", SearchOption.AllDirectories); + + // Add Directories to Resolver + if ((directories != null) && (directories.Length > 0)) + { + foreach (var dir in directories) + { + // Validate Path + if (!Directory.Exists(dir)) + continue; + + // Skip Disabled Folders + if (IsDisabledFolder(dir, out string dirNameLower)) + continue; + + // Load Assemblies + if (IsUserLibsFolder(dirNameLower)) + MelonUtils.AddNativeDLLDirectory(dir); + Resolver.MelonAssemblyResolver.AddSearchDirectory(dir); + } + + // Load UserLibs + foreach (var dir in directories) + { + // Validate Path + if (!Directory.Exists(dir)) + continue; + + // Skip Disabled Folders and any folders that doesn't end with or isn't equal to UserLibs + if (IsDisabledFolder(dir, out string dirNameLower) + || !IsUserLibsFolder(dirNameLower)) + continue; + + // Load Assemblies + LoadFolder(dir, false, ref hasWroteLine, ref melonAssemblies); + } + + // Load Melons from Extended Folders + foreach (var dir in directories) + { + // Validate Path + if (!Directory.Exists(dir)) + continue; + + // Skip Disabled Folders + if (IsDisabledFolder(dir, out _)) + continue; + + // Load Melons from Extended Folder + LoadFolder(dir, true, ref hasWroteLine, ref melonAssemblies); + } + } + + // Load Melons from Base Path + LoadFolder(path, true, ref hasWroteLine, ref melonAssemblies); + } + } +} \ No newline at end of file diff --git a/MelonLoader/Melons/MelonHandler.cs b/MelonLoader/Melons/MelonHandler.cs index d0d230273..80725eaf7 100644 --- a/MelonLoader/Melons/MelonHandler.cs +++ b/MelonLoader/Melons/MelonHandler.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Reflection; +using MelonLoader.Melons; using MelonLoader.Utils; namespace MelonLoader @@ -31,7 +32,7 @@ internal static void Setup() Directory.CreateDirectory(MelonEnvironment.ModsDirectory); } - private static bool firstSpacer = false; + internal static bool firstSpacer = false; public static void LoadMelonsFromDirectory(string path) where T : MelonTypeBase { path = Path.GetFullPath(path); @@ -42,7 +43,7 @@ public static void LoadMelonsFromDirectory(string path) where T : MelonTypeBa bool hasWroteLine = false; - var files = Directory.GetFiles(path, "*.dll"); + var files = Directory.GetFiles(path, "*.dll", SearchOption.TopDirectoryOnly); var melonAssemblies = new List(); foreach (var f in files) { @@ -101,9 +102,7 @@ public static void LoadUserlibs(string path) MelonLogger.Msg(loadingMsg); bool hasWroteLine = false; - - var files = Directory.GetFiles(path, "*.dll"); - var melonAssemblies = new List(); + var files = Directory.GetFiles(path, "*.dll", SearchOption.TopDirectoryOnly); foreach (var f in files) { if (!hasWroteLine) @@ -112,14 +111,13 @@ public static void LoadUserlibs(string path) MelonLogger.WriteLine(Color.Magenta); } - var asm = MelonAssembly.LoadMelonAssembly(f, false); - if (asm == null) - continue; - - melonAssemblies.Add(asm); + MelonAssembly.LoadMelonAssembly(f, false); } } + public static void LoadMelonFolders(string path) where T : MelonTypeBase + => MelonFolderHandler.Scan(path); + #region Obsolete Members /// /// List of Plugins. diff --git a/MelonLoader/Modules/MelonModule.cs b/MelonLoader/Modules/MelonModule.cs index 46a46afb0..ee23a5f83 100644 --- a/MelonLoader/Modules/MelonModule.cs +++ b/MelonLoader/Modules/MelonModule.cs @@ -4,6 +4,10 @@ using System.Linq; using System.Reflection; +#if NET6_0_OR_GREATER +using System.Runtime.Loader; +#endif + namespace MelonLoader.Modules { /// @@ -53,7 +57,11 @@ internal static MelonModule Load(Info moduleInfo) Assembly asm; try { +#if NET6_0_OR_GREATER + asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(moduleInfo.fullPath); +#else asm = Assembly.LoadFrom(moduleInfo.fullPath); +#endif } catch (Exception ex) { @@ -115,6 +123,7 @@ public class Info public readonly string fullPath; internal readonly Func shouldBeRemoved; internal readonly Func shouldBeIgnored; + internal MelonModule moduleGC; internal Info(string path, Func shouldBeIgnored = null, Func shouldBeRemoved = null) { diff --git a/MelonLoader/Properties/AssemblyInfo.cs b/MelonLoader/Properties/AssemblyInfo.cs index 6d54a6130..3277cba2d 100644 --- a/MelonLoader/Properties/AssemblyInfo.cs +++ b/MelonLoader/Properties/AssemblyInfo.cs @@ -17,4 +17,5 @@ [assembly: InternalsVisibleTo("Il2CppAssemblyGenerator")] [assembly: InternalsVisibleTo("Il2CppUnityTls")] [assembly: InternalsVisibleTo("Il2Cpp")] -[assembly: InternalsVisibleTo("MelonStartScreen")] \ No newline at end of file +[assembly: InternalsVisibleTo("MelonStartScreen")] +[assembly: InternalsVisibleTo("EOS")] \ No newline at end of file diff --git a/MelonLoader/Properties/BuildInfo.cs b/MelonLoader/Properties/BuildInfo.cs index c2ea9782c..c6ff6b100 100644 --- a/MelonLoader/Properties/BuildInfo.cs +++ b/MelonLoader/Properties/BuildInfo.cs @@ -6,6 +6,6 @@ public static class BuildInfo public const string Description = "MelonLoader"; public const string Author = "Lava Gang"; public const string Company = "discord.gg/2Wn3N2P"; - public const string Version = "0.6.5"; + public const string Version = "0.6.6"; } } \ No newline at end of file diff --git a/MelonLoader/MonoInternals/ResolveInternals/AssemblyManager.cs b/MelonLoader/Resolver/AssemblyManager.cs similarity index 79% rename from MelonLoader/MonoInternals/ResolveInternals/AssemblyManager.cs rename to MelonLoader/Resolver/AssemblyManager.cs index 2d97578c0..f0d9b583a 100644 --- a/MelonLoader/MonoInternals/ResolveInternals/AssemblyManager.cs +++ b/MelonLoader/Resolver/AssemblyManager.cs @@ -1,11 +1,18 @@ using System; using System.Collections.Generic; using System.Reflection; + +#if NET6_0_OR_GREATER +using System.Runtime.Loader; +#else using System.Runtime.CompilerServices; +#endif + +#pragma warning disable CS8632 -namespace MelonLoader.MonoInternals.ResolveInternals +namespace MelonLoader.Resolver { - internal static class AssemblyManager + internal class AssemblyManager { internal static Dictionary InfoDict = new Dictionary(); @@ -29,11 +36,6 @@ internal static AssemblyResolveInfo GetInfo(string name) return InfoDict[name]; } - private static Assembly Resolve(string requested_name, ushort major, ushort minor, ushort build, ushort revision, bool is_preload) - { - Version requested_version = new Version(major, minor, build, revision); - return Resolve(requested_name, requested_version, is_preload); - } private static Assembly Resolve(string requested_name, Version requested_version, bool is_preload) { // Get Resolve Information Object @@ -44,7 +46,7 @@ private static Assembly Resolve(string requested_name, Version requested_version // Run Passthrough Events if (assembly == null) - assembly = MonoResolveManager.SafeInvoke_OnAssemblyResolve(requested_name, requested_version); + assembly = MelonAssemblyResolver.SafeInvoke_OnAssemblyResolve(requested_name, requested_version); // Search Directories if (is_preload && (assembly == null)) @@ -70,10 +72,30 @@ internal static void LoadInfo(Assembly assembly) resolveInfo.SetVersionSpecific(assemblyName.Version, assembly); // Run Passthrough Events - MonoResolveManager.SafeInvoke_OnAssemblyLoad(assembly); + MelonAssemblyResolver.SafeInvoke_OnAssemblyLoad(assembly); + } + +#if NET6_0_OR_GREATER + + private static Assembly? Resolve(AssemblyLoadContext alc, AssemblyName name) + => Resolve(name.Name, name.Version, true); + + private static void InstallHooks() + { + AssemblyLoadContext.Default.Resolving += Resolve; + } + +#else + + private static Assembly Resolve(string requested_name, ushort major, ushort minor, ushort build, ushort revision, bool is_preload) + { + Version requested_version = new Version(major, minor, build, revision); + return Resolve(requested_name, requested_version, is_preload); } [MethodImpl(MethodImplOptions.InternalCall)] private extern static void InstallHooks(); + +#endif } } diff --git a/MelonLoader/MonoInternals/AssemblyResolveInfo.cs b/MelonLoader/Resolver/AssemblyResolveInfo.cs similarity index 94% rename from MelonLoader/MonoInternals/AssemblyResolveInfo.cs rename to MelonLoader/Resolver/AssemblyResolveInfo.cs index 7cc1df4e9..eb975cfbc 100644 --- a/MelonLoader/MonoInternals/AssemblyResolveInfo.cs +++ b/MelonLoader/Resolver/AssemblyResolveInfo.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Reflection; -namespace MelonLoader.MonoInternals +namespace MelonLoader.Resolver { public class AssemblyResolveInfo { @@ -17,7 +17,7 @@ internal Assembly Resolve(Version requested_version) return Override; // Check for Requested Version - if ((requested_version != null) + if (requested_version != null && GetVersionSpecific(requested_version, out Assembly assembly)) return assembly; diff --git a/MelonLoader/MonoInternals/MonoResolveManager.cs b/MelonLoader/Resolver/MelonAssemblyResolver.cs similarity index 66% rename from MelonLoader/MonoInternals/MonoResolveManager.cs rename to MelonLoader/Resolver/MelonAssemblyResolver.cs index 0d775be2b..d4062503a 100644 --- a/MelonLoader/MonoInternals/MonoResolveManager.cs +++ b/MelonLoader/Resolver/MelonAssemblyResolver.cs @@ -1,17 +1,22 @@ using System; using System.IO; using System.Reflection; -using MelonLoader.MonoInternals.ResolveInternals; using MelonLoader.Utils; -namespace MelonLoader.MonoInternals +#if NET6_0_OR_GREATER +using System.Runtime.Loader; +#endif + +#pragma warning disable CS0618 // Type or member is obsolete + +namespace MelonLoader.Resolver { - public static class MonoResolveManager + public class MelonAssemblyResolver { - internal static bool Setup() + internal static void Setup() { if (!AssemblyManager.Setup()) - return false; + return; // Setup Search Directories string[] searchdirlist = @@ -22,10 +27,12 @@ internal static bool Setup() MelonEnvironment.MelonBaseDirectory, MelonEnvironment.GameRootDirectory, MelonEnvironment.OurRuntimeDirectory, + MelonEnvironment.Il2CppAssembliesDirectory, + MelonEnvironment.UnityGameManagedDirectory, }; foreach (string path in searchdirlist) AddSearchDirectory(path); - + ForceResolveRuntime("Mono.Cecil.dll"); ForceResolveRuntime("MonoMod.exe"); ForceResolveRuntime("MonoMod.Utils.dll"); @@ -37,13 +44,11 @@ internal static bool Setup() "MelonLoader", "MelonLoader.ModHandler", }; - Assembly base_assembly = typeof(MonoResolveManager).Assembly; + Assembly base_assembly = typeof(MelonAssemblyResolver).Assembly; foreach (string assemblyName in assembly_list) GetAssemblyResolveInfo(assemblyName).Override = base_assembly; - MelonDebug.Msg("[MonoResolveManager] Setup Successful!"); - - return true; + MelonDebug.Msg("[MelonAssemblyResolver] Setup Successful!"); } private static void ForceResolveRuntime(string fileName) @@ -53,9 +58,16 @@ private static void ForceResolveRuntime(string fileName) return; Assembly assembly = null; - try { assembly = Assembly.LoadFrom(filePath); } + try + { +#if NET6_0_OR_GREATER + assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(filePath); +#else + assembly = Assembly.LoadFrom(filePath); +#endif + } catch { assembly = null; } - + if (assembly == null) return; @@ -72,12 +84,32 @@ public static void RemoveSearchDirectory(string path) public delegate void OnAssemblyLoadHandler(Assembly assembly); public static event OnAssemblyLoadHandler OnAssemblyLoad; internal static void SafeInvoke_OnAssemblyLoad(Assembly assembly) - => OnAssemblyLoad?.Invoke(assembly); + { +#if !NET6_0_OR_GREATER + // Backwards Compatibility + MonoInternals.MonoResolveManager.SafeInvoke_OnAssemblyLoad(assembly); +#endif + OnAssemblyLoad?.Invoke(assembly); + } public delegate Assembly OnAssemblyResolveHandler(string name, Version version); public static event OnAssemblyResolveHandler OnAssemblyResolve; internal static Assembly SafeInvoke_OnAssemblyResolve(string name, Version version) - => OnAssemblyResolve?.Invoke(name, version); + { +#if NET6_0_OR_GREATER + + return OnAssemblyResolve?.Invoke(name, version); + +#else + + // Backwards Compatibility + var assembly = MonoInternals.MonoResolveManager.SafeInvoke_OnAssemblyResolve(name, version); + if (assembly == null) + assembly = OnAssemblyResolve?.Invoke(name, version); + return assembly; + +#endif + } public static AssemblyResolveInfo GetAssemblyResolveInfo(string name) => AssemblyManager.GetInfo(name); diff --git a/MelonLoader/MonoInternals/ResolveInternals/SearchDirectoryManager.cs b/MelonLoader/Resolver/SearchDirectoryManager.cs similarity index 74% rename from MelonLoader/MonoInternals/ResolveInternals/SearchDirectoryManager.cs rename to MelonLoader/Resolver/SearchDirectoryManager.cs index 1c08dbd1a..8f1ff4da9 100644 --- a/MelonLoader/MonoInternals/ResolveInternals/SearchDirectoryManager.cs +++ b/MelonLoader/Resolver/SearchDirectoryManager.cs @@ -1,11 +1,17 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; + +#if NET6_0_OR_GREATER +using System.Runtime.Loader; +#else +using System; using System.Runtime.InteropServices; +using MelonLoader.Utils; +#endif -namespace MelonLoader.MonoInternals.ResolveInternals +namespace MelonLoader.Resolver { internal static class SearchDirectoryManager { @@ -60,18 +66,28 @@ internal static Assembly Scan(string requested_name) while (enumerator.MoveNext()) { string folderpath = enumerator.Current.Path; + if (folderpath.ContainsExtension() + || !Directory.Exists(folderpath)) + continue; string filepath = Directory.GetFiles(folderpath).Where(x => - !string.IsNullOrEmpty(x) - && ((Path.GetExtension(x).ToLowerInvariant().Equals(".dll") - && Path.GetFileName(x).Equals($"{requested_name}.dll")) - || (Path.GetExtension(x).ToLowerInvariant().Equals(".exe") - && Path.GetFileName(x).Equals($"{requested_name}.exe"))) + (!string.IsNullOrEmpty(x) + && ((Path.GetExtension(x).ToLowerInvariant().Equals(".dll") + && Path.GetFileName(x).Equals($"{requested_name}.dll")) + || (Path.GetExtension(x).ToLowerInvariant().Equals(".exe") + && Path.GetFileName(x).Equals($"{requested_name}.exe")))) ).FirstOrDefault(); if (string.IsNullOrEmpty(filepath)) continue; + MelonDebug.Msg($"[MelonAssemblyResolver] Loading from {filepath}..."); + +#if NET6_0_OR_GREATER + + return AssemblyLoadContext.Default.LoadFromAssemblyPath(filepath); + +#else IntPtr filePathPtr = Marshal.StringToHGlobalAnsi(filepath); if (filePathPtr == IntPtr.Zero) continue; @@ -89,8 +105,10 @@ internal static Assembly Scan(string requested_name) continue; return MonoLibrary.CastManagedAssemblyPtr(assemblyReflectionPtr); +#endif } + MelonDebug.Msg($"[MelonAssemblyResolver] Failed to find {requested_name} in any of the known search directories"); return null; } diff --git a/MelonLoader/Utils/InteropSupport.cs b/MelonLoader/Utils/InteropSupport.cs index 2d738ad73..dd11bab9e 100644 --- a/MelonLoader/Utils/InteropSupport.cs +++ b/MelonLoader/Utils/InteropSupport.cs @@ -13,6 +13,7 @@ public interface Interface FieldInfo MethodBaseToIl2CppFieldInfo(MethodBase method); int? GetIl2CppMethodCallerCount(MethodBase method); void RegisterTypeInIl2CppDomain(Type type, bool logSuccess); + void RegisterTypeInIl2CppDomainWithInterfaces(Type type, Type[] interfaces, bool logSuccess); IntPtr CopyMethodInfoStruct(IntPtr ptr); } internal static Interface SMInterface; @@ -89,7 +90,9 @@ public static T Il2CppObjectPtrToIl2CppObject(IntPtr ptr) return SMInterface.GetIl2CppMethodCallerCount(method); } - public static void RegisterTypeInIl2CppDomain(Type type) => RegisterTypeInIl2CppDomain(type, true); + public static void RegisterTypeInIl2CppDomain(Type type) + => RegisterTypeInIl2CppDomain(type, true); + public static void RegisterTypeInIl2CppDomain(Type type, bool logSuccess) { ValidateInterface(); @@ -98,6 +101,21 @@ public static void RegisterTypeInIl2CppDomain(Type type, bool logSuccess) SMInterface.RegisterTypeInIl2CppDomain(type, logSuccess); } + public static void RegisterTypeInIl2CppDomainWithInterfaces(Type type, Type[] interfaces) + => RegisterTypeInIl2CppDomainWithInterfaces(type, interfaces, true); + + public static void RegisterTypeInIl2CppDomainWithInterfaces(Type type, Type[] interfaces, bool logSuccess) + { + ValidateInterface(); + if (type == null) + throw new NullReferenceException("The type cannot be null."); + if (interfaces == null) + throw new NullReferenceException("The interfaces cannot be null."); + if (interfaces.Length <= 0) + throw new NullReferenceException("The interfaces cannot be empty."); + SMInterface.RegisterTypeInIl2CppDomainWithInterfaces(type, interfaces, logSuccess); + } + public static IntPtr CopyMethodInfoStruct(IntPtr ptr) { ValidateInterface(); diff --git a/MelonLoader/MonoInternals/MonoLibrary.cs b/MelonLoader/Utils/MonoLibrary.cs similarity index 95% rename from MelonLoader/MonoInternals/MonoLibrary.cs rename to MelonLoader/Utils/MonoLibrary.cs index 04107b73e..a6f4ba7cd 100644 --- a/MelonLoader/MonoInternals/MonoLibrary.cs +++ b/MelonLoader/Utils/MonoLibrary.cs @@ -1,9 +1,11 @@ -using System; +#if !NET6_0_OR_GREATER + +using System; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace MelonLoader.MonoInternals +namespace MelonLoader.Utils { public class MonoLibrary { @@ -50,3 +52,4 @@ internal static bool Setup() public dmono_assembly_get_object mono_assembly_get_object = null; } } +#endif \ No newline at end of file diff --git a/README.md b/README.md index f71e5b066..76636ac80 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,10 @@ MelonLoader is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/LavaGang/MelonLoader/blob/master/LICENSE.md) for the full License. Third-party Libraries used as Source Code and/or bundled in Binary Form: -- [Research Detours Package](https://github.com/microsoft/Detours) is licensed under the MIT License. See [LICENSE](https://github.com/LavaGang/MelonLoader/blob/master/Bootstrap/Base/MSDetours/LICENSE.md) for the full License. +- [Dobby](https://github.com/jmpews/Dobby) is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/jmpews/Dobby/blob/master/LICENSE) for the full License. +- [dobby-rs](https://github.com/RinLovesYou/dobby-rs) is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/RinLovesYou/dobby-rs/blob/master/LICENSE) for the full License. +- [dobby-sys](https://github.com/RinLovesYou/dobby-sys) is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/RinLovesYou/dobby-sys/blob/master/LICENSE) for the full License. +- [unity-rs](https://github.com/RinLovesYou/unity-rs) is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/RinLovesYou/unity-rs/blob/master/LICENSE.txt) for the full License. - [Mono](https://github.com/Unity-Technologies/mono) is licensed under multiple licenses. See [LICENSE](https://github.com/Unity-Technologies/mono/blob/unity-master/LICENSE) for full details. - [HarmonyX](https://github.com/BepInEx/HarmonyX) is licensed under the MIT License. See [LICENSE](https://github.com/BepInEx/HarmonyX/blob/master/LICENSE) for the full License. - [MonoMod](https://github.com/MonoMod/MonoMod) is licensed under the MIT License. See [LICENSE](https://github.com/MonoMod/MonoMod/blob/master/LICENSE) for the full License. diff --git a/Rust/Bootstrap/src/base_assembly/mono.rs b/Rust/Bootstrap/src/base_assembly/mono.rs index 8923d78a5..1db090d48 100644 --- a/Rust/Bootstrap/src/base_assembly/mono.rs +++ b/Rust/Bootstrap/src/base_assembly/mono.rs @@ -42,7 +42,7 @@ pub fn init(runtime: &FerrexRuntime) -> Result<(), DynErr> { //get the AssemblyManager class and grab some methods from it let assemblymanager_class = melonloader_assembly.get_class( - "MelonLoader.MonoInternals.ResolveInternals", + "MelonLoader.Resolver", "AssemblyManager", runtime, )?; diff --git a/Rust/Bootstrap/src/constants.rs b/Rust/Bootstrap/src/constants.rs index ced7a3f1c..859912f66 100644 --- a/Rust/Bootstrap/src/constants.rs +++ b/Rust/Bootstrap/src/constants.rs @@ -18,7 +18,7 @@ pub type InvokeFnIl2Cpp = extern "C" fn( pub type InitFnMono = extern "C" fn(*const c_char, *const c_char) -> *mut MonoDomain; pub type InitFnIl2Cpp = extern "C" fn(*const c_char) -> *mut Il2CppDomain; -pub const MELON_VERSION: &str = "0.6.5"; +pub const MELON_VERSION: &str = "0.6.6"; pub const IS_ALPHA: bool = false; diff --git a/Rust/Bootstrap/src/icalls/mod.rs b/Rust/Bootstrap/src/icalls/mod.rs index 9d58ef91d..70383934c 100644 --- a/Rust/Bootstrap/src/icalls/mod.rs +++ b/Rust/Bootstrap/src/icalls/mod.rs @@ -17,10 +17,10 @@ pub fn init(runtime: &FerrexRuntime) -> Result<(), DynErr> { runtime.add_internal_call("MelonLoader.BootstrapInterop::NativeLogConsole", logger::log_console_interop as MethodPointer)?; runtime.add_internal_call("MelonLoader.BootstrapInterop::NativeGetJavaVM", core_android::get_raw_java_vm as MethodPointer)?; runtime.add_internal_call("MelonLoader.BootstrapInterop::NativeGetPackageName", paths::get_package_name_raw as MethodPointer)?; - runtime.add_internal_call("MelonLoader.MonoInternals.MonoLibrary::GetLibPtr", mono_library::get_lib_ptr as MethodPointer)?; - runtime.add_internal_call("MelonLoader.MonoInternals.MonoLibrary::CastManagedAssemblyPtr", mono_library::cast_assembly_ptr as MethodPointer)?; - runtime.add_internal_call("MelonLoader.MonoInternals.MonoLibrary::GetRootDomainPtr", mono_library::get_domain_ptr as MethodPointer)?; - runtime.add_internal_call("MelonLoader.MonoInternals.ResolveInternals.AssemblyManager::InstallHooks", resolve_internals::install_hooks as MethodPointer)?; + runtime.add_internal_call("MelonLoader.Utils.MonoLibrary::GetLibPtr", mono_library::get_lib_ptr as MethodPointer)?; + runtime.add_internal_call("MelonLoader.Utils.MonoLibrary::CastManagedAssemblyPtr", mono_library::cast_assembly_ptr as MethodPointer)?; + runtime.add_internal_call("MelonLoader.Utils.MonoLibrary::GetRootDomainPtr", mono_library::get_domain_ptr as MethodPointer)?; + runtime.add_internal_call("MelonLoader.Resolver.AssemblyManager::InstallHooks", resolve_internals::install_hooks as MethodPointer)?; runtime.add_internal_call("MelonLoader.Support.Preload::GetManagedDirectory", preload::get_managed_dir as MethodPointer)?; Ok(())