diff --git a/BepInEx.Preloader/Entrypoint.cs b/BepInEx.Preloader/Entrypoint.cs index 0da6fb5..033732c 100644 --- a/BepInEx.Preloader/Entrypoint.cs +++ b/BepInEx.Preloader/Entrypoint.cs @@ -9,8 +9,9 @@ namespace BepInEx.Preloader { public static void PreloaderMain(string[] args) { - Paths.SetExecutablePath(args[0]); - Paths.SetManagedPath(Environment.GetEnvironmentVariable("DOORSTOP_MANAGED_FOLDER_DIR")); + Paths.SetExecutablePath(args[0], + Path.GetDirectoryName(Path.GetDirectoryName(Path.GetFullPath(Environment.GetEnvironmentVariable("DOORSTOP_INVOKE_DLL_PATH")))), + Environment.GetEnvironmentVariable("DOORSTOP_MANAGED_FOLDER_DIR")); AppDomain.CurrentDomain.AssemblyResolve += LocalResolve; Preloader.Run(); diff --git a/BepInEx.Preloader/Patching/AssemblyPatcher.cs b/BepInEx.Preloader/Patching/AssemblyPatcher.cs index ade13d0..332a004 100644 --- a/BepInEx.Preloader/Patching/AssemblyPatcher.cs +++ b/BepInEx.Preloader/Patching/AssemblyPatcher.cs @@ -41,20 +41,50 @@ namespace BepInEx.Preloader.Patching private static T CreateDelegate(MethodInfo method) where T : class => method != null ? Delegate.CreateDelegate(typeof(T), method) as T : null; + private static PatcherPlugin ToPatcherPlugin(TypeDefinition type) + { + if (type.IsInterface || type.IsAbstract && !type.IsSealed) + return null; + + var targetDlls = type.Methods.FirstOrDefault(m => m.Name.Equals("get_TargetDLLs", StringComparison.InvariantCultureIgnoreCase) && + m.IsPublic && + m.IsStatic); + + if (targetDlls == null || + targetDlls.ReturnType.FullName != "System.Collections.Generic.IEnumerable`1") + return null; + + var patch = type.Methods.FirstOrDefault(m => m.Name.Equals("Patch") && + m.IsPublic && + m.IsStatic && + m.ReturnType.FullName == "System.Void" && + m.Parameters.Count == 1 && + (m.Parameters[0].ParameterType.FullName == "Mono.Cecil.AssemblyDefinition&" || + m.Parameters[0].ParameterType.FullName == "Mono.Cecil.AssemblyDefinition")); + + if (patch == null) + return null; + + return new PatcherPlugin + { + Type = type, + Name = type.FullName + }; + } + /// - /// Adds all patchers from all managed assemblies specified in a directory. - /// - /// Directory to search patcher DLLs from. - /// A function that locates assembly patchers in a given managed assembly. - public static void AddPatchersFromDirectory(string directory, - Func patcherLocator) + /// Adds all patchers from all managed assemblies specified in a directory. + /// + /// Directory to search patcher DLLs from. + /// A function that locates assembly patchers in a given managed assembly. + public static void AddPatchersFromDirectory(string directory) { if (!Directory.Exists(directory)) return; var sortedPatchers = new SortedDictionary(); - var patchers = TypeLoader.FindPluginTypes(directory, patcherLocator); + var patchers = TypeLoader.FindPluginTypes(directory, ToPatcherPlugin); foreach (var keyValuePair in patchers) { @@ -83,7 +113,7 @@ namespace BepInEx.Preloader.Patching var patcher = methods.FirstOrDefault(m => m.Name.Equals("Patch", StringComparison.CurrentCultureIgnoreCase) && m.ReturnType == typeof(void) && - m.GetParameters().Length == 0 && + m.GetParameters().Length == 1 && (m.GetParameters()[0].ParameterType == typeof(AssemblyDefinition) || m.GetParameters()[0].ParameterType == typeof(AssemblyDefinition).MakeByRefType())); @@ -209,7 +239,7 @@ namespace BepInEx.Preloader.Patching if (ConfigBreakBeforeLoadAssemblies.Value) { - Logger.LogInfo($"BepInEx is about load the following assemblies:\n{string.Join("\n", patchedAssemblies.ToArray())}"); + Logger.LogInfo($"BepInEx is about load the following assemblies:\n{String.Join("\n", patchedAssemblies.ToArray())}"); Logger.LogInfo($"The assemblies were dumped into {DumpedAssembliesPath}"); Logger.LogInfo("Load any assemblies into the debugger, set breakpoints and continue execution."); Debugger.Break(); diff --git a/BepInEx.Preloader/Preloader.cs b/BepInEx.Preloader/Preloader.cs index 69a50c5..407ef8d 100644 --- a/BepInEx.Preloader/Preloader.cs +++ b/BepInEx.Preloader/Preloader.cs @@ -26,6 +26,20 @@ namespace BepInEx.Preloader /// private static PreloaderConsoleListener PreloaderLog { get; set; } + public static bool IsPostUnity2017 { get; } = File.Exists(Path.Combine(Paths.ManagedPath, "UnityEngine.CoreModule.dll")); + + public static bool IsDotNet46 { get; } = string.Compare("4.0.30319.42000", Environment.Version.ToString(), StringComparison.Ordinal) <= 0; + + static Preloader() + { + ConfigEntrypointAssembly = ConfigFile.CoreConfig.Wrap( + "Preloader.Entrypoint", + "Assembly", + "The local filename of the assembly to target.", + IsPostUnity2017 ? "UnityEngine.CoreModule.dll" : "UnityEngine.dll" + ); + } + public static void Run() { try @@ -57,13 +71,8 @@ namespace BepInEx.Preloader Logger.LogMessage(attribute.Info); } -#if UNITY_2018 - Logger.LogMessage("Compiled in Unity v2018 mode"); -#else - Logger.LogMessage("Compiled in Legacy Unity mode"); -#endif - - Logger.LogInfo($"Running under Unity v{Process.GetCurrentProcess().MainModule.FileVersionInfo.FileVersion}"); + Logger.LogInfo($"Running under Unity v{FileVersionInfo.GetVersionInfo(Paths.ExecutablePath).FileVersion}"); + Logger.LogInfo($"CLR runtime version: {Environment.Version}"); Logger.LogMessage("Preloader started"); @@ -75,7 +84,7 @@ namespace BepInEx.Preloader Name = "BepInEx.Chainloader" }); - AssemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath, ToPatcherPlugin); + AssemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Logger.LogInfo($"{AssemblyPatcher.PatcherPlugins.Count} patcher plugin(s) loaded"); @@ -123,36 +132,6 @@ namespace BepInEx.Preloader } } - public static PatcherPlugin ToPatcherPlugin(TypeDefinition type) - { - if (type.IsInterface || type.IsAbstract) - return null; - - var targetDlls = type.Methods.FirstOrDefault(m => m.Name.Equals("get_TargetDLLs", StringComparison.InvariantCultureIgnoreCase) && - m.IsPublic && - m.IsStatic); - - if (targetDlls == null || - targetDlls.ReturnType.FullName != "System.Collections.Generic.IEnumerable`1") - return null; - - var patch = type.Methods.FirstOrDefault(m => m.Name.Equals("Patch") && - m.IsPublic && - m.IsStatic && - m.ReturnType.FullName == "System.Void" && - m.Parameters.Count == 1 && - (m.Parameters[0].ParameterType.FullName == "Mono.Cecil.AssemblyDefinition&" || - m.Parameters[0].ParameterType.FullName == "Mono.Cecil.AssemblyDefinition")); - - if (patch == null) - return null; - - return new PatcherPlugin - { - Type = type - }; - } - /// /// Inserts BepInEx's own chainloader entrypoint into UnityEngine. /// @@ -223,7 +202,7 @@ namespace BepInEx.Preloader il.InsertBefore(ins, il.Create(OpCodes.Ldc_I4_0)); //startConsole (always false, we already load the console in Preloader) il.InsertBefore(ins, - il.Create(OpCodes.Call, initMethod)); //Chainloader.Initialize(string containerExePath, bool startConsole = true) + il.Create(OpCodes.Call, initMethod)); // Chainloader.Initialize(string containerExePath, string managedPath = null, bool startConsole = true) il.InsertBefore(ins, il.Create(OpCodes.Call, startMethod)); } @@ -259,16 +238,7 @@ namespace BepInEx.Preloader #region Config - private static readonly ConfigWrapper ConfigEntrypointAssembly = ConfigFile.CoreConfig.Wrap( - "Preloader.Entrypoint", - "Assembly", - "The local filename of the assembly to target.", -#if UNITY_2018 - "UnityEngine.CoreModule.dll" -#else - "UnityEngine.dll" -#endif - ); + private static readonly ConfigWrapper ConfigEntrypointAssembly; private static readonly ConfigWrapper ConfigEntrypointType = ConfigFile.CoreConfig.Wrap( "Preloader.Entrypoint", diff --git a/BepInEx.Preloader/RuntimeFixes/UnityPatches.cs b/BepInEx.Preloader/RuntimeFixes/UnityPatches.cs index a6a8c4d..0332461 100644 --- a/BepInEx.Preloader/RuntimeFixes/UnityPatches.cs +++ b/BepInEx.Preloader/RuntimeFixes/UnityPatches.cs @@ -40,17 +40,13 @@ namespace BepInEx.Preloader.RuntimeFixes __result = $"file://{location.Replace('\\', '/')}"; } -#if UNITY_2018 -/* - * DESC: Workaround for Trace class not working because of missing .config file - * AFFECTS: Unity 2018+ (not .NET Standard / MonoBleedingEdge runtimes) - */ [HarmonyPostfix, HarmonyPatch(typeof(AppDomain), nameof(AppDomain.SetupInformation), MethodType.Getter)] public static void GetExeConfigName(AppDomainSetup __result) { + if (!Preloader.IsDotNet46) + return; __result.ApplicationBase = $"file://{Paths.GameRootPath}"; __result.ConfigurationFile = "app.config"; } -#endif } } \ No newline at end of file diff --git a/BepInEx/Bootstrap/Chainloader.cs b/BepInEx/Bootstrap/Chainloader.cs index c434633..cbaf343 100644 --- a/BepInEx/Bootstrap/Chainloader.cs +++ b/BepInEx/Bootstrap/Chainloader.cs @@ -39,15 +39,13 @@ namespace BepInEx.Bootstrap /// /// Initializes BepInEx to be able to start the chainloader. /// - public static void Initialize(string containerExePath, string managedPath = null, bool startConsole = true) + public static void Initialize(string containerExePath, bool startConsole = true) { if (_initialized) return; //Set vitals - Paths.SetExecutablePath(containerExePath); - Paths.SetManagedPath(managedPath); - Paths.SetPluginPath(ConfigPluginsDirectory.Value); + Paths.SetExecutablePath(containerExePath, pluginPath: ConfigPluginsDirectory.Value); //Start logging if (ConsoleWindow.ConfigConsoleEnabled.Value && startConsole) diff --git a/BepInEx/Paths.cs b/BepInEx/Paths.cs index ff8c150..dfce4cd 100644 --- a/BepInEx/Paths.cs +++ b/BepInEx/Paths.cs @@ -8,33 +8,29 @@ namespace BepInEx /// public static class Paths { - internal static void SetExecutablePath(string executablePath) + internal static void SetExecutablePath(string executablePath, string bepinRootPath = null, string managedPath = null, string pluginPath = null) { ExecutablePath = executablePath; ProcessName = Path.GetFileNameWithoutExtension(executablePath); GameRootPath = Path.GetDirectoryName(executablePath); - ManagedPath = Utility.CombinePaths(GameRootPath, $"{ProcessName}_Data", "Managed"); - BepInExRootPath = Path.Combine(GameRootPath, "BepInEx"); + + if (ManagedPath == null || managedPath != null) + ManagedPath = managedPath ?? Utility.CombinePaths(GameRootPath, $"{ProcessName}_Data", "Managed"); + + if (BepInExRootPath == null || bepinRootPath != null) + BepInExRootPath = bepinRootPath ?? Path.Combine(GameRootPath, "BepInEx"); + ConfigPath = Path.Combine(BepInExRootPath, "config"); BepInExConfigPath = Path.Combine(ConfigPath, "BepInEx.cfg"); - PluginPath = Path.Combine(BepInExRootPath, "plugins"); + + if (PluginPath == null || pluginPath != null) + PluginPath = pluginPath ?? Path.Combine(BepInExRootPath, "plugins"); + PatcherPluginPath = Path.Combine(BepInExRootPath, "patchers"); BepInExAssemblyDirectory = Path.Combine(BepInExRootPath, "core"); BepInExAssemblyPath = Path.Combine(BepInExAssemblyDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.dll"); } - internal static void SetManagedPath(string managedPath) - { - if (managedPath == null) - return; - ManagedPath = managedPath; - } - - internal static void SetPluginPath(string pluginPath) - { - PluginPath = Utility.CombinePaths(BepInExRootPath, pluginPath); - } - /// /// The directory that the core BepInEx DLLs reside in. ///