Browse Source

Implement custom entry point

Bepis 6 years ago
parent
commit
13a0a860a3
3 changed files with 79 additions and 65 deletions
  1. 11 0
      BepInEx.Common/Utility.cs
  2. 14 12
      BepInEx/Bootstrap/Chainloader.cs
  3. 54 53
      BepInEx/Bootstrap/Preloader.cs

+ 11 - 0
BepInEx.Common/Utility.cs

@@ -29,6 +29,17 @@ namespace BepInEx.Common
         /// <returns>A combined path.</returns>
         public static string CombinePaths(params string[] parts) => parts.Aggregate(Path.Combine);
 
+		/// <summary>
+		/// Tries to parse a bool, with a default value if unable to parse.
+		/// </summary>
+		/// <param name="input">The string to parse</param>
+		/// <param name="defaultValue">The value to return if parsing is unsuccessful.</param>
+		/// <returns>Boolean value of input if able to be parsed, otherwise default value.</returns>
+	    public static bool SafeParseBool(string input, bool defaultValue = false)
+	    {
+		    return bool.TryParse(input, out bool result) ? result : defaultValue;
+	    }
+
         /// <summary>
         /// Converts a file path into a UnityEngine.WWW format.
         /// </summary>

+ 14 - 12
BepInEx/Bootstrap/Chainloader.cs

@@ -45,19 +45,20 @@ namespace BepInEx.Bootstrap
 
 			try
 			{
-                UnityLogWriter unityLogWriter = new UnityLogWriter();
+				UnityLogWriter unityLogWriter = new UnityLogWriter();
 
-			    if (Preloader.PreloaderLog != null)
-			        unityLogWriter.WriteToLog($"{Preloader.PreloaderLog}\r\n");
+				if (Preloader.PreloaderLog != null)
+					unityLogWriter.WriteToLog($"{Preloader.PreloaderLog}\r\n");
 
-                Logger.SetLogger(unityLogWriter);
+				Logger.SetLogger(unityLogWriter);
 
-                if(bool.Parse(Config.GetEntry("log_unity_messages", "false", "Global")))
-                    UnityLogWriter.ListenUnityLogs();
+				if (bool.Parse(Config.GetEntry("log-unity-messages", "false", "Global")))
+					UnityLogWriter.ListenUnityLogs();
+				
+				var productNameProp = typeof(Application).GetProperty("productName", BindingFlags.Public | BindingFlags.Static);
+				if (productNameProp != null)
+					ConsoleWindow.Title = $"BepInEx {Assembly.GetExecutingAssembly().GetName().Version} - {productNameProp.GetValue(null, null)}";
 
-			    string consoleTile = $"BepInEx {Assembly.GetExecutingAssembly().GetName().Version} - {Application.productName}";
-			    ConsoleWindow.Title = consoleTile;
-                
 				Logger.Log(LogLevel.Message, "Chainloader started");
 
 				UnityEngine.Object.DontDestroyOnLoad(ManagerObject);
@@ -82,6 +83,7 @@ namespace BepInEx.Bootstrap
 
 				Dictionary<Type, IEnumerable<Type>> dependencyDict = new Dictionary<Type, IEnumerable<Type>>();
 
+
 				foreach (Type t in pluginTypes)
 				{
 					try
@@ -105,8 +107,8 @@ namespace BepInEx.Bootstrap
 					try
 					{
 						var metadata = MetadataHelper.GetMetadata(t);
-
-						var plugin = (BaseUnityPlugin) ManagerObject.AddComponent(t);
+						
+						var plugin = (BaseUnityPlugin)ManagerObject.AddComponent(t);
 
 						Plugins.Add(plugin);
 					    Logger.Log(LogLevel.Info, $"Loaded [{metadata.Name} {metadata.Version}]");
@@ -119,7 +121,7 @@ namespace BepInEx.Bootstrap
 			}
 			catch (Exception ex)
 			{
-				UnityInjector.ConsoleUtil.ConsoleWindow.Attach();
+				ConsoleWindow.Attach();
 
 				Console.WriteLine("Error occurred starting the game");
 				Console.WriteLine(ex.ToString());

+ 54 - 53
BepInEx/Bootstrap/Preloader.cs

@@ -5,6 +5,7 @@ using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Text;
+using BepInEx.Common;
 using BepInEx.Logging;
 using Mono.Cecil;
 using Mono.Cecil.Cil;
@@ -45,8 +46,8 @@ namespace BepInEx.Bootstrap
             try
             {
                 AllocateConsole();
-
-                PreloaderLog = new PreloaderLogWriter(SafeGetConfigBool("preloader-logconsole", "false"));
+				
+                PreloaderLog = new PreloaderLogWriter(Utility.SafeParseBool(Config.GetEntry("preloader-logconsole", "false", "BepInEx")));
                 PreloaderLog.Enabled = true;
 
                 string consoleTile =
@@ -58,7 +59,9 @@ namespace BepInEx.Bootstrap
                 PreloaderLog.WriteLine(consoleTile);
                 Logger.Log(LogLevel.Message, "Preloader started");
 
-                AddPatcher(new[] {"UnityEngine.dll"}, PatchEntrypoint);
+	            string entrypointAssembly = Config.GetEntry("entrypoint-assembly", "UnityEngine.dll", "Preloader");
+
+	            AddPatcher(new[] {entrypointAssembly}, PatchEntrypoint);
 
                 if (Directory.Exists(Paths.PatcherPluginPath))
                 {
@@ -196,33 +199,55 @@ namespace BepInEx.Bootstrap
         /// <param name="assembly">The assembly that will be attempted to be patched.</param>
         public static void PatchEntrypoint(ref AssemblyDefinition assembly)
         {
-            if (assembly.Name.Name == "UnityEngine")
-            {
+			string entrypointType = Config.GetEntry("entrypoint-type", "Application", "Preloader");
+			string entrypointMethod = Config.HasEntry("entrypoint-method") ? Config.GetEntry("entrypoint-method", section: "Preloader") : "";
+	        bool isCctor = entrypointMethod.IsNullOrWhiteSpace() || entrypointMethod == ".cctor";
+			
 #if CECIL_10
-                using (var injected = AssemblyDefinition.ReadAssembly(Paths.BepInExAssemblyPath))
+			using (var injected = AssemblyDefinition.ReadAssembly(Paths.BepInExAssemblyPath))
 #elif CECIL_9
-                AssemblyDefinition injected = AssemblyDefinition.ReadAssembly(BepInExAssemblyPath);
+            AssemblyDefinition injected = AssemblyDefinition.ReadAssembly(BepInExAssemblyPath);
 #endif
-                {
-                    var originalInjectMethod = injected.MainModule.Types.First(x => x.Name == "Chainloader").Methods
-                                                       .First(x => x.Name == "Initialize");
-
-                    var injectMethod = assembly.MainModule.ImportReference(originalInjectMethod);
-
-                    var sceneManager = assembly.MainModule.Types.First(x => x.Name == "Application");
-
-                    var voidType = assembly.MainModule.ImportReference(typeof(void));
-                    var cctor = new MethodDefinition(".cctor",
-                                                     MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig
-                                                     | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
-                                                     voidType);
-
-                    var ilp = cctor.Body.GetILProcessor();
-                    ilp.Append(ilp.Create(OpCodes.Call, injectMethod));
-                    ilp.Append(ilp.Create(OpCodes.Ret));
-
-                    sceneManager.Methods.Add(cctor);
-                }
+            {
+                var originalInjectMethod = injected.MainModule.Types.First(x => x.Name == "Chainloader").Methods
+                                                    .First(x => x.Name == "Initialize");
+
+                var injectMethod = assembly.MainModule.ImportReference(originalInjectMethod);
+
+
+                var entryType = assembly.MainModule.Types.First(x => x.Name == entrypointType);
+				
+
+	            if (isCctor)
+	            {
+		            MethodDefinition cctor = entryType.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic);
+		            ILProcessor il;
+
+		            if (cctor == null)
+		            {
+			            cctor = new MethodDefinition(".cctor",
+				            MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig
+				            | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
+				            assembly.MainModule.ImportReference(typeof(void)));
+
+			            entryType.Methods.Add(cctor);
+			            il = cctor.Body.GetILProcessor();
+			            il.Append(il.Create(OpCodes.Ret));
+		            }
+
+		            Instruction ins = cctor.Body.Instructions.First();
+		            il = cctor.Body.GetILProcessor();
+		            il.InsertBefore(ins, il.Create(OpCodes.Call, injectMethod));
+	            }
+	            else
+	            {
+		            foreach (var loadScene in entryType.Methods.Where(x => x.Name == entrypointMethod))
+		            {
+			            var il = loadScene.Body.GetILProcessor();
+
+			            il.InsertBefore(loadScene.Body.Instructions[0], il.Create(OpCodes.Call, injectMethod));
+		            }
+	            }
             }
         }
 
@@ -231,8 +256,8 @@ namespace BepInEx.Bootstrap
         /// </summary>
         public static void AllocateConsole()
         {
-            bool console = SafeGetConfigBool("console", "false");
-            bool shiftjis = SafeGetConfigBool("console-shiftjis", "false");
+            bool console = Utility.SafeParseBool(Config.GetEntry("console", "false", "BepInEx"));
+            bool shiftjis = Utility.SafeParseBool(Config.GetEntry("console-shiftjis", "false", "BepInEx"));
 
             if (console)
                 try
@@ -263,29 +288,5 @@ namespace BepInEx.Bootstrap
         {
             PatcherDictionary[patcher] = dllNames;
         }
-
-
-        /// <summary>
-        ///     Safely retrieves a boolean value from the config. Returns false if not able to retrieve safely.
-        /// </summary>
-        /// <param name="key">The key to retrieve from the config.</param>
-        /// <param name="defaultValue">The default value to both return and set if the key does not exist in the config.</param>
-        /// <returns>
-        ///     The value of the key if found in the config, or the default value specified if not found, or false if it was
-        ///     unable to safely retrieve the value from the config.
-        /// </returns>
-        private static bool SafeGetConfigBool(string key, string defaultValue)
-        {
-            try
-            {
-                string result = Config.GetEntry(key, defaultValue);
-
-                return bool.Parse(result);
-            }
-            catch
-            {
-                return false;
-            }
-        }
     }
 }