Browse Source

Fix whitespace issues

Bepis 5 years ago
parent
commit
becdbed829
32 changed files with 2424 additions and 2436 deletions
  1. 22 22
      BepInEx.Bootstrap/Entrypoint.cs
  2. 1 1
      BepInEx.Bootstrap/Properties/AssemblyInfo.cs
  3. 14 14
      BepInEx.Patcher/EmbeddedResource.cs
  4. 176 176
      BepInEx.Patcher/Program.cs
  5. 1 1
      BepInEx.Patcher/Properties/AssemblyInfo.cs
  6. 1 0
      BepInEx.Patcher/packages.config
  7. 41 41
      BepInEx.Preloader/Entrypoint.cs
  8. 166 170
      BepInEx.Preloader/Patcher/AssemblyPatcher.cs
  9. 26 26
      BepInEx.Preloader/Patcher/PatcherPlugin.cs
  10. 268 267
      BepInEx.Preloader/Preloader.cs
  11. 30 30
      BepInEx.Preloader/UnityPatches.cs
  12. 14 14
      BepInEx/Bootstrap/Chainloader.cs
  13. 63 61
      BepInEx/Bootstrap/TypeLoader.cs
  14. 191 190
      BepInEx/Config.cs
  15. 164 166
      BepInEx/ConfigWrapper.cs
  16. 54 54
      BepInEx/ConsoleUtil/ConsoleEncoding/ConsoleEncoding.Buffers.cs
  17. 37 35
      BepInEx/ConsoleUtil/ConsoleEncoding/ConsoleEncoding.PInvoke.cs
  18. 77 77
      BepInEx/ConsoleUtil/ConsoleEncoding/ConsoleEncoding.cs
  19. 125 124
      BepInEx/ConsoleUtil/ConsoleWindow.cs
  20. 138 146
      BepInEx/ConsoleUtil/Kon.cs
  21. 45 45
      BepInEx/ConsoleUtil/SafeConsole.cs
  22. 155 158
      BepInEx/Contract/Attributes.cs
  23. 4 7
      BepInEx/Contract/BaseUnityPlugin.cs
  24. 56 56
      BepInEx/Logging/BaseLogger.cs
  25. 47 47
      BepInEx/Logging/LogLevel.cs
  26. 19 19
      BepInEx/Logging/Logger.cs
  27. 126 127
      BepInEx/Logging/LoggerTraceListener.cs
  28. 90 89
      BepInEx/Logging/PreloaderLogWriter.cs
  29. 108 108
      BepInEx/Logging/UnityLogWriter.cs
  30. 54 54
      BepInEx/Paths.cs
  31. 1 1
      BepInEx/Properties/AssemblyInfo.cs
  32. 110 110
      BepInEx/Utility.cs

+ 22 - 22
BepInEx.Bootstrap/Entrypoint.cs

@@ -5,32 +5,32 @@ using System.Reflection;
 
 namespace BepInEx.Bootstrap
 {
-    public static class Entrypoint
-    {
-	    public static void Init()
-	    {
-		    AppDomain.CurrentDomain.AssemblyResolve += ResolveBepInEx;
+	public static class Entrypoint
+	{
+		public static void Init()
+		{
+			AppDomain.CurrentDomain.AssemblyResolve += ResolveBepInEx;
 
 			Linker.StartBepInEx();
-	    }
+		}
 
-	    private static readonly string LocalDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
+		private static readonly string LocalDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
 
-	    private static Assembly ResolveBepInEx(object sender, ResolveEventArgs args)
-	    {
-		    string path = Path.Combine(LocalDirectory, $@"BepInEx\core\{new AssemblyName(args.Name).Name}.dll");
+		private static Assembly ResolveBepInEx(object sender, ResolveEventArgs args)
+		{
+			string path = Path.Combine(LocalDirectory, $@"BepInEx\core\{new AssemblyName(args.Name).Name}.dll");
 
-		    if (!File.Exists(path))
-			    return null;
+			if (!File.Exists(path))
+				return null;
 
-		    try
-		    {
-			    return Assembly.LoadFile(path);
-		    }
-		    catch (Exception)
-		    {
-			    return null;
-		    }
-	    }
-    }
+			try
+			{
+				return Assembly.LoadFile(path);
+			}
+			catch (Exception)
+			{
+				return null;
+			}
+		}
+	}
 }

+ 1 - 1
BepInEx.Bootstrap/Properties/AssemblyInfo.cs

@@ -33,4 +33,4 @@ using System.Runtime.InteropServices;
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
 [assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 14 - 14
BepInEx.Patcher/EmbeddedResource.cs

@@ -3,20 +3,20 @@ using System.Reflection;
 
 namespace BepInEx.Patcher
 {
-    internal static class EmbeddedResource
-    {
-        public static byte[] Get(string resourceName)
-        {
-            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
-            {
-                int length = (int)stream.Length;
+	internal static class EmbeddedResource
+	{
+		public static byte[] Get(string resourceName)
+		{
+			using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
+			{
+				int length = (int)stream.Length;
 
-                byte[] buffer = new byte[length];
+				byte[] buffer = new byte[length];
 
-                stream.Read(buffer, 0, length);
+				stream.Read(buffer, 0, length);
 
-                return buffer;
-            }
-        }
-    }
-}
+				return buffer;
+			}
+		}
+	}
+}

+ 176 - 176
BepInEx.Patcher/Program.cs

@@ -8,40 +8,40 @@ using MethodAttributes = Mono.Cecil.MethodAttributes;
 
 namespace BepInEx.Patcher
 {
-    class Program
-    {
-        static void WriteError(string message)
-        {
-            Console.ForegroundColor = ConsoleColor.Red;
-            Console.WriteLine("Failed");
-            Console.ResetColor();
-            Console.WriteLine(message);
-            Console.WriteLine();
-        }
-
-        static void WriteSuccess()
-        {
-            Console.ForegroundColor = ConsoleColor.Green;
-            Console.WriteLine("Success");
-            Console.ResetColor();
-        }
-
-        static void Main(string[] args)
-        {
-            Console.WriteLine($"BepInEx Patcher v{Assembly.GetExecutingAssembly().GetName().Version}");
-
-            if (args.Length >= 1) //short circuit for specific dll patch
-                Environment.Exit(PatchUnityExe(Path.GetDirectoryName(args[0]), args[0], out string message) ? 0 : 9999);
-
-            bool hasFound = false;
-            bool hasFailure = false;
-            int patchCount = 0;
-
-            foreach (string exePath in Directory.GetFiles(Directory.GetCurrentDirectory()))
-            {
-                string gameName = Path.GetFileNameWithoutExtension(exePath);
-
-                string managedDir = Environment.CurrentDirectory + $@"\{gameName}_Data\Managed";
+	class Program
+	{
+		static void WriteError(string message)
+		{
+			Console.ForegroundColor = ConsoleColor.Red;
+			Console.WriteLine("Failed");
+			Console.ResetColor();
+			Console.WriteLine(message);
+			Console.WriteLine();
+		}
+
+		static void WriteSuccess()
+		{
+			Console.ForegroundColor = ConsoleColor.Green;
+			Console.WriteLine("Success");
+			Console.ResetColor();
+		}
+
+		static void Main(string[] args)
+		{
+			Console.WriteLine($"BepInEx Patcher v{Assembly.GetExecutingAssembly().GetName().Version}");
+
+			if (args.Length >= 1) //short circuit for specific dll patch
+				Environment.Exit(PatchUnityExe(Path.GetDirectoryName(args[0]), args[0], out string message) ? 0 : 9999);
+
+			bool hasFound = false;
+			bool hasFailure = false;
+			int patchCount = 0;
+
+			foreach (string exePath in Directory.GetFiles(Directory.GetCurrentDirectory()))
+			{
+				string gameName = Path.GetFileNameWithoutExtension(exePath);
+
+				string managedDir = Environment.CurrentDirectory + $@"\{gameName}_Data\Managed";
 
 #if UNITY_2018
 				string unityOutputDLL = Path.GetFullPath($"{managedDir}\\UnityEngine.CoreModule.dll");
@@ -51,55 +51,55 @@ namespace BepInEx.Patcher
 
 
 				if (!Directory.Exists(managedDir) || !File.Exists(unityOutputDLL))
-                    continue;
-
-                hasFound = true;
-
-                Console.Write($"Patching {gameName}... ");
-
-                if (PatchUnityExe(managedDir, unityOutputDLL, out string message))
-                {
-                    WriteSuccess();
-                    patchCount++;
-                }
-                else
-                {
-                    WriteError(message);
-                    hasFailure = true;
-                }
-            }
-
-            Console.WriteLine();
-
-            if (!hasFound)
-                Console.WriteLine("Didn't find any games to patch! Exiting.");
-            else
-                Console.WriteLine($"Patched {patchCount} assemblies!");
-
-            if (hasFailure)
-            {
-                Console.WriteLine("Press any key to continue...");
-                Console.ReadKey();
-            }
-            else
-                System.Threading.Thread.Sleep(3000);
-        }
-
-        static bool PatchUnityExe(string managedDir, string unityOutputDLL, out string message)
-        {
-            message = null;
-
-            try
-            {
-                string injectedDLL = Path.GetFullPath($"{managedDir}\\BepInEx.Bootstrap.dll");
-                File.WriteAllBytes(injectedDLL, EmbeddedResource.Get("BepInEx.Patcher.BepInEx.Bootstrap.dll"));
-
-                var defaultResolver = new DefaultAssemblyResolver();
-                defaultResolver.AddSearchDirectory(managedDir);
-                var rp = new ReaderParameters
-                {
-                    AssemblyResolver = defaultResolver
-                };
+					continue;
+
+				hasFound = true;
+
+				Console.Write($"Patching {gameName}... ");
+
+				if (PatchUnityExe(managedDir, unityOutputDLL, out string message))
+				{
+					WriteSuccess();
+					patchCount++;
+				}
+				else
+				{
+					WriteError(message);
+					hasFailure = true;
+				}
+			}
+
+			Console.WriteLine();
+
+			if (!hasFound)
+				Console.WriteLine("Didn't find any games to patch! Exiting.");
+			else
+				Console.WriteLine($"Patched {patchCount} assemblies!");
+
+			if (hasFailure)
+			{
+				Console.WriteLine("Press any key to continue...");
+				Console.ReadKey();
+			}
+			else
+				System.Threading.Thread.Sleep(3000);
+		}
+
+		static bool PatchUnityExe(string managedDir, string unityOutputDLL, out string message)
+		{
+			message = null;
+
+			try
+			{
+				string injectedDLL = Path.GetFullPath($"{managedDir}\\BepInEx.Bootstrap.dll");
+				File.WriteAllBytes(injectedDLL, EmbeddedResource.Get("BepInEx.Patcher.BepInEx.Bootstrap.dll"));
+
+				var defaultResolver = new DefaultAssemblyResolver();
+				defaultResolver.AddSearchDirectory(managedDir);
+				var rp = new ReaderParameters
+				{
+					AssemblyResolver = defaultResolver
+				};
 
 #if UNITY_2018
 				string unityBackupDLL = Path.GetFullPath($"{managedDir}\\UnityEngine.CoreModule.dll.bak");
@@ -110,97 +110,97 @@ namespace BepInEx.Patcher
 				//determine which assembly to use as a base
 				AssemblyDefinition unity = AssemblyDefinition.ReadAssembly(unityOutputDLL, rp);
 
-                if (!VerifyAssembly(unity, out message))
-                {
-                    //try and fall back to .bak if exists
-                    if (File.Exists(unityBackupDLL))
-                    {
-                        unity.Dispose();
-                        unity = AssemblyDefinition.ReadAssembly(unityBackupDLL, rp);
-
-                        if (!VerifyAssembly(unity, out message))
-                        {
-                            //can't use anything
-                            unity.Dispose();
-                            message += "\r\nThe backup is not usable.";
-                            return false;
-                        }
-                    }
-                    else
-                    {
-                        //can't use anything
-                        unity.Dispose();
-                        message += "\r\nNo backup exists.";
-                        return false;
-                    }
-                }
-                else
-                {
-                    //make a backup of the assembly
-                    File.Copy(unityOutputDLL, unityBackupDLL, true);
-                    unity.Dispose();
-                    unity = AssemblyDefinition.ReadAssembly(unityBackupDLL, rp);
-                }
-                
-                //patch
-                using (unity)
-                using (AssemblyDefinition injected = AssemblyDefinition.ReadAssembly(injectedDLL, rp))
-                {
-                    InjectAssembly(unity, injected);
-                    
-                    unity.Write(unityOutputDLL);
-                }
-            }
-            catch (Exception e)
-            {
-                message = e.ToString();
-                return false;
-            }
-
-            return true;
-        }
-
-        static void InjectAssembly(AssemblyDefinition unity, AssemblyDefinition injected)
-        {
-            //Entry point
-            var originalInjectMethod = injected.MainModule.Types.First(x => x.Name == "Entrypoint")
-                .Methods.First(x => x.Name == "Init");
-
-            var injectMethod = unity.MainModule.ImportReference(originalInjectMethod);
-
-            var sceneManager = unity.MainModule.Types.First(x => x.Name == "Application");
-
-            var voidType = unity.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);
-        }
-
-        static bool VerifyAssembly(AssemblyDefinition unity, out string message)
-        {
-            bool canPatch = true;
-            message = "";
-
-            //check if already patched
-            if (unity.MainModule.AssemblyReferences.Any(x => x.Name.Contains("BepInEx")))
-            {
-                canPatch = false;
-
-                message += "This assembly has already been patched by BepInEx.\r\n";
-            }
-
-	        message = message.Trim();
-            return canPatch;
-        }
-    }
+				if (!VerifyAssembly(unity, out message))
+				{
+					//try and fall back to .bak if exists
+					if (File.Exists(unityBackupDLL))
+					{
+						unity.Dispose();
+						unity = AssemblyDefinition.ReadAssembly(unityBackupDLL, rp);
+
+						if (!VerifyAssembly(unity, out message))
+						{
+							//can't use anything
+							unity.Dispose();
+							message += "\r\nThe backup is not usable.";
+							return false;
+						}
+					}
+					else
+					{
+						//can't use anything
+						unity.Dispose();
+						message += "\r\nNo backup exists.";
+						return false;
+					}
+				}
+				else
+				{
+					//make a backup of the assembly
+					File.Copy(unityOutputDLL, unityBackupDLL, true);
+					unity.Dispose();
+					unity = AssemblyDefinition.ReadAssembly(unityBackupDLL, rp);
+				}
+
+				//patch
+				using (unity)
+				using (AssemblyDefinition injected = AssemblyDefinition.ReadAssembly(injectedDLL, rp))
+				{
+					InjectAssembly(unity, injected);
+
+					unity.Write(unityOutputDLL);
+				}
+			}
+			catch (Exception e)
+			{
+				message = e.ToString();
+				return false;
+			}
+
+			return true;
+		}
+
+		static void InjectAssembly(AssemblyDefinition unity, AssemblyDefinition injected)
+		{
+			//Entry point
+			var originalInjectMethod = injected.MainModule.Types.First(x => x.Name == "Entrypoint")
+											   .Methods.First(x => x.Name == "Init");
+
+			var injectMethod = unity.MainModule.ImportReference(originalInjectMethod);
+
+			var sceneManager = unity.MainModule.Types.First(x => x.Name == "Application");
+
+			var voidType = unity.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);
+		}
+
+		static bool VerifyAssembly(AssemblyDefinition unity, out string message)
+		{
+			bool canPatch = true;
+			message = "";
+
+			//check if already patched
+			if (unity.MainModule.AssemblyReferences.Any(x => x.Name.Contains("BepInEx")))
+			{
+				canPatch = false;
+
+				message += "This assembly has already been patched by BepInEx.\r\n";
+			}
+
+			message = message.Trim();
+			return canPatch;
+		}
+	}
 }

+ 1 - 1
BepInEx.Patcher/Properties/AssemblyInfo.cs

@@ -33,4 +33,4 @@ using System.Runtime.InteropServices;
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
 [assembly: AssemblyVersion("5.0.0.0")]
-[assembly: AssemblyFileVersion("5.0.0.0")]
+[assembly: AssemblyFileVersion("5.0.0.0")]

+ 1 - 0
BepInEx.Patcher/packages.config

@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
+
 <packages>
   <package id="ILRepack" version="2.0.15" targetFramework="net35" />
   <package id="ILRepack.MSBuild.Task" version="1.0.9" targetFramework="net35" />

+ 41 - 41
BepInEx.Preloader/Entrypoint.cs

@@ -4,51 +4,51 @@ using System.Reflection;
 
 namespace BepInEx.Preloader
 {
-    internal static class Entrypoint
-    {
-        /// <summary>
-        ///     The main entrypoint of BepInEx, called from Doorstop.
-        /// </summary>
-        /// <param name="args">
-        ///     The arguments passed in from Doorstop. First argument is the path of the currently executing
-        ///     process.
-        /// </param>
-        public static void Main(string[] args)
-        {
-            // Manually set up the path for patchers to work
-            typeof(Paths).GetProperty(nameof(Paths.ExecutablePath)).SetValue(null, args[0], null);
-            //Paths.ExecutablePath = args[0];
-            AppDomain.CurrentDomain.AssemblyResolve += LocalResolve;
+	internal static class Entrypoint
+	{
+		/// <summary>
+		///     The main entrypoint of BepInEx, called from Doorstop.
+		/// </summary>
+		/// <param name="args">
+		///     The arguments passed in from Doorstop. First argument is the path of the currently executing
+		///     process.
+		/// </param>
+		public static void Main(string[] args)
+		{
+			// Manually set up the path for patchers to work
+			typeof(Paths).GetProperty(nameof(Paths.ExecutablePath)).SetValue(null, args[0], null);
+			//Paths.ExecutablePath = args[0];
+			AppDomain.CurrentDomain.AssemblyResolve += LocalResolve;
 
-            Preloader.Run();
-        }
+			Preloader.Run();
+		}
 
-        /// <summary>
-        ///     A handler for <see cref="AppDomain" />.AssemblyResolve to perform some special handling.
-        ///     <para>
-        ///         It attempts to check currently loaded assemblies (ignoring the version), and then checks the BepInEx/core path,
-        ///         BepInEx/patchers path and the BepInEx folder, all in that order.
-        ///     </para>
-        /// </summary>
-        /// <param name="sender"></param>
-        /// <param name="args"></param>
-        /// <returns></returns>
-        internal static Assembly LocalResolve(object sender, ResolveEventArgs args)
-        {
-            var assemblyName = new AssemblyName(args.Name);
+		/// <summary>
+		///     A handler for <see cref="AppDomain" />.AssemblyResolve to perform some special handling.
+		///     <para>
+		///         It attempts to check currently loaded assemblies (ignoring the version), and then checks the BepInEx/core path,
+		///         BepInEx/patchers path and the BepInEx folder, all in that order.
+		///     </para>
+		/// </summary>
+		/// <param name="sender"></param>
+		/// <param name="args"></param>
+		/// <returns></returns>
+		internal static Assembly LocalResolve(object sender, ResolveEventArgs args)
+		{
+			var assemblyName = new AssemblyName(args.Name);
 
-            var foundAssembly = AppDomain.CurrentDomain.GetAssemblies()
-                .FirstOrDefault(x => x.GetName().Name == assemblyName.Name);
+			var foundAssembly = AppDomain.CurrentDomain.GetAssemblies()
+										 .FirstOrDefault(x => x.GetName().Name == assemblyName.Name);
 
-            if (foundAssembly != null)
-                return foundAssembly;
+			if (foundAssembly != null)
+				return foundAssembly;
 
-            if (Utility.TryResolveDllAssembly(assemblyName, Paths.BepInExAssemblyDirectory, out foundAssembly)
-                || Utility.TryResolveDllAssembly(assemblyName, Paths.PatcherPluginPath, out foundAssembly)
-                || Utility.TryResolveDllAssembly(assemblyName, Paths.PluginPath, out foundAssembly))
-                return foundAssembly;
+			if (Utility.TryResolveDllAssembly(assemblyName, Paths.BepInExAssemblyDirectory, out foundAssembly)
+				|| Utility.TryResolveDllAssembly(assemblyName, Paths.PatcherPluginPath, out foundAssembly)
+				|| Utility.TryResolveDllAssembly(assemblyName, Paths.PluginPath, out foundAssembly))
+				return foundAssembly;
 
-            return null;
-        }
-    }
+			return null;
+		}
+	}
 }

+ 166 - 170
BepInEx.Preloader/Patcher/AssemblyPatcher.cs

@@ -7,174 +7,170 @@ using Mono.Cecil;
 
 namespace BepInEx.Preloader.Patcher
 {
-    /// <summary>
-    ///     Delegate used in patching assemblies.
-    /// </summary>
-    /// <param name="assembly">The assembly that is being patched.</param>
-    internal delegate void AssemblyPatcherDelegate(ref AssemblyDefinition assembly);
-
-    /// <summary>
-    ///     Worker class which is used for loading and patching entire folders of assemblies, or alternatively patching and
-    ///     loading assemblies one at a time.
-    /// </summary>
-    internal static class AssemblyPatcher
-    {
-        private static readonly List<PatcherPlugin> patchers = new List<PatcherPlugin>();
-
-        /// <summary>
-        ///     Configuration value of whether assembly dumping is enabled or not.
-        /// </summary>
-        private static bool DumpingEnabled =>
-            Utility.SafeParseBool(Config.GetEntry("dump-assemblies", "false", "Preloader"));
-
-        /// <summary>
-        ///     Adds a single assembly patcher to the pool of applicable patches.
-        /// </summary>
-        /// <param name="patcher">Patcher to apply.</param>
-        public static void AddPatcher(PatcherPlugin patcher)
-        {
-            patchers.Add(patcher);
-        }
-
-        /// <summary>
-        ///     Adds all patchers from all managed assemblies specified in a directory.
-        /// </summary>
-        /// <param name="directory">Directory to search patcher DLLs from.</param>
-        /// <param name="patcherLocator">A function that locates assembly patchers in a given managed assembly.</param>
-        public static void AddPatchersFromDirectory(string directory,
-            Func<Assembly, List<PatcherPlugin>> patcherLocator)
-        {
-            if (!Directory.Exists(directory))
-                return;
-
-            var sortedPatchers = new SortedDictionary<string, PatcherPlugin>();
-
-            foreach (string assemblyPath in Directory.GetFiles(directory, "*.dll"))
-                try
-                {
-                    var assembly = Assembly.LoadFrom(assemblyPath);
-
-                    foreach (var patcher in patcherLocator(assembly))
-                        sortedPatchers.Add(patcher.Name, patcher);
-                }
-                catch (BadImageFormatException)
-                {
-                } //unmanaged DLL
-                catch (ReflectionTypeLoadException)
-                {
-                } //invalid references
-
-            foreach (KeyValuePair<string, PatcherPlugin> patcher in sortedPatchers)
-                AddPatcher(patcher.Value);
-        }
-
-        private static void InitializePatchers()
-        {
-            foreach (var assemblyPatcher in patchers)
-                assemblyPatcher.Initializer?.Invoke();
-        }
-
-        private static void FinalizePatching()
-        {
-            foreach (var assemblyPatcher in patchers)
-                assemblyPatcher.Finalizer?.Invoke();
-        }
-
-        /// <summary>
-        ///     Releases all patchers to let them be collected by GC.
-        /// </summary>
-        public static void DisposePatchers()
-        {
-            patchers.Clear();
-        }
-
-        /// <summary>
-        ///     Applies patchers to all assemblies in the given directory and loads patched assemblies into memory.
-        /// </summary>
-        /// <param name="directory">Directory to load CLR assemblies from.</param>
-        public static void PatchAndLoad(string directory)
-        {
-            // First, load patchable assemblies into Cecil
-            var assemblies = new Dictionary<string, AssemblyDefinition>();
-
-            foreach (string assemblyPath in Directory.GetFiles(directory, "*.dll"))
-            {
-                var assembly = AssemblyDefinition.ReadAssembly(assemblyPath);
-
-                //NOTE: this is special cased here because the dependency handling for System.dll is a bit wonky
-                //System has an assembly reference to itself, and it also has a reference to Mono.Security causing a circular dependency
-                //It's also generally dangerous to change system.dll since so many things rely on it, 
-                // and it's already loaded into the appdomain since this loader references it, so we might as well skip it
-                if (assembly.Name.Name == "System"
-                    || assembly.Name.Name == "mscorlib"
-                ) //mscorlib is already loaded into the appdomain so it can't be patched
-                {
-                    assembly.Dispose();
-                    continue;
-                }
-
-                if (UnityPatches.AssemblyLocations.ContainsKey(assembly.FullName))
-                {
-                    Logger.Log(LogLevel.Warning,
-                        $"Tried to load duplicate assembly {Path.GetFileName(assemblyPath)} from Managed folder! Skipping...");
-                    continue;
-                }
-
-                assemblies.Add(Path.GetFileName(assemblyPath), assembly);
-                UnityPatches.AssemblyLocations.Add(assembly.FullName, Path.GetFullPath(assemblyPath));
-            }
-
-            // Next, initialize all the patchers
-            InitializePatchers();
-
-            // Then, perform the actual patching
-            var patchedAssemblies = new HashSet<string>();
-            foreach (var assemblyPatcher in patchers)
-            foreach (string targetDll in assemblyPatcher.TargetDLLs)
-                if (assemblies.TryGetValue(targetDll, out var assembly))
-                {
-                    assemblyPatcher.Patcher?.Invoke(ref assembly);
-                    assemblies[targetDll] = assembly;
-                    patchedAssemblies.Add(targetDll);
-                }
-
-            // Finally, load all assemblies into memory
-            foreach (KeyValuePair<string, AssemblyDefinition> kv in assemblies)
-            {
-                string filename = kv.Key;
-                var assembly = kv.Value;
-
-                if (DumpingEnabled && patchedAssemblies.Contains(filename))
-                    using (var mem = new MemoryStream())
-                    {
-                        string dirPath = Path.Combine(Paths.PluginPath, "DumpedAssemblies");
-
-                        if (!Directory.Exists(dirPath))
-                            Directory.CreateDirectory(dirPath);
-
-                        assembly.Write(mem);
-                        File.WriteAllBytes(Path.Combine(dirPath, filename), mem.ToArray());
-                    }
-
-                Load(assembly);
-                assembly.Dispose();
-            }
-
-            //run all finalizers
-            FinalizePatching();
-        }
-
-        /// <summary>
-        ///     Loads an individual assembly defintion into the CLR.
-        /// </summary>
-        /// <param name="assembly">The assembly to load.</param>
-        public static void Load(AssemblyDefinition assembly)
-        {
-            using (var assemblyStream = new MemoryStream())
-            {
-                assembly.Write(assemblyStream);
-                Assembly.Load(assemblyStream.ToArray());
-            }
-        }
-    }
+	/// <summary>
+	///     Delegate used in patching assemblies.
+	/// </summary>
+	/// <param name="assembly">The assembly that is being patched.</param>
+	internal delegate void AssemblyPatcherDelegate(ref AssemblyDefinition assembly);
+
+	/// <summary>
+	///     Worker class which is used for loading and patching entire folders of assemblies, or alternatively patching and
+	///     loading assemblies one at a time.
+	/// </summary>
+	internal static class AssemblyPatcher
+	{
+		private static readonly List<PatcherPlugin> patchers = new List<PatcherPlugin>();
+
+		/// <summary>
+		///     Configuration value of whether assembly dumping is enabled or not.
+		/// </summary>
+		private static bool DumpingEnabled =>
+			Utility.SafeParseBool(Config.GetEntry("dump-assemblies", "false", "Preloader"));
+
+		/// <summary>
+		///     Adds a single assembly patcher to the pool of applicable patches.
+		/// </summary>
+		/// <param name="patcher">Patcher to apply.</param>
+		public static void AddPatcher(PatcherPlugin patcher)
+		{
+			patchers.Add(patcher);
+		}
+
+		/// <summary>
+		///     Adds all patchers from all managed assemblies specified in a directory.
+		/// </summary>
+		/// <param name="directory">Directory to search patcher DLLs from.</param>
+		/// <param name="patcherLocator">A function that locates assembly patchers in a given managed assembly.</param>
+		public static void AddPatchersFromDirectory(string directory,
+			Func<Assembly, List<PatcherPlugin>> patcherLocator)
+		{
+			if (!Directory.Exists(directory))
+				return;
+
+			var sortedPatchers = new SortedDictionary<string, PatcherPlugin>();
+
+			foreach (string assemblyPath in Directory.GetFiles(directory, "*.dll"))
+				try
+				{
+					var assembly = Assembly.LoadFrom(assemblyPath);
+
+					foreach (var patcher in patcherLocator(assembly))
+						sortedPatchers.Add(patcher.Name, patcher);
+				}
+				catch (BadImageFormatException) { } //unmanaged DLL
+				catch (ReflectionTypeLoadException) { } //invalid references
+
+			foreach (KeyValuePair<string, PatcherPlugin> patcher in sortedPatchers)
+				AddPatcher(patcher.Value);
+		}
+
+		private static void InitializePatchers()
+		{
+			foreach (var assemblyPatcher in patchers)
+				assemblyPatcher.Initializer?.Invoke();
+		}
+
+		private static void FinalizePatching()
+		{
+			foreach (var assemblyPatcher in patchers)
+				assemblyPatcher.Finalizer?.Invoke();
+		}
+
+		/// <summary>
+		///     Releases all patchers to let them be collected by GC.
+		/// </summary>
+		public static void DisposePatchers()
+		{
+			patchers.Clear();
+		}
+
+		/// <summary>
+		///     Applies patchers to all assemblies in the given directory and loads patched assemblies into memory.
+		/// </summary>
+		/// <param name="directory">Directory to load CLR assemblies from.</param>
+		public static void PatchAndLoad(string directory)
+		{
+			// First, load patchable assemblies into Cecil
+			var assemblies = new Dictionary<string, AssemblyDefinition>();
+
+			foreach (string assemblyPath in Directory.GetFiles(directory, "*.dll"))
+			{
+				var assembly = AssemblyDefinition.ReadAssembly(assemblyPath);
+
+				//NOTE: this is special cased here because the dependency handling for System.dll is a bit wonky
+				//System has an assembly reference to itself, and it also has a reference to Mono.Security causing a circular dependency
+				//It's also generally dangerous to change system.dll since so many things rely on it, 
+				// and it's already loaded into the appdomain since this loader references it, so we might as well skip it
+				if (assembly.Name.Name == "System"
+					|| assembly.Name.Name == "mscorlib"
+				) //mscorlib is already loaded into the appdomain so it can't be patched
+				{
+					assembly.Dispose();
+					continue;
+				}
+
+				if (UnityPatches.AssemblyLocations.ContainsKey(assembly.FullName))
+				{
+					Logger.Log(LogLevel.Warning,
+						$"Tried to load duplicate assembly {Path.GetFileName(assemblyPath)} from Managed folder! Skipping...");
+					continue;
+				}
+
+				assemblies.Add(Path.GetFileName(assemblyPath), assembly);
+				UnityPatches.AssemblyLocations.Add(assembly.FullName, Path.GetFullPath(assemblyPath));
+			}
+
+			// Next, initialize all the patchers
+			InitializePatchers();
+
+			// Then, perform the actual patching
+			var patchedAssemblies = new HashSet<string>();
+			foreach (var assemblyPatcher in patchers)
+				foreach (string targetDll in assemblyPatcher.TargetDLLs)
+					if (assemblies.TryGetValue(targetDll, out var assembly))
+					{
+						assemblyPatcher.Patcher?.Invoke(ref assembly);
+						assemblies[targetDll] = assembly;
+						patchedAssemblies.Add(targetDll);
+					}
+
+			// Finally, load all assemblies into memory
+			foreach (KeyValuePair<string, AssemblyDefinition> kv in assemblies)
+			{
+				string filename = kv.Key;
+				var assembly = kv.Value;
+
+				if (DumpingEnabled && patchedAssemblies.Contains(filename))
+					using (var mem = new MemoryStream())
+					{
+						string dirPath = Path.Combine(Paths.PluginPath, "DumpedAssemblies");
+
+						if (!Directory.Exists(dirPath))
+							Directory.CreateDirectory(dirPath);
+
+						assembly.Write(mem);
+						File.WriteAllBytes(Path.Combine(dirPath, filename), mem.ToArray());
+					}
+
+				Load(assembly);
+				assembly.Dispose();
+			}
+
+			//run all finalizers
+			FinalizePatching();
+		}
+
+		/// <summary>
+		///     Loads an individual assembly defintion into the CLR.
+		/// </summary>
+		/// <param name="assembly">The assembly to load.</param>
+		public static void Load(AssemblyDefinition assembly)
+		{
+			using (var assemblyStream = new MemoryStream())
+			{
+				assembly.Write(assemblyStream);
+				Assembly.Load(assemblyStream.ToArray());
+			}
+		}
+	}
 }

+ 26 - 26
BepInEx.Preloader/Patcher/PatcherPlugin.cs

@@ -3,34 +3,34 @@ using System.Collections.Generic;
 
 namespace BepInEx.Preloader.Patcher
 {
-    /// <summary>
-    ///     A single assembly patcher.
-    /// </summary>
-    internal class PatcherPlugin
-    {
-        /// <summary>
-        ///     Target assemblies to patch.
-        /// </summary>
-        public IEnumerable<string> TargetDLLs { get; set; } = null;
+	/// <summary>
+	///     A single assembly patcher.
+	/// </summary>
+	internal class PatcherPlugin
+	{
+		/// <summary>
+		///     Target assemblies to patch.
+		/// </summary>
+		public IEnumerable<string> TargetDLLs { get; set; } = null;
 
-        /// <summary>
-        ///     Initializer method that is run before any patching occurs.
-        /// </summary>
-        public Action Initializer { get; set; } = null;
+		/// <summary>
+		///     Initializer method that is run before any patching occurs.
+		/// </summary>
+		public Action Initializer { get; set; } = null;
 
-        /// <summary>
-        ///     Finalizer method that is run after all patching is done.
-        /// </summary>
-        public Action Finalizer { get; set; } = null;
+		/// <summary>
+		///     Finalizer method that is run after all patching is done.
+		/// </summary>
+		public Action Finalizer { get; set; } = null;
 
-        /// <summary>
-        ///     The main patcher method that is called on every DLL defined in <see cref="TargetDLLs" />.
-        /// </summary>
-        public AssemblyPatcherDelegate Patcher { get; set; } = null;
+		/// <summary>
+		///     The main patcher method that is called on every DLL defined in <see cref="TargetDLLs" />.
+		/// </summary>
+		public AssemblyPatcherDelegate Patcher { get; set; } = null;
 
-        /// <summary>
-        ///     Name of the patcher.
-        /// </summary>
-        public string Name { get; set; } = string.Empty;
-    }
+		/// <summary>
+		///     Name of the patcher.
+		/// </summary>
+		public string Name { get; set; } = string.Empty;
+	}
 }

+ 268 - 267
BepInEx.Preloader/Preloader.cs

@@ -14,281 +14,282 @@ using MethodAttributes = Mono.Cecil.MethodAttributes;
 
 namespace BepInEx.Preloader
 {
-    /// <summary>
-    ///     The main entrypoint of BepInEx, and initializes all patchers and the chainloader.
-    /// </summary>
-    internal static class Preloader
-    {
-        /// <summary>
-        ///     The log writer that is specific to the preloader.
-        /// </summary>
-        private static PreloaderLogWriter PreloaderLog { get; set; }
+	/// <summary>
+	///     The main entrypoint of BepInEx, and initializes all patchers and the chainloader.
+	/// </summary>
+	internal static class Preloader
+	{
+		/// <summary>
+		///     The log writer that is specific to the preloader.
+		/// </summary>
+		private static PreloaderLogWriter PreloaderLog { get; set; }
 
-        public static void Run()
-        {
-            try
-            {
-                AllocateConsole();
+		public static void Run()
+		{
+			try
+			{
+				AllocateConsole();
 
-                UnityPatches.Apply();
+				UnityPatches.Apply();
 
-                PreloaderLog =
-                    new PreloaderLogWriter(
-                        Utility.SafeParseBool(Config.GetEntry("preloader-logconsole", "false", "BepInEx")));
-                PreloaderLog.Enabled = true;
+				PreloaderLog =
+					new PreloaderLogWriter(
+						Utility.SafeParseBool(Config.GetEntry("preloader-logconsole", "false", "BepInEx")));
+				PreloaderLog.Enabled = true;
 
-                string consoleTile =
-                    $"BepInEx {typeof(Paths).Assembly.GetName().Version} - {Process.GetCurrentProcess().ProcessName}";
-                ConsoleWindow.Title = consoleTile;
+				string consoleTile =
+					$"BepInEx {typeof(Paths).Assembly.GetName().Version} - {Process.GetCurrentProcess().ProcessName}";
+				ConsoleWindow.Title = consoleTile;
 
-                Logger.SetLogger(PreloaderLog);
+				Logger.SetLogger(PreloaderLog);
 
-                PreloaderLog.WriteLine(consoleTile);
+				PreloaderLog.WriteLine(consoleTile);
 
 				//See BuildInfoAttribute for more information about this section.
 				object[] attributes = typeof(BuildInfoAttribute).Assembly.GetCustomAttributes(typeof(BuildInfoAttribute), false);
 
-                if (attributes.Length > 0)
-                {
-                    var attribute = (BuildInfoAttribute)attributes[0];
-
-                    PreloaderLog.WriteLine(attribute.Info);
-                }
-
-                Logger.Log(LogLevel.Message, "Preloader started");
-
-                string entrypointAssembly = Config.GetEntry("entrypoint-assembly", "UnityEngine.dll", "Preloader");
-
-                AssemblyPatcher.AddPatcher(new PatcherPlugin
-                    {TargetDLLs = new[] {entrypointAssembly}, Patcher = PatchEntrypoint});
-                AssemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath, GetPatcherMethods);
-
-                AssemblyPatcher.PatchAndLoad(Paths.ManagedPath);
-
-                AssemblyPatcher.DisposePatchers();
-            }
-            catch (Exception ex)
-            {
-                Logger.Log(LogLevel.Fatal, "Could not run preloader!");
-                Logger.Log(LogLevel.Fatal, ex);
-
-                PreloaderLog.Enabled = false;
-
-                try
-                {
-                    if (!ConsoleWindow.IsAttatched)
-                    {
-                        //if we've already attached the console, then the log will already be written to the console
-                        AllocateConsole();
-                        Console.Write(PreloaderLog);
-                    }
-                }
-                finally
-                {
-                    File.WriteAllText(
-                        Path.Combine(Paths.GameRootPath, $"preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"),
-                        PreloaderLog + "\r\n" + ex);
-
-                    PreloaderLog.Dispose();
-                    PreloaderLog = null;
-                }
-            }
-        }
-
-        /// <summary>
-        ///     Scans the assembly for classes that use the patcher contract, and returns a list of valid patchers.
-        /// </summary>
-        /// <param name="assembly">The assembly to scan.</param>
-        /// <returns>A list of assembly patchers that were found in the assembly.</returns>
-        public static List<PatcherPlugin> GetPatcherMethods(Assembly assembly)
-        {
-            var patcherMethods = new List<PatcherPlugin>();
-            var flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase;
-
-            foreach (var type in assembly.GetExportedTypes())
-                try
-                {
-                    if (type.IsInterface)
-                        continue;
-
-                    var targetsProperty = type.GetProperty("TargetDLLs",
-                        flags,
-                        null,
-                        typeof(IEnumerable<string>),
-                        Type.EmptyTypes,
-                        null);
-
-                    //first try get the ref patcher method
-                    var patcher = type.GetMethod("Patch",
-                        flags,
-                        null,
-                        CallingConventions.Any,
-                        new[] {typeof(AssemblyDefinition).MakeByRefType()},
-                        null);
-
-                    if (patcher == null) //otherwise try getting the non-ref patcher method
-                        patcher = type.GetMethod("Patch",
-                            flags,
-                            null,
-                            CallingConventions.Any,
-                            new[] {typeof(AssemblyDefinition)},
-                            null);
-
-                    if (targetsProperty == null || !targetsProperty.CanRead || patcher == null)
-                        continue;
-
-                    var assemblyPatcher = new PatcherPlugin();
-
-                    assemblyPatcher.Name = $"{assembly.GetName().Name}{type.FullName}";
-                    assemblyPatcher.Patcher = (ref AssemblyDefinition ass) =>
-                    {
-                        //we do the array fuckery here to get the ref result out
-                        object[] args = {ass};
-
-                        patcher.Invoke(null, args);
-
-                        ass = (AssemblyDefinition) args[0];
-                    };
-
-                    assemblyPatcher.TargetDLLs = (IEnumerable<string>) targetsProperty.GetValue(null, null);
-
-                    var initMethod = type.GetMethod("Initialize",
-                        flags,
-                        null,
-                        CallingConventions.Any,
-                        Type.EmptyTypes,
-                        null);
-
-                    if (initMethod != null)
-                        assemblyPatcher.Initializer = () => initMethod.Invoke(null, null);
-
-                    var finalizeMethod = type.GetMethod("Finish",
-                        flags,
-                        null,
-                        CallingConventions.Any,
-                        Type.EmptyTypes,
-                        null);
-
-                    if (finalizeMethod != null)
-                        assemblyPatcher.Finalizer = () => finalizeMethod.Invoke(null, null);
-                }
-                catch (Exception ex)
-                {
-                    Logger.Log(LogLevel.Warning, $"Could not load patcher methods from {assembly.GetName().Name}");
-                    Logger.Log(LogLevel.Warning, $"{ex}");
-                }
-
-            Logger.Log(LogLevel.Info,
-                $"Loaded {patcherMethods.Count} patcher methods from {assembly.GetName().Name}");
-
-            return patcherMethods;
-        }
-
-        /// <summary>
-        ///     Inserts BepInEx's own chainloader entrypoint into UnityEngine.
-        /// </summary>
-        /// <param name="assembly">The assembly that will be attempted to be patched.</param>
-        public static void PatchEntrypoint(ref AssemblyDefinition assembly)
-        {
-            if (assembly.MainModule.AssemblyReferences.Any(x => x.Name.Contains("BepInEx")))
-                throw new Exception(
-                    "BepInEx has been detected to be patched! Please unpatch before using a patchless variant!");
-
-            string entrypointType = Config.GetEntry("entrypoint-type", "Application", "Preloader");
-            string entrypointMethod = Config.GetEntry("entrypoint-method", ".cctor", "Preloader");
-
-            bool isCctor = entrypointMethod.IsNullOrWhiteSpace() || entrypointMethod == ".cctor";
-
-
-            var entryType = assembly.MainModule.Types.FirstOrDefault(x => x.Name == entrypointType);
-
-            if (entryType == null) throw new Exception("The entrypoint type is invalid! Please check your config.ini");
-
-            using (var injected = AssemblyDefinition.ReadAssembly(Paths.BepInExAssemblyPath))
-            {
-                var originalInitMethod = injected.MainModule.Types.First(x => x.Name == "Chainloader").Methods
-                    .First(x => x.Name == "Initialize");
-
-                var originalStartMethod = injected.MainModule.Types.First(x => x.Name == "Chainloader").Methods
-                    .First(x => x.Name == "Start");
-
-                var initMethod = assembly.MainModule.ImportReference(originalInitMethod);
-                var startMethod = assembly.MainModule.ImportReference(originalStartMethod);
-
-                var methods = new List<MethodDefinition>();
-
-                if (isCctor)
-                {
-                    var cctor = entryType.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic);
-
-                    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);
-                        var il = cctor.Body.GetILProcessor();
-                        il.Append(il.Create(OpCodes.Ret));
-                    }
-
-                    methods.Add(cctor);
-                }
-                else
-                {
-                    methods.AddRange(entryType.Methods.Where(x => x.Name == entrypointMethod));
-                }
-
-                if (!methods.Any())
-                    throw new Exception("The entrypoint method is invalid! Please check your config.ini");
-
-                foreach (var method in methods)
-                {
-                    var il = method.Body.GetILProcessor();
-
-                    var ins = il.Body.Instructions.First();
-
-                    il.InsertBefore(ins, il.Create(OpCodes.Ldstr, Paths.ExecutablePath)); //containerExePath
-                    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.InsertBefore(ins, il.Create(OpCodes.Call, startMethod));
-                }
-            }
-        }
-
-        /// <summary>
-        ///     Allocates a console window for use by BepInEx safely.
-        /// </summary>
-        public static void AllocateConsole()
-        {
-            bool console = Utility.SafeParseBool(Config.GetEntry("console", "false", "BepInEx"));
-            bool shiftjis = Utility.SafeParseBool(Config.GetEntry("console-shiftjis", "false", "BepInEx"));
-
-            if (!console)
-                return;
-
-            try
-            {
-                ConsoleWindow.Attach();
-
-                var encoding = (uint) Encoding.UTF8.CodePage;
-
-                if (shiftjis)
-                    encoding = 932;
-
-                ConsoleEncoding.ConsoleCodePage = encoding;
-                Console.OutputEncoding = ConsoleEncoding.GetEncoding(encoding);
-            }
-            catch (Exception ex)
-            {
-                Logger.Log(LogLevel.Error, "Failed to allocate console!");
-                Logger.Log(LogLevel.Error, ex);
-            }
-        }
-    }
+				if (attributes.Length > 0)
+				{
+					var attribute = (BuildInfoAttribute)attributes[0];
+
+					PreloaderLog.WriteLine(attribute.Info);
+				}
+
+				Logger.Log(LogLevel.Message, "Preloader started");
+
+				string entrypointAssembly = Config.GetEntry("entrypoint-assembly", "UnityEngine.dll", "Preloader");
+
+				AssemblyPatcher.AddPatcher(new PatcherPlugin
+					{ TargetDLLs = new[] { entrypointAssembly }, Patcher = PatchEntrypoint });
+				AssemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath, GetPatcherMethods);
+
+				AssemblyPatcher.PatchAndLoad(Paths.ManagedPath);
+
+				AssemblyPatcher.DisposePatchers();
+			}
+			catch (Exception ex)
+			{
+				Logger.Log(LogLevel.Fatal, "Could not run preloader!");
+				Logger.Log(LogLevel.Fatal, ex);
+
+				PreloaderLog.Enabled = false;
+
+				try
+				{
+					if (!ConsoleWindow.IsAttatched)
+					{
+						//if we've already attached the console, then the log will already be written to the console
+						AllocateConsole();
+						Console.Write(PreloaderLog);
+					}
+				}
+				finally
+				{
+					File.WriteAllText(
+						Path.Combine(Paths.GameRootPath, $"preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"),
+						PreloaderLog + "\r\n" + ex);
+
+					PreloaderLog.Dispose();
+					PreloaderLog = null;
+				}
+			}
+		}
+
+		/// <summary>
+		///     Scans the assembly for classes that use the patcher contract, and returns a list of valid patchers.
+		/// </summary>
+		/// <param name="assembly">The assembly to scan.</param>
+		/// <returns>A list of assembly patchers that were found in the assembly.</returns>
+		public static List<PatcherPlugin> GetPatcherMethods(Assembly assembly)
+		{
+			var patcherMethods = new List<PatcherPlugin>();
+			var flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase;
+
+			foreach (var type in assembly.GetExportedTypes())
+				try
+				{
+					if (type.IsInterface)
+						continue;
+
+					var targetsProperty = type.GetProperty("TargetDLLs",
+						flags,
+						null,
+						typeof(IEnumerable<string>),
+						Type.EmptyTypes,
+						null);
+
+					//first try get the ref patcher method
+					var patcher = type.GetMethod("Patch",
+						flags,
+						null,
+						CallingConventions.Any,
+						new[] { typeof(AssemblyDefinition).MakeByRefType() },
+						null);
+
+					if (patcher == null) //otherwise try getting the non-ref patcher method
+						patcher = type.GetMethod("Patch",
+							flags,
+							null,
+							CallingConventions.Any,
+							new[] { typeof(AssemblyDefinition) },
+							null);
+
+					if (targetsProperty == null || !targetsProperty.CanRead || patcher == null)
+						continue;
+
+					var assemblyPatcher = new PatcherPlugin();
+
+					assemblyPatcher.Name = $"{assembly.GetName().Name}{type.FullName}";
+					assemblyPatcher.Patcher = (ref AssemblyDefinition ass) =>
+					{
+						//we do the array fuckery here to get the ref result out
+						object[] args = { ass };
+
+						patcher.Invoke(null, args);
+
+						ass = (AssemblyDefinition)args[0];
+					};
+
+					assemblyPatcher.TargetDLLs = (IEnumerable<string>)targetsProperty.GetValue(null, null);
+
+					var initMethod = type.GetMethod("Initialize",
+						flags,
+						null,
+						CallingConventions.Any,
+						Type.EmptyTypes,
+						null);
+
+					if (initMethod != null)
+						assemblyPatcher.Initializer = () => initMethod.Invoke(null, null);
+
+					var finalizeMethod = type.GetMethod("Finish",
+						flags,
+						null,
+						CallingConventions.Any,
+						Type.EmptyTypes,
+						null);
+
+					if (finalizeMethod != null)
+						assemblyPatcher.Finalizer = () => finalizeMethod.Invoke(null, null);
+				}
+				catch (Exception ex)
+				{
+					Logger.Log(LogLevel.Warning, $"Could not load patcher methods from {assembly.GetName().Name}");
+					Logger.Log(LogLevel.Warning, $"{ex}");
+				}
+
+			Logger.Log(LogLevel.Info,
+				$"Loaded {patcherMethods.Count} patcher methods from {assembly.GetName().Name}");
+
+			return patcherMethods;
+		}
+
+		/// <summary>
+		///     Inserts BepInEx's own chainloader entrypoint into UnityEngine.
+		/// </summary>
+		/// <param name="assembly">The assembly that will be attempted to be patched.</param>
+		public static void PatchEntrypoint(ref AssemblyDefinition assembly)
+		{
+			if (assembly.MainModule.AssemblyReferences.Any(x => x.Name.Contains("BepInEx")))
+				throw new Exception(
+					"BepInEx has been detected to be patched! Please unpatch before using a patchless variant!");
+
+			string entrypointType = Config.GetEntry("entrypoint-type", "Application", "Preloader");
+			string entrypointMethod = Config.GetEntry("entrypoint-method", ".cctor", "Preloader");
+
+			bool isCctor = entrypointMethod.IsNullOrWhiteSpace() || entrypointMethod == ".cctor";
+
+
+			var entryType = assembly.MainModule.Types.FirstOrDefault(x => x.Name == entrypointType);
+
+			if (entryType == null)
+				throw new Exception("The entrypoint type is invalid! Please check your config.ini");
+
+			using (var injected = AssemblyDefinition.ReadAssembly(Paths.BepInExAssemblyPath))
+			{
+				var originalInitMethod = injected.MainModule.Types.First(x => x.Name == "Chainloader").Methods
+												 .First(x => x.Name == "Initialize");
+
+				var originalStartMethod = injected.MainModule.Types.First(x => x.Name == "Chainloader").Methods
+												  .First(x => x.Name == "Start");
+
+				var initMethod = assembly.MainModule.ImportReference(originalInitMethod);
+				var startMethod = assembly.MainModule.ImportReference(originalStartMethod);
+
+				var methods = new List<MethodDefinition>();
+
+				if (isCctor)
+				{
+					var cctor = entryType.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic);
+
+					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);
+						var il = cctor.Body.GetILProcessor();
+						il.Append(il.Create(OpCodes.Ret));
+					}
+
+					methods.Add(cctor);
+				}
+				else
+				{
+					methods.AddRange(entryType.Methods.Where(x => x.Name == entrypointMethod));
+				}
+
+				if (!methods.Any())
+					throw new Exception("The entrypoint method is invalid! Please check your config.ini");
+
+				foreach (var method in methods)
+				{
+					var il = method.Body.GetILProcessor();
+
+					var ins = il.Body.Instructions.First();
+
+					il.InsertBefore(ins, il.Create(OpCodes.Ldstr, Paths.ExecutablePath)); //containerExePath
+					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.InsertBefore(ins, il.Create(OpCodes.Call, startMethod));
+				}
+			}
+		}
+
+		/// <summary>
+		///     Allocates a console window for use by BepInEx safely.
+		/// </summary>
+		public static void AllocateConsole()
+		{
+			bool console = Utility.SafeParseBool(Config.GetEntry("console", "false", "BepInEx"));
+			bool shiftjis = Utility.SafeParseBool(Config.GetEntry("console-shiftjis", "false", "BepInEx"));
+
+			if (!console)
+				return;
+
+			try
+			{
+				ConsoleWindow.Attach();
+
+				var encoding = (uint)Encoding.UTF8.CodePage;
+
+				if (shiftjis)
+					encoding = 932;
+
+				ConsoleEncoding.ConsoleCodePage = encoding;
+				Console.OutputEncoding = ConsoleEncoding.GetEncoding(encoding);
+			}
+			catch (Exception ex)
+			{
+				Logger.Log(LogLevel.Error, "Failed to allocate console!");
+				Logger.Log(LogLevel.Error, ex);
+			}
+		}
+	}
 }

+ 30 - 30
BepInEx.Preloader/UnityPatches.cs

@@ -6,45 +6,45 @@ using Harmony;
 
 namespace BepInEx.Preloader
 {
-    internal static class UnityPatches
-    {
-        public static HarmonyInstance HarmonyInstance { get; } = HarmonyInstance.Create("com.bepinex.unitypatches");
+	internal static class UnityPatches
+	{
+		public static HarmonyInstance HarmonyInstance { get; } = HarmonyInstance.Create("com.bepinex.unitypatches");
 
-        public static Dictionary<string, string> AssemblyLocations { get; } =
-            new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
+		public static Dictionary<string, string> AssemblyLocations { get; } =
+			new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
 
-        public static void Apply()
-        {
-            HarmonyWrapper.PatchAll(typeof(UnityPatches), HarmonyInstance);
-        }
+		public static void Apply()
+		{
+			HarmonyWrapper.PatchAll(typeof(UnityPatches), HarmonyInstance);
+		}
 
-        [HarmonyPostfix]
-        [HarmonyPatch(typeof(Assembly), nameof(Assembly.Location), MethodType.Getter)]
-        public static void GetLocation(ref string __result, Assembly __instance)
-        {
-            if (AssemblyLocations.TryGetValue(__instance.FullName, out string location))
-                __result = location;
-        }
+		[HarmonyPostfix]
+		[HarmonyPatch(typeof(Assembly), nameof(Assembly.Location), MethodType.Getter)]
+		public static void GetLocation(ref string __result, Assembly __instance)
+		{
+			if (AssemblyLocations.TryGetValue(__instance.FullName, out string location))
+				__result = location;
+		}
 
-        [HarmonyPostfix]
-        [HarmonyPatch(typeof(Assembly), nameof(Assembly.CodeBase), MethodType.Getter)]
-        public static void GetCodeBase(ref string __result, Assembly __instance)
-        {
-            if (AssemblyLocations.TryGetValue(__instance.FullName, out string location))
-                __result = $"file://{location.Replace('\\', '/')}";
-        }
+		[HarmonyPostfix]
+		[HarmonyPatch(typeof(Assembly), nameof(Assembly.CodeBase), MethodType.Getter)]
+		public static void GetCodeBase(ref string __result, Assembly __instance)
+		{
+			if (AssemblyLocations.TryGetValue(__instance.FullName, out string location))
+				__result = $"file://{location.Replace('\\', '/')}";
+		}
 
 #if UNITY_2018
 /*
  * DESC: Workaround for Trace class not working because of missing .config file
  * AFFECTS: Unity 2018+
  */
-        [HarmonyPostfix, HarmonyPatch(typeof(AppDomain), nameof(AppDomain.SetupInformation), MethodType.Getter)]
-        public static void GetExeConfigName(AppDomainSetup __result)
-        {
-            __result.ApplicationBase = $"file://{Paths.GameRootPath}";
-            __result.ConfigurationFile = "app.config";
-        }
+		[HarmonyPostfix, HarmonyPatch(typeof(AppDomain), nameof(AppDomain.SetupInformation), MethodType.Getter)]
+		public static void GetExeConfigName(AppDomainSetup __result)
+		{
+			__result.ApplicationBase = $"file://{Paths.GameRootPath}";
+			__result.ConfigurationFile = "app.config";
+		}
 #endif
-    }
+	}
 }

+ 14 - 14
BepInEx/Bootstrap/Chainloader.cs

@@ -47,15 +47,15 @@ namespace BepInEx.Bootstrap
 			if (startConsole)
 			{
 				ConsoleWindow.Attach();
-				
+
 				ConsoleEncoding.ConsoleCodePage = (uint)Encoding.UTF8.CodePage;
 				Console.OutputEncoding = Encoding.UTF8;
 			}
-			
+
 			UnityLogWriter unityLogWriter = new UnityLogWriter();
 
-		    if (Logger.CurrentLogger != null && Logger.CurrentLogger is PreloaderLogWriter preloaderLogger)
-                unityLogWriter.WriteToLog($"{preloaderLogger}\r\n");
+			if (Logger.CurrentLogger != null && Logger.CurrentLogger is PreloaderLogWriter preloaderLogger)
+				unityLogWriter.WriteToLog($"{preloaderLogger}\r\n");
 
 			Logger.SetLogger(unityLogWriter);
 
@@ -94,17 +94,17 @@ namespace BepInEx.Bootstrap
 				string currentProcess = Process.GetCurrentProcess().ProcessName.ToLower();
 
 				var pluginTypes = TypeLoader.LoadTypes<BaseUnityPlugin>(Paths.PluginPath)
-					.Where(plugin =>
-					{
-						//Perform a filter for currently running process
-						var filters = MetadataHelper.GetAttributes<BepInProcess>(plugin);
+											.Where(plugin =>
+											{
+												//Perform a filter for currently running process
+												var filters = MetadataHelper.GetAttributes<BepInProcess>(plugin);
 
-						if (!filters.Any())
-							return true;
+												if (!filters.Any())
+													return true;
 
-						return filters.Any(x => x.ProcessName.ToLower().Replace(".exe", "") == currentProcess);
-					})
-					.ToList();
+												return filters.Any(x => x.ProcessName.ToLower().Replace(".exe", "") == currentProcess);
+											})
+											.ToList();
 
 				Logger.Log(LogLevel.Info, $"{pluginTypes.Count} plugins selected");
 
@@ -135,7 +135,7 @@ namespace BepInEx.Bootstrap
 					{
 						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}]");

+ 63 - 61
BepInEx/Bootstrap/TypeLoader.cs

@@ -10,68 +10,70 @@ namespace BepInEx.Bootstrap
 	/// <summary>
 	/// Provides methods for loading specified types from an assembly.
 	/// </summary>
-    public static class TypeLoader
-    {
-        /// <summary>
-        /// Loads a list of types from a directory containing assemblies, that derive from a base type.
-        /// </summary>
-        /// <typeparam name="T">The specfiic base type to search for.</typeparam>
-        /// <param name="directory">The directory to search for assemblies.</param>
-        /// <returns>Returns a list of found derivative types.</returns>
-        public static IEnumerable<Type> LoadTypes<T>(string directory)
-        {
-            List<Type> types = new List<Type>();
-            Type pluginType = typeof(T);
+	public static class TypeLoader
+	{
+		/// <summary>
+		/// Loads a list of types from a directory containing assemblies, that derive from a base type.
+		/// </summary>
+		/// <typeparam name="T">The specfiic base type to search for.</typeparam>
+		/// <param name="directory">The directory to search for assemblies.</param>
+		/// <returns>Returns a list of found derivative types.</returns>
+		public static IEnumerable<Type> LoadTypes<T>(string directory)
+		{
+			List<Type> types = new List<Type>();
+			Type pluginType = typeof(T);
 
-            foreach (string dll in Directory.GetFiles(Path.GetFullPath(directory), "*.dll"))
-            {
-                try
-                {
-                    AssemblyName an = AssemblyName.GetAssemblyName(dll);
-                    Assembly assembly = Assembly.Load(an);
+			foreach (string dll in Directory.GetFiles(Path.GetFullPath(directory), "*.dll"))
+			{
+				try
+				{
+					AssemblyName an = AssemblyName.GetAssemblyName(dll);
+					Assembly assembly = Assembly.Load(an);
 
-                    foreach (Type type in assembly.GetTypes())
-                    {
-                        if (!type.IsInterface && !type.IsAbstract && type.BaseType == pluginType)
-                            types.Add(type);
-                    }
-                }
-                catch (BadImageFormatException) { } //unmanaged DLL
-                catch (ReflectionTypeLoadException ex)
-                {
-                    Logger.Log(LogLevel.Error, $"Could not load \"{Path.GetFileName(dll)}\" as a plugin!");
-                    Logger.Log(LogLevel.Debug, TypeLoadExceptionToString(ex));
-                }
-            }
+					foreach (Type type in assembly.GetTypes())
+					{
+						if (!type.IsInterface && !type.IsAbstract && type.BaseType == pluginType)
+							types.Add(type);
+					}
+				}
+				catch (BadImageFormatException) { } //unmanaged DLL
+				catch (ReflectionTypeLoadException ex)
+				{
+					Logger.Log(LogLevel.Error, $"Could not load \"{Path.GetFileName(dll)}\" as a plugin!");
+					Logger.Log(LogLevel.Debug, TypeLoadExceptionToString(ex));
+				}
+			}
 
-            return types;
-        }
+			return types;
+		}
 
-        private static string TypeLoadExceptionToString(ReflectionTypeLoadException ex)
-        {
-            StringBuilder sb = new StringBuilder();
-            foreach (Exception exSub in ex.LoaderExceptions)
-            {
-                sb.AppendLine(exSub.Message);
-                if (exSub is FileNotFoundException exFileNotFound)
-                {
-                    if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
-                    {
-                        sb.AppendLine("Fusion Log:");
-                        sb.AppendLine(exFileNotFound.FusionLog);
-                    }
-                }
-                else if (exSub is FileLoadException exLoad)
-                {
-                    if (!string.IsNullOrEmpty(exLoad.FusionLog))
-                    {
-                        sb.AppendLine("Fusion Log:");
-                        sb.AppendLine(exLoad.FusionLog);
-                    }
-                }
-                sb.AppendLine();
-            }
-            return sb.ToString();
-        }
-    }
-}
+		private static string TypeLoadExceptionToString(ReflectionTypeLoadException ex)
+		{
+			StringBuilder sb = new StringBuilder();
+			foreach (Exception exSub in ex.LoaderExceptions)
+			{
+				sb.AppendLine(exSub.Message);
+				if (exSub is FileNotFoundException exFileNotFound)
+				{
+					if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
+					{
+						sb.AppendLine("Fusion Log:");
+						sb.AppendLine(exFileNotFound.FusionLog);
+					}
+				}
+				else if (exSub is FileLoadException exLoad)
+				{
+					if (!string.IsNullOrEmpty(exLoad.FusionLog))
+					{
+						sb.AppendLine("Fusion Log:");
+						sb.AppendLine(exLoad.FusionLog);
+					}
+				}
+
+				sb.AppendLine();
+			}
+
+			return sb.ToString();
+		}
+	}
+}

+ 191 - 190
BepInEx/Config.cs

@@ -6,55 +6,55 @@ using BepInEx.Logging;
 
 namespace BepInEx
 {
-    /// <summary>
-    /// A helper class to handle persistent data.
-    /// </summary>
-    public static class Config
-    {
-        private static readonly Dictionary<string, Dictionary<string, string>> cache = new Dictionary<string, Dictionary<string, string>>();
+	/// <summary>
+	/// A helper class to handle persistent data.
+	/// </summary>
+	public static class Config
+	{
+		private static readonly Dictionary<string, Dictionary<string, string>> cache = new Dictionary<string, Dictionary<string, string>>();
 
-        private static string configPath => Path.Combine(Paths.PluginPath, "config.ini");
+		private static string configPath => Path.Combine(Paths.PluginPath, "config.ini");
 
-        private static readonly Regex sanitizeKeyRegex = new Regex(@"[^a-zA-Z0-9\-\.]+");
+		private static readonly Regex sanitizeKeyRegex = new Regex(@"[^a-zA-Z0-9\-\.]+");
 
-        private static void RaiseConfigReloaded()
-        {
-            ConfigReloaded?.Invoke();
-        }
+		private static void RaiseConfigReloaded()
+		{
+			ConfigReloaded?.Invoke();
+		}
 
 		/// <summary>
 		/// An event that is fired every time the config is reloaded.
 		/// </summary>
-        public static event Action ConfigReloaded;
-
-        /// <summary>
-        /// If enabled, writes the config to disk every time a value is set.
-        /// </summary>
-        public static bool SaveOnConfigSet { get; set; } = true;
-
-        static Config()
-        {
-            if (File.Exists(configPath))
-            {
-                ReloadConfig();
-            }
-            else
-            {
-                SaveConfig();
-            }
-        }
-
-	    /// <summary>
-	    /// Returns the value of the key if found, otherwise returns the default value.
-	    /// </summary>
-	    /// <param name="key">The key to search for.</param>
-	    /// <param name="defaultValue">The default value to return if the key is not found.</param>
-	    /// <param name="section">The section of the config to search the key for.</param>
-	    /// <returns>The value of the key.</returns>
-	    public static string GetEntry(string key, string defaultValue = "", string section = "")
-        {
-	        try
-	        {
+		public static event Action ConfigReloaded;
+
+		/// <summary>
+		/// If enabled, writes the config to disk every time a value is set.
+		/// </summary>
+		public static bool SaveOnConfigSet { get; set; } = true;
+
+		static Config()
+		{
+			if (File.Exists(configPath))
+			{
+				ReloadConfig();
+			}
+			else
+			{
+				SaveConfig();
+			}
+		}
+
+		/// <summary>
+		/// Returns the value of the key if found, otherwise returns the default value.
+		/// </summary>
+		/// <param name="key">The key to search for.</param>
+		/// <param name="defaultValue">The default value to return if the key is not found.</param>
+		/// <param name="section">The section of the config to search the key for.</param>
+		/// <returns>The value of the key.</returns>
+		public static string GetEntry(string key, string defaultValue = "", string section = "")
+		{
+			try
+			{
 				key = Sanitize(key);
 				section = section.IsNullOrWhiteSpace() ? "Global" : Sanitize(section);
 
@@ -70,157 +70,158 @@ namespace BepInEx
 				SetEntry(key, defaultValue, section);
 				return defaultValue;
 			}
-	        catch (Exception ex)
-	        {
-		        Logger.Log(LogLevel.Error | LogLevel.Message, "Unable to read config entry!");
-		        Logger.Log(LogLevel.Error, ex);
-		        return defaultValue;
-	        }
-        }
-
-        /// <summary>
-        /// Reloads the config from disk. Unwritten changes are lost.
-        /// </summary>
-        public static void ReloadConfig()
-        {
-            cache.Clear();
-
-            string currentSection = "";
-
-            foreach (string rawLine in File.ReadAllLines(configPath))
-            {
-                string line = rawLine.Trim();
-
-                bool commentIndex = line.StartsWith(";") || line.StartsWith("#");
-
-                if (commentIndex) //trim comment
-                    continue;
-
-                if (line.StartsWith("[") && line.EndsWith("]")) //section
-                {
-                    currentSection = line.Substring(1, line.Length - 2);
-                    continue;
-                }
-
-                string[] split = line.Split('='); //actual config line
-                if (split.Length != 2)
-                    continue; //empty/invalid line
-
-                if (!cache.ContainsKey(currentSection))
-                    cache[currentSection] = new Dictionary<string, string>();
-
-                cache[currentSection][split[0]] = split[1];
-            }
-
-            RaiseConfigReloaded();
-        }
-
-        /// <summary>
-        /// Writes the config to disk.
-        /// </summary>
-        public static void SaveConfig()
-        {
-            using (StreamWriter writer = new StreamWriter(File.Create(configPath), System.Text.Encoding.UTF8))
-                foreach (var sectionKv in cache)
-                {
-                    writer.WriteLine($"[{sectionKv.Key}]");
-
-                    foreach (var entryKv in sectionKv.Value)
-                        writer.WriteLine($"{entryKv.Key}={entryKv.Value}");
-
-                    writer.WriteLine();
-                }
-        }
-
-        /// <summary>
-        /// Sets the value of the key in the config.
-        /// </summary>
-        /// <param name="key">The key to set the value to.</param>
-        /// <param name="value">The value to set.</param>
-        public static void SetEntry(string key, string value, string section = "")
-        {
-	        try
-	        {
-		        key = Sanitize(key);
-		        section = section.IsNullOrWhiteSpace() ? "Global" : Sanitize(section);
-
-		        if (!cache.TryGetValue(section, out Dictionary<string, string> subdict))
-		        {
-			        subdict = new Dictionary<string, string>();
-			        cache[section] = subdict;
-		        }
-
-		        subdict[key] = value;
-
-		        if (SaveOnConfigSet)
-			        SaveConfig();
-	        }
-	        catch (Exception ex)
-	        {
-		        Logger.Log(LogLevel.Error | LogLevel.Message, "Unable to save config entry!");
-		        Logger.Log(LogLevel.Error, ex);
-	        }
-        }
-       
-        /// <summary>
-        /// Returns wether a value is currently set.
-        /// </summary>
-        /// <param name="key">The key to check against</param>
-        /// <param name="section">The section to check in</param>
-        /// <returns>True if the key is present</returns>
-        public static bool HasEntry(string key, string section = "")
-        {
-            key = Sanitize(key);
-            section = section.IsNullOrWhiteSpace() ? "Global" : Sanitize(section);
-
-            return cache.ContainsKey(section) && cache[section].ContainsKey(key);
-        }
-
-
-        /// <summary>
-        /// Removes a value from the config.
-        /// </summary>
-        /// <param name="key">The key to remove</param>
-        /// <param name="section">The section to remove from</param>
-        /// <returns>True if the key was removed</returns>
-        public static bool UnsetEntry(string key, string section = "")
-        {
-            key = Sanitize(key);
-            section = section.IsNullOrWhiteSpace() ? "Global" : Sanitize(section);
-
-            if (!HasEntry(key, section))
-                return false;
-
-            cache[section].Remove(key);
-            return true;
-        }
+			catch (Exception ex)
+			{
+				Logger.Log(LogLevel.Error | LogLevel.Message, "Unable to read config entry!");
+				Logger.Log(LogLevel.Error, ex);
+				return defaultValue;
+			}
+		}
+
+		/// <summary>
+		/// Reloads the config from disk. Unwritten changes are lost.
+		/// </summary>
+		public static void ReloadConfig()
+		{
+			cache.Clear();
+
+			string currentSection = "";
+
+			foreach (string rawLine in File.ReadAllLines(configPath))
+			{
+				string line = rawLine.Trim();
+
+				bool commentIndex = line.StartsWith(";") || line.StartsWith("#");
+
+				if (commentIndex) //trim comment
+					continue;
+
+				if (line.StartsWith("[") && line.EndsWith("]")) //section
+				{
+					currentSection = line.Substring(1, line.Length - 2);
+					continue;
+				}
+
+				string[] split = line.Split('='); //actual config line
+				if (split.Length != 2)
+					continue; //empty/invalid line
+
+				if (!cache.ContainsKey(currentSection))
+					cache[currentSection] = new Dictionary<string, string>();
+
+				cache[currentSection][split[0]] = split[1];
+			}
+
+			RaiseConfigReloaded();
+		}
+
+		/// <summary>
+		/// Writes the config to disk.
+		/// </summary>
+		public static void SaveConfig()
+		{
+			using (StreamWriter writer = new StreamWriter(File.Create(configPath), System.Text.Encoding.UTF8))
+				foreach (var sectionKv in cache)
+				{
+					writer.WriteLine($"[{sectionKv.Key}]");
+
+					foreach (var entryKv in sectionKv.Value)
+						writer.WriteLine($"{entryKv.Key}={entryKv.Value}");
+
+					writer.WriteLine();
+				}
+		}
+
+		/// <summary>
+		/// Sets the value of the key in the config.
+		/// </summary>
+		/// <param name="key">The key to set the value to.</param>
+		/// <param name="value">The value to set.</param>
+		public static void SetEntry(string key, string value, string section = "")
+		{
+			try
+			{
+				key = Sanitize(key);
+				section = section.IsNullOrWhiteSpace() ? "Global" : Sanitize(section);
+
+				if (!cache.TryGetValue(section, out Dictionary<string, string> subdict))
+				{
+					subdict = new Dictionary<string, string>();
+					cache[section] = subdict;
+				}
+
+				subdict[key] = value;
+
+				if (SaveOnConfigSet)
+					SaveConfig();
+			}
+			catch (Exception ex)
+			{
+				Logger.Log(LogLevel.Error | LogLevel.Message, "Unable to save config entry!");
+				Logger.Log(LogLevel.Error, ex);
+			}
+		}
+
+		/// <summary>
+		/// Returns wether a value is currently set.
+		/// </summary>
+		/// <param name="key">The key to check against</param>
+		/// <param name="section">The section to check in</param>
+		/// <returns>True if the key is present</returns>
+		public static bool HasEntry(string key, string section = "")
+		{
+			key = Sanitize(key);
+			section = section.IsNullOrWhiteSpace() ? "Global" : Sanitize(section);
+
+			return cache.ContainsKey(section) && cache[section].ContainsKey(key);
+		}
+
+
+		/// <summary>
+		/// Removes a value from the config.
+		/// </summary>
+		/// <param name="key">The key to remove</param>
+		/// <param name="section">The section to remove from</param>
+		/// <returns>True if the key was removed</returns>
+		public static bool UnsetEntry(string key, string section = "")
+		{
+			key = Sanitize(key);
+			section = section.IsNullOrWhiteSpace() ? "Global" : Sanitize(section);
+
+			if (!HasEntry(key, section))
+				return false;
+
+			cache[section].Remove(key);
+			return true;
+		}
 
 		/// <summary>
 		/// Replaces any potentially breaking input with underscores.
 		/// </summary>
 		/// <param name="text">The text to sanitize.</param>
 		/// <returns>Sanitized text.</returns>
-        public static string Sanitize(string text)
-        {
-            return sanitizeKeyRegex.Replace(text, "_");
-        }
-
-        #region Extensions
-
-        public static string GetEntry(this BaseUnityPlugin plugin, string key, string defaultValue = "")
-        {
-            return GetEntry(key, defaultValue, MetadataHelper.GetMetadata(plugin).GUID);
-        }
-
-        public static void SetEntry(this BaseUnityPlugin plugin, string key, string value)
-        {
-            SetEntry(key, value, MetadataHelper.GetMetadata(plugin).GUID);
-        }
-
-        public static bool HasEntry(this BaseUnityPlugin plugin, string key)
-        {
-            return HasEntry(key, MetadataHelper.GetMetadata(plugin).GUID);
-        }
-        #endregion Extensions
-    }
-}
+		public static string Sanitize(string text)
+		{
+			return sanitizeKeyRegex.Replace(text, "_");
+		}
+
+		#region Extensions
+
+		public static string GetEntry(this BaseUnityPlugin plugin, string key, string defaultValue = "")
+		{
+			return GetEntry(key, defaultValue, MetadataHelper.GetMetadata(plugin).GUID);
+		}
+
+		public static void SetEntry(this BaseUnityPlugin plugin, string key, string value)
+		{
+			SetEntry(key, value, MetadataHelper.GetMetadata(plugin).GUID);
+		}
+
+		public static bool HasEntry(this BaseUnityPlugin plugin, string key)
+		{
+			return HasEntry(key, MetadataHelper.GetMetadata(plugin).GUID);
+		}
+
+		#endregion Extensions
+	}
+}

+ 164 - 166
BepInEx/ConfigWrapper.cs

@@ -4,169 +4,167 @@ using BepInEx.Logging;
 
 namespace BepInEx
 {
-    public interface IConfigConverter<T>
-    {
-        string ConvertToString(T value);
-        T ConvertFromString(string str);
-    }
-    
-    public class ConfigWrapper<T>
-    {
-        private readonly Func<string, T> _strToObj;
-        private readonly Func<T, string> _objToStr;
-        private readonly string _defaultStr;
-        private readonly T _default;
-        private T _lastValue;
-        private bool _lastValueSet;
-
-        public string Key { get; protected set; }
-
-        public string Section { get; protected set; }
-
-        public T Value
-        {
-            get { return GetValue(); }
-            set { SetValue(value); }
-        }
-
-        public ConfigWrapper(string key, T @default = default(T))
-        {
-            var cvt = TypeDescriptor.GetConverter(typeof(T));
-
-            if (!cvt.CanConvertFrom(typeof(string)))
-                throw new ArgumentException("Default TypeConverter can't convert from String");
-
-            if (!cvt.CanConvertTo(typeof(string)))
-                throw new ArgumentException("Default TypeConverter can't convert to String");
-
-            _strToObj = (str) => (T)cvt.ConvertFromInvariantString(str);
-            _objToStr = (obj) => cvt.ConvertToInvariantString(obj);
-
-            _defaultStr = _objToStr(@default);
-            _default = @default;
-            Key = key;
-        }
-
-        public ConfigWrapper(string key, Func<string, T> strToObj, Func<T, string> objToStr, T @default = default(T))
-        {
-            if (objToStr == null)
-                throw new ArgumentNullException("objToStr");
-
-            if (strToObj == null)
-                throw new ArgumentNullException("strToObj");
-
-            _strToObj = strToObj;
-            _objToStr = objToStr;
-
-            _defaultStr = _objToStr(@default);
-            Key = key;
-        }
-
-        public ConfigWrapper(string key, IConfigConverter<T> converter, T @default = default(T))
-            : this(key, converter.ConvertFromString, converter.ConvertToString, @default)
-        {
-
-        }
-
-
-        public ConfigWrapper(string key, BaseUnityPlugin plugin, T @default = default(T))
-            : this(key, @default)
-        {
-            Section = MetadataHelper.GetMetadata(plugin).GUID;
-        }
-
-        public ConfigWrapper(string key, BaseUnityPlugin plugin, Func<string, T> strToObj, Func<T, string> objToStr, T @default = default(T))
-          : this(key, strToObj, objToStr, @default)
-        {
-            Section = MetadataHelper.GetMetadata(plugin).GUID;
-        }
-
-        public ConfigWrapper(string key, BaseUnityPlugin plugin, IConfigConverter<T> converter, T @default = default(T))
-          : this(key, converter.ConvertFromString, converter.ConvertToString, @default)
-        {
-            Section = MetadataHelper.GetMetadata(plugin).GUID;
-        }
-
-        public ConfigWrapper(string key, string section, T @default = default(T))
-            : this(key, @default)
-        {
-            Section = section;
-        }
-
-        public ConfigWrapper(string key, string section, Func<string, T> strToObj, Func<T, string> objToStr, T @default = default(T))
-           : this(key, strToObj, objToStr, @default)
-        {
-            Section = section;
-        }
-
-        public ConfigWrapper(string key, string section, IConfigConverter<T> converter, T @default = default(T))
-           : this(key, converter.ConvertFromString, converter.ConvertToString, @default)
-        {
-            Section = section;
-        }
-
-        protected virtual bool GetKeyExists()
-        {
-            return Config.HasEntry(Key, Section);
-        }
-
-        protected virtual T GetValue()
-        {
-            try
-            {
-                var strVal = Config.GetEntry(Key, _defaultStr, Section);
-                var obj = _strToObj(strVal);
-
-                // Always update in case config was changed from outside
-                _lastValue = obj;
-                _lastValueSet = true;
-
-                return obj;
-            }
-            catch (Exception ex)
-            {
-                Logger.Log(LogLevel.Error, "ConfigWrapper Get Converter Exception: " + ex.Message);
-                return _default;
-            }
-        }
-
-        protected virtual void SetValue(T value)
-        {
-            try
-            {
-                // Always write just in case config was changed from outside
-                var strVal = _objToStr(value);
-                Config.SetEntry(Key, strVal, Section);
-
-                if (_lastValueSet && Equals(_lastValue, value)) return;
-
-                _lastValue = value;
-                _lastValueSet = true;
-
-                OnSettingChanged();
-            }
-            catch (Exception ex)
-            {
-                Logger.Log(LogLevel.Error, "ConfigWrapper Set Converter Exception: " + ex.Message);
-            }
-        }
-
-        public void Clear()
-        {
-            Config.UnsetEntry(Key, Section);
-
-            _lastValueSet = false;
-            OnSettingChanged();
-        }
-
-        /// <summary>
-        /// Fired when the setting is changed. Does not detect changes made outside from this object.
-        /// </summary>
-        public event EventHandler SettingChanged;
-
-        private void OnSettingChanged()
-        {
-            SettingChanged?.Invoke(this, EventArgs.Empty);
-        }
-    }
-}
+	public interface IConfigConverter<T>
+	{
+		string ConvertToString(T value);
+		T ConvertFromString(string str);
+	}
+
+	public class ConfigWrapper<T>
+	{
+		private readonly Func<string, T> _strToObj;
+		private readonly Func<T, string> _objToStr;
+		private readonly string _defaultStr;
+		private readonly T _default;
+		private T _lastValue;
+		private bool _lastValueSet;
+
+		public string Key { get; protected set; }
+
+		public string Section { get; protected set; }
+
+		public T Value
+		{
+			get { return GetValue(); }
+			set { SetValue(value); }
+		}
+
+		public ConfigWrapper(string key, T @default = default(T))
+		{
+			var cvt = TypeDescriptor.GetConverter(typeof(T));
+
+			if (!cvt.CanConvertFrom(typeof(string)))
+				throw new ArgumentException("Default TypeConverter can't convert from String");
+
+			if (!cvt.CanConvertTo(typeof(string)))
+				throw new ArgumentException("Default TypeConverter can't convert to String");
+
+			_strToObj = (str) => (T)cvt.ConvertFromInvariantString(str);
+			_objToStr = (obj) => cvt.ConvertToInvariantString(obj);
+
+			_defaultStr = _objToStr(@default);
+			_default = @default;
+			Key = key;
+		}
+
+		public ConfigWrapper(string key, Func<string, T> strToObj, Func<T, string> objToStr, T @default = default(T))
+		{
+			if (objToStr == null)
+				throw new ArgumentNullException("objToStr");
+
+			if (strToObj == null)
+				throw new ArgumentNullException("strToObj");
+
+			_strToObj = strToObj;
+			_objToStr = objToStr;
+
+			_defaultStr = _objToStr(@default);
+			Key = key;
+		}
+
+		public ConfigWrapper(string key, IConfigConverter<T> converter, T @default = default(T))
+			: this(key, converter.ConvertFromString, converter.ConvertToString, @default) { }
+
+
+		public ConfigWrapper(string key, BaseUnityPlugin plugin, T @default = default(T))
+			: this(key, @default)
+		{
+			Section = MetadataHelper.GetMetadata(plugin).GUID;
+		}
+
+		public ConfigWrapper(string key, BaseUnityPlugin plugin, Func<string, T> strToObj, Func<T, string> objToStr, T @default = default(T))
+			: this(key, strToObj, objToStr, @default)
+		{
+			Section = MetadataHelper.GetMetadata(plugin).GUID;
+		}
+
+		public ConfigWrapper(string key, BaseUnityPlugin plugin, IConfigConverter<T> converter, T @default = default(T))
+			: this(key, converter.ConvertFromString, converter.ConvertToString, @default)
+		{
+			Section = MetadataHelper.GetMetadata(plugin).GUID;
+		}
+
+		public ConfigWrapper(string key, string section, T @default = default(T))
+			: this(key, @default)
+		{
+			Section = section;
+		}
+
+		public ConfigWrapper(string key, string section, Func<string, T> strToObj, Func<T, string> objToStr, T @default = default(T))
+			: this(key, strToObj, objToStr, @default)
+		{
+			Section = section;
+		}
+
+		public ConfigWrapper(string key, string section, IConfigConverter<T> converter, T @default = default(T))
+			: this(key, converter.ConvertFromString, converter.ConvertToString, @default)
+		{
+			Section = section;
+		}
+
+		protected virtual bool GetKeyExists()
+		{
+			return Config.HasEntry(Key, Section);
+		}
+
+		protected virtual T GetValue()
+		{
+			try
+			{
+				var strVal = Config.GetEntry(Key, _defaultStr, Section);
+				var obj = _strToObj(strVal);
+
+				// Always update in case config was changed from outside
+				_lastValue = obj;
+				_lastValueSet = true;
+
+				return obj;
+			}
+			catch (Exception ex)
+			{
+				Logger.Log(LogLevel.Error, "ConfigWrapper Get Converter Exception: " + ex.Message);
+				return _default;
+			}
+		}
+
+		protected virtual void SetValue(T value)
+		{
+			try
+			{
+				// Always write just in case config was changed from outside
+				var strVal = _objToStr(value);
+				Config.SetEntry(Key, strVal, Section);
+
+				if (_lastValueSet && Equals(_lastValue, value))
+					return;
+
+				_lastValue = value;
+				_lastValueSet = true;
+
+				OnSettingChanged();
+			}
+			catch (Exception ex)
+			{
+				Logger.Log(LogLevel.Error, "ConfigWrapper Set Converter Exception: " + ex.Message);
+			}
+		}
+
+		public void Clear()
+		{
+			Config.UnsetEntry(Key, Section);
+
+			_lastValueSet = false;
+			OnSettingChanged();
+		}
+
+		/// <summary>
+		/// Fired when the setting is changed. Does not detect changes made outside from this object.
+		/// </summary>
+		public event EventHandler SettingChanged;
+
+		private void OnSettingChanged()
+		{
+			SettingChanged?.Invoke(this, EventArgs.Empty);
+		}
+	}
+}

+ 54 - 54
BepInEx/ConsoleUtil/ConsoleEncoding/ConsoleEncoding.Buffers.cs

@@ -5,57 +5,57 @@
 
 namespace UnityInjector.ConsoleUtil
 {
-    // --------------------------------------------------
-    // Code ported from
-    // https://gist.github.com/asm256/9bfb88336a1433e2328a
-    // Which in turn was seemingly ported from
-    // http://jonskeet.uk/csharp/ebcdic/
-    // using only safe (managed) code
-    // --------------------------------------------------
-
-    partial class ConsoleEncoding
-    {
-        private byte[] _byteBuffer = new byte[256];
-        private char[] _charBuffer = new char[256];
-        private byte[] _zeroByte = new byte[0];
-        private char[] _zeroChar = new char[0];
-
-        private void ExpandByteBuffer(int count)
-        {
-            if (_byteBuffer.Length < count)
-                _byteBuffer = new byte[count];
-        }
-
-        private void ExpandCharBuffer(int count)
-        {
-            if (_charBuffer.Length < count)
-                _charBuffer = new char[count];
-        }
-
-        private void ReadByteBuffer(byte[] bytes, int index, int count)
-        {
-            for (int i = 0; i < count; i++)
-                bytes[index + i] = _byteBuffer[i];
-        }
-
-        private void ReadCharBuffer(char[] chars, int index, int count)
-        {
-            for (int i = 0; i < count; i++)
-                chars[index + i] = _charBuffer[i];
-        }
-
-        private void WriteByteBuffer(byte[] bytes, int index, int count)
-        {
-            ExpandByteBuffer(count);
-            for (int i = 0; i < count; i++)
-                _byteBuffer[i] = bytes[index + i];
-        }
-
-        private void WriteCharBuffer(char[] chars, int index, int count)
-        {
-            ExpandCharBuffer(count);
-            for (int i = 0; i < count; i++)
-                _charBuffer[i] = chars[index + i];
-        }
-    }
-}
+	// --------------------------------------------------
+	// Code ported from
+	// https://gist.github.com/asm256/9bfb88336a1433e2328a
+	// Which in turn was seemingly ported from
+	// http://jonskeet.uk/csharp/ebcdic/
+	// using only safe (managed) code
+	// --------------------------------------------------
+
+	partial class ConsoleEncoding
+	{
+		private byte[] _byteBuffer = new byte[256];
+		private char[] _charBuffer = new char[256];
+		private byte[] _zeroByte = new byte[0];
+		private char[] _zeroChar = new char[0];
+
+		private void ExpandByteBuffer(int count)
+		{
+			if (_byteBuffer.Length < count)
+				_byteBuffer = new byte[count];
+		}
+
+		private void ExpandCharBuffer(int count)
+		{
+			if (_charBuffer.Length < count)
+				_charBuffer = new char[count];
+		}
+
+		private void ReadByteBuffer(byte[] bytes, int index, int count)
+		{
+			for (int i = 0; i < count; i++)
+				bytes[index + i] = _byteBuffer[i];
+		}
+
+		private void ReadCharBuffer(char[] chars, int index, int count)
+		{
+			for (int i = 0; i < count; i++)
+				chars[index + i] = _charBuffer[i];
+		}
+
+		private void WriteByteBuffer(byte[] bytes, int index, int count)
+		{
+			ExpandByteBuffer(count);
+			for (int i = 0; i < count; i++)
+				_byteBuffer[i] = bytes[index + i];
+		}
+
+		private void WriteCharBuffer(char[] chars, int index, int count)
+		{
+			ExpandCharBuffer(count);
+			for (int i = 0; i < count; i++)
+				_charBuffer[i] = chars[index + i];
+		}
+	}
+}

+ 37 - 35
BepInEx/ConsoleUtil/ConsoleEncoding/ConsoleEncoding.PInvoke.cs

@@ -8,42 +8,44 @@ using System.Runtime.InteropServices;
 
 namespace UnityInjector.ConsoleUtil
 {
-    // --------------------------------------------------
-    // Code ported from
-    // https://gist.github.com/asm256/9bfb88336a1433e2328a
-    // Which in turn was seemingly ported from
-    // http://jonskeet.uk/csharp/ebcdic/
-    // using only safe (managed) code
-    // --------------------------------------------------
-    partial class ConsoleEncoding
-    {
-        [DllImport("kernel32.dll")]
-        private static extern uint GetConsoleOutputCP();
+	// --------------------------------------------------
+	// Code ported from
+	// https://gist.github.com/asm256/9bfb88336a1433e2328a
+	// Which in turn was seemingly ported from
+	// http://jonskeet.uk/csharp/ebcdic/
+	// using only safe (managed) code
+	// --------------------------------------------------
+	partial class ConsoleEncoding
+	{
+		[DllImport("kernel32.dll")]
+		private static extern uint GetConsoleOutputCP();
 
-        [DllImport("kernel32.dll")]
-        private static extern uint GetACP();
+		[DllImport("kernel32.dll")]
+		private static extern uint GetACP();
 
-        [DllImport("kernel32.dll", SetLastError = true)]
-        private static extern int MultiByteToWideChar(
-            uint codePage,
-            uint dwFlags,
-            [In, MarshalAs(UnmanagedType.LPArray)] byte[] lpMultiByteStr,
-            int cbMultiByte,
-            [Out, MarshalAs(UnmanagedType.LPArray)] char[] lpWideCharStr,
-            int cchWideChar);
+		[DllImport("kernel32.dll", SetLastError = true)]
+		private static extern int MultiByteToWideChar(
+			uint codePage,
+			uint dwFlags,
+			[In, MarshalAs(UnmanagedType.LPArray)] byte[] lpMultiByteStr,
+			int cbMultiByte,
+			[Out, MarshalAs(UnmanagedType.LPArray)]
+			char[] lpWideCharStr,
+			int cchWideChar);
 
-        [DllImport("kernel32.dll")]
-        private static extern IntPtr SetConsoleOutputCP(uint codepage);
+		[DllImport("kernel32.dll")]
+		private static extern IntPtr SetConsoleOutputCP(uint codepage);
 
-        [DllImport("kernel32.dll", SetLastError = true)]
-        private static extern int WideCharToMultiByte(
-            uint codePage,
-            uint dwFlags,
-            [In, MarshalAs(UnmanagedType.LPArray)] char[] lpWideCharStr,
-            int cchWideChar,
-            [Out, MarshalAs(UnmanagedType.LPArray)] byte[] lpMultiByteStr,
-            int cbMultiByte,
-            IntPtr lpDefaultChar,
-            IntPtr lpUsedDefaultChar);
-    }
-}
+		[DllImport("kernel32.dll", SetLastError = true)]
+		private static extern int WideCharToMultiByte(
+			uint codePage,
+			uint dwFlags,
+			[In, MarshalAs(UnmanagedType.LPArray)] char[] lpWideCharStr,
+			int cchWideChar,
+			[Out, MarshalAs(UnmanagedType.LPArray)]
+			byte[] lpMultiByteStr,
+			int cbMultiByte,
+			IntPtr lpDefaultChar,
+			IntPtr lpUsedDefaultChar);
+	}
+}

+ 77 - 77
BepInEx/ConsoleUtil/ConsoleEncoding/ConsoleEncoding.cs

@@ -8,80 +8,80 @@ using System.Text;
 
 namespace UnityInjector.ConsoleUtil
 {
-    // --------------------------------------------------
-    // Code ported from
-    // https://gist.github.com/asm256/9bfb88336a1433e2328a
-    // Which in turn was seemingly ported from
-    // http://jonskeet.uk/csharp/ebcdic/
-    // using only safe (managed) code
-    // --------------------------------------------------
-    public partial class ConsoleEncoding : Encoding
-    {
-        private readonly uint _codePage;
-        public override int CodePage => (int) _codePage;
-
-        public static uint ConsoleCodePage
-        {
-            get { return GetConsoleOutputCP(); }
-            set { SetConsoleOutputCP(value); }
-        }
-
-        public static uint GetActiveCodePage()
-        {
-            return GetACP();
-        }
-
-        private ConsoleEncoding(uint codePage)
-        {
-            _codePage = codePage;
-        }
-
-        public static ConsoleEncoding GetEncoding(uint codePage)
-        {
-            return new ConsoleEncoding(codePage);
-        }
-
-        public override int GetByteCount(char[] chars, int index, int count)
-        {
-            WriteCharBuffer(chars, index, count);
-            int result = WideCharToMultiByte(_codePage, 0, _charBuffer, count, _zeroByte, 0, IntPtr.Zero, IntPtr.Zero);
-            return result;
-        }
-
-        public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
-        {
-            var byteCount = GetMaxByteCount(charCount);
-
-            WriteCharBuffer(chars, charIndex, charCount);
-
-            ExpandByteBuffer(byteCount);
-            int result = WideCharToMultiByte(_codePage, 0, chars, charCount, _byteBuffer, byteCount, IntPtr.Zero, IntPtr.Zero);
-            ReadByteBuffer(bytes, byteIndex, byteCount);
-
-            return result;
-        }
-
-        public override int GetCharCount(byte[] bytes, int index, int count)
-        {
-            WriteByteBuffer(bytes, index, count);
-            int result = MultiByteToWideChar(_codePage, 0, bytes, count, _zeroChar, 0);
-            return result;
-        }
-
-        public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
-        {
-            var charCount = GetMaxCharCount(byteCount);
-
-            WriteByteBuffer(bytes, byteIndex, byteCount);
-
-            ExpandCharBuffer(charCount);
-            int result = MultiByteToWideChar(_codePage, 0, bytes, byteCount, _charBuffer, charCount);
-            ReadCharBuffer(chars, charIndex, charCount);
-
-            return result;
-        }
-
-        public override int GetMaxByteCount(int charCount) => charCount * 2;
-        public override int GetMaxCharCount(int byteCount) => byteCount;
-    }
-}
+	// --------------------------------------------------
+	// Code ported from
+	// https://gist.github.com/asm256/9bfb88336a1433e2328a
+	// Which in turn was seemingly ported from
+	// http://jonskeet.uk/csharp/ebcdic/
+	// using only safe (managed) code
+	// --------------------------------------------------
+	public partial class ConsoleEncoding : Encoding
+	{
+		private readonly uint _codePage;
+		public override int CodePage => (int)_codePage;
+
+		public static uint ConsoleCodePage
+		{
+			get { return GetConsoleOutputCP(); }
+			set { SetConsoleOutputCP(value); }
+		}
+
+		public static uint GetActiveCodePage()
+		{
+			return GetACP();
+		}
+
+		private ConsoleEncoding(uint codePage)
+		{
+			_codePage = codePage;
+		}
+
+		public static ConsoleEncoding GetEncoding(uint codePage)
+		{
+			return new ConsoleEncoding(codePage);
+		}
+
+		public override int GetByteCount(char[] chars, int index, int count)
+		{
+			WriteCharBuffer(chars, index, count);
+			int result = WideCharToMultiByte(_codePage, 0, _charBuffer, count, _zeroByte, 0, IntPtr.Zero, IntPtr.Zero);
+			return result;
+		}
+
+		public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
+		{
+			var byteCount = GetMaxByteCount(charCount);
+
+			WriteCharBuffer(chars, charIndex, charCount);
+
+			ExpandByteBuffer(byteCount);
+			int result = WideCharToMultiByte(_codePage, 0, chars, charCount, _byteBuffer, byteCount, IntPtr.Zero, IntPtr.Zero);
+			ReadByteBuffer(bytes, byteIndex, byteCount);
+
+			return result;
+		}
+
+		public override int GetCharCount(byte[] bytes, int index, int count)
+		{
+			WriteByteBuffer(bytes, index, count);
+			int result = MultiByteToWideChar(_codePage, 0, bytes, count, _zeroChar, 0);
+			return result;
+		}
+
+		public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
+		{
+			var charCount = GetMaxCharCount(byteCount);
+
+			WriteByteBuffer(bytes, byteIndex, byteCount);
+
+			ExpandCharBuffer(charCount);
+			int result = MultiByteToWideChar(_codePage, 0, bytes, byteCount, _charBuffer, charCount);
+			ReadCharBuffer(chars, charIndex, charCount);
+
+			return result;
+		}
+
+		public override int GetMaxByteCount(int charCount) => charCount * 2;
+		public override int GetMaxCharCount(int byteCount) => byteCount;
+	}
+}

+ 125 - 124
BepInEx/ConsoleUtil/ConsoleWindow.cs

@@ -10,128 +10,129 @@ using System.Text;
 
 namespace UnityInjector.ConsoleUtil
 {
-    public class ConsoleWindow
-    {
-        public static bool IsAttatched { get; private set; }
-        private static IntPtr _cOut;
-        private static IntPtr _oOut;
-
-        public static void Attach()
-        {
-            if (IsAttatched)
-                return;
-
-            if (_oOut == IntPtr.Zero)
-                _oOut = GetStdHandle(-11);
-
-            // Store Current Window
-            IntPtr currWnd = GetForegroundWindow();
-
-            //Check for existing console before allocating
-            if (GetConsoleWindow() == IntPtr.Zero)
-                if (!AllocConsole())
-                    throw new Exception("AllocConsole() failed");
-
-            // Restore Foreground
-            SetForegroundWindow(currWnd);
-
-            _cOut = CreateFile("CONOUT$", 0x80000000 | 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero);
-            BepInEx.ConsoleUtil.Kon.conOut = _cOut;
-
-            if (!SetStdHandle(-11, _cOut))
-                throw new Exception("SetStdHandle() failed");
-            Init();
-
-	        IsAttatched = true;
-        }
-
-        public static string Title
-        {
-            set
-            {
-	            if (!IsAttatched)
-		            return;
-
-	            if (value == null)
-	            {
-		            throw new ArgumentNullException(nameof(value));
-	            }
-	            if (value.Length > 24500)
-	            {
-		            throw new InvalidOperationException("Console title too long");
-	            }
-
-	            if (!SetConsoleTitle(value))
-	            {
-		            throw new InvalidOperationException("Console title invalid");
-	            }
-            }
-        }
-
-        public static void Detach()
-        {
-            if (!IsAttatched)
-                return;
-
-            if (!CloseHandle(_cOut))
-                throw new Exception("CloseHandle() failed");
-            _cOut = IntPtr.Zero;
-            if (!FreeConsole())
-                throw new Exception("FreeConsole() failed");
-            if (!SetStdHandle(-11, _oOut))
-                throw new Exception("SetStdHandle() failed");
-            Init();
-
-	        IsAttatched = false;
-        }
-
-        [DllImport("user32.dll")]
-        private static extern IntPtr GetForegroundWindow();
-
-        [DllImport("user32.dll")]
-        [return: MarshalAs(UnmanagedType.Bool)]
-        static extern bool SetForegroundWindow(IntPtr hWnd);
-
-        [DllImport("kernel32.dll", SetLastError = true)]
-        private static extern bool AllocConsole();
-
-        [DllImport("kernel32.dll")]
-        private static extern IntPtr GetConsoleWindow();
-
-        [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
-        private static extern bool CloseHandle(IntPtr handle);
-
-        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
-        private static extern IntPtr CreateFile(
-            string fileName,
-            uint desiredAccess,
-            int shareMode,
-            IntPtr securityAttributes,
-            int creationDisposition,
-            int flagsAndAttributes,
-            IntPtr templateFile);
-
-        [DllImport("kernel32.dll", SetLastError = false)]
-        private static extern bool FreeConsole();
-
-        [DllImport("kernel32.dll", SetLastError = true)]
-        private static extern IntPtr GetStdHandle(int nStdHandle);
-
-        private static void Init()
-        {
-            var stdOut = Console.OpenStandardOutput();
-            var stdWriter = new StreamWriter(stdOut, Encoding.Default)
-            {
-                AutoFlush = true
-            };
-            Console.SetOut(stdWriter);
-            Console.SetError(stdWriter);
-        }
-
-        [DllImport("kernel32.dll", SetLastError = true)]
-        private static extern bool SetStdHandle(int nStdHandle, IntPtr hConsoleOutput);
-        
-        [DllImport("kernel32.dll", BestFitMapping = true, CharSet = CharSet.Auto, SetLastError = true)]
-        private static extern bool SetConsoleTitle(string title);
-    }
+	public class ConsoleWindow
+	{
+		public static bool IsAttatched { get; private set; }
+		private static IntPtr _cOut;
+		private static IntPtr _oOut;
+
+		public static void Attach()
+		{
+			if (IsAttatched)
+				return;
+
+			if (_oOut == IntPtr.Zero)
+				_oOut = GetStdHandle(-11);
+
+			// Store Current Window
+			IntPtr currWnd = GetForegroundWindow();
+
+			//Check for existing console before allocating
+			if (GetConsoleWindow() == IntPtr.Zero)
+				if (!AllocConsole())
+					throw new Exception("AllocConsole() failed");
+
+			// Restore Foreground
+			SetForegroundWindow(currWnd);
+
+			_cOut = CreateFile("CONOUT$", 0x80000000 | 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero);
+			BepInEx.ConsoleUtil.Kon.conOut = _cOut;
+
+			if (!SetStdHandle(-11, _cOut))
+				throw new Exception("SetStdHandle() failed");
+			Init();
+
+			IsAttatched = true;
+		}
+
+		public static string Title
+		{
+			set
+			{
+				if (!IsAttatched)
+					return;
+
+				if (value == null)
+				{
+					throw new ArgumentNullException(nameof(value));
+				}
+
+				if (value.Length > 24500)
+				{
+					throw new InvalidOperationException("Console title too long");
+				}
+
+				if (!SetConsoleTitle(value))
+				{
+					throw new InvalidOperationException("Console title invalid");
+				}
+			}
+		}
+
+		public static void Detach()
+		{
+			if (!IsAttatched)
+				return;
+
+			if (!CloseHandle(_cOut))
+				throw new Exception("CloseHandle() failed");
+			_cOut = IntPtr.Zero;
+			if (!FreeConsole())
+				throw new Exception("FreeConsole() failed");
+			if (!SetStdHandle(-11, _oOut))
+				throw new Exception("SetStdHandle() failed");
+			Init();
+
+			IsAttatched = false;
+		}
+
+		[DllImport("user32.dll")]
+		private static extern IntPtr GetForegroundWindow();
+
+		[DllImport("user32.dll")]
+		[return: MarshalAs(UnmanagedType.Bool)]
+		static extern bool SetForegroundWindow(IntPtr hWnd);
+
+		[DllImport("kernel32.dll", SetLastError = true)]
+		private static extern bool AllocConsole();
+
+		[DllImport("kernel32.dll")]
+		private static extern IntPtr GetConsoleWindow();
+
+		[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
+		private static extern bool CloseHandle(IntPtr handle);
+
+		[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+		private static extern IntPtr CreateFile(
+			string fileName,
+			uint desiredAccess,
+			int shareMode,
+			IntPtr securityAttributes,
+			int creationDisposition,
+			int flagsAndAttributes,
+			IntPtr templateFile);
+
+		[DllImport("kernel32.dll", SetLastError = false)]
+		private static extern bool FreeConsole();
+
+		[DllImport("kernel32.dll", SetLastError = true)]
+		private static extern IntPtr GetStdHandle(int nStdHandle);
+
+		private static void Init()
+		{
+			var stdOut = Console.OpenStandardOutput();
+			var stdWriter = new StreamWriter(stdOut, Encoding.Default)
+			{
+				AutoFlush = true
+			};
+			Console.SetOut(stdWriter);
+			Console.SetError(stdWriter);
+		}
+
+		[DllImport("kernel32.dll", SetLastError = true)]
+		private static extern bool SetStdHandle(int nStdHandle, IntPtr hConsoleOutput);
+
+		[DllImport("kernel32.dll", BestFitMapping = true, CharSet = CharSet.Auto, SetLastError = true)]
+		private static extern bool SetConsoleTitle(string title);
+	}
 }

+ 138 - 146
BepInEx/ConsoleUtil/Kon.cs

@@ -4,149 +4,141 @@ using System.Security.Permissions;
 
 namespace BepInEx.ConsoleUtil
 {
-    internal class Kon
-    {
-        #region pinvoke
-
-        [DllImport("kernel32.dll", SetLastError = true)]
-        private static extern bool GetConsoleScreenBufferInfo(IntPtr hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
-
-        [DllImport("kernel32.dll", SetLastError = true)]
-        private static extern bool SetConsoleTextAttribute(IntPtr hConsoleOutput, short attributes);
-
-        [DllImport("kernel32.dll", SetLastError = true)]
-        private static extern IntPtr GetStdHandle(int nStdHandle);
-
-        #endregion
-
-        #region Types
-
-        private struct CONSOLE_SCREEN_BUFFER_INFO
-        {
-            internal COORD dwSize;
-            internal COORD dwCursorPosition;
-            internal short wAttributes;
-            internal SMALL_RECT srWindow;
-            internal COORD dwMaximumWindowSize;
-        }
-
-        private struct COORD
-        {
-            internal short X;
-            internal short Y;
-        }
-
-        private struct SMALL_RECT
-        {
-            internal short Left;
-            internal short Top;
-            internal short Right;
-            internal short Bottom;
-        }
-
-        private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
-
-        #endregion
-
-        #region Private
-
-        private static short ConsoleColorToColorAttribute(short color, bool isBackground)
-        {
-            if ((color & -16) != 0)
-                throw new ArgumentException("Arg_InvalidConsoleColor");
-            if (isBackground)
-                color <<= 4;
-            return color;
-        }
-
-        private static CONSOLE_SCREEN_BUFFER_INFO GetBufferInfo(bool throwOnNoConsole, out bool succeeded)
-        {
-            succeeded = false;
-            if (!(conOut == INVALID_HANDLE_VALUE))
-            {
-                CONSOLE_SCREEN_BUFFER_INFO console_SCREEN_BUFFER_INFO;
-                if (!GetConsoleScreenBufferInfo(conOut, out console_SCREEN_BUFFER_INFO))
-                {
-                    bool consoleScreenBufferInfo = GetConsoleScreenBufferInfo(GetStdHandle(-12), out console_SCREEN_BUFFER_INFO);
-                    if (!consoleScreenBufferInfo)
-                        consoleScreenBufferInfo = GetConsoleScreenBufferInfo(GetStdHandle(-10), out console_SCREEN_BUFFER_INFO);
-
-                    if (!consoleScreenBufferInfo)
-                        if (Marshal.GetLastWin32Error() == 6 && !throwOnNoConsole)
-                            return default(CONSOLE_SCREEN_BUFFER_INFO);
-                }
-                succeeded = true;
-                return console_SCREEN_BUFFER_INFO;
-            }
-            if (!throwOnNoConsole)
-                return default(CONSOLE_SCREEN_BUFFER_INFO);
-            throw new Exception("IO.IO_NoConsole");
-        }
-
-        private static void SetConsoleColor(bool isBackground, ConsoleColor c)
-        {
-            new UIPermission(UIPermissionWindow.SafeTopLevelWindows).Demand();
-            var color = ConsoleColorToColorAttribute((short)c, isBackground);
-            bool flag;
-            var bufferInfo = GetBufferInfo(false, out flag);
-            if (!flag) return;
-            var num = bufferInfo.wAttributes;
-            num &= (short)(isBackground ? -241 : -16);
-            num = (short)((ushort)num | (ushort)color);
-            SetConsoleTextAttribute(conOut, num);
-        }
-
-        private static ConsoleColor GetConsoleColor(bool isBackground)
-        {
-            bool flag;
-            var bufferInfo = GetBufferInfo(false, out flag);
-            if (!flag) return isBackground ? ConsoleColor.Black : ConsoleColor.Gray;
-            return ColorAttributeToConsoleColor((short)(bufferInfo.wAttributes & 240));
-        }
-
-        private static ConsoleColor ColorAttributeToConsoleColor(short c)
-        {
-            if ((short)(c & 255) != 0)
-                c >>= 4;
-            return (ConsoleColor)c;
-        }
-
-        internal static IntPtr conOut = IntPtr.Zero;
-
-        #endregion
-
-        #region Public
-
-        public static void ResetConsoleColor()
-        {
-            SetConsoleColor(true, ConsoleColor.Black);
-            SetConsoleColor(false, ConsoleColor.Gray);
-        }
-
-        public static ConsoleColor ForegroundColor
-        {
-            get
-            {
-                return GetConsoleColor(false);
-            }
-            set
-            {
-                SetConsoleColor(false, value);
-            }
-        }
-
-        public static ConsoleColor BackgroundColor
-        {
-            get
-            {
-                return GetConsoleColor(true);
-            }
-            set
-            {
-                SetConsoleColor(true, value);
-            }
-        }
-
-        #endregion
-    }
-}
+	internal class Kon
+	{
+		#region pinvoke
+
+		[DllImport("kernel32.dll", SetLastError = true)]
+		private static extern bool GetConsoleScreenBufferInfo(IntPtr hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
+
+		[DllImport("kernel32.dll", SetLastError = true)]
+		private static extern bool SetConsoleTextAttribute(IntPtr hConsoleOutput, short attributes);
+
+		[DllImport("kernel32.dll", SetLastError = true)]
+		private static extern IntPtr GetStdHandle(int nStdHandle);
+
+		#endregion
+
+		#region Types
+
+		private struct CONSOLE_SCREEN_BUFFER_INFO
+		{
+			internal COORD dwSize;
+			internal COORD dwCursorPosition;
+			internal short wAttributes;
+			internal SMALL_RECT srWindow;
+			internal COORD dwMaximumWindowSize;
+		}
+
+		private struct COORD
+		{
+			internal short X;
+			internal short Y;
+		}
+
+		private struct SMALL_RECT
+		{
+			internal short Left;
+			internal short Top;
+			internal short Right;
+			internal short Bottom;
+		}
+
+		private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
+
+		#endregion
+
+		#region Private
+
+		private static short ConsoleColorToColorAttribute(short color, bool isBackground)
+		{
+			if ((color & -16) != 0)
+				throw new ArgumentException("Arg_InvalidConsoleColor");
+			if (isBackground)
+				color <<= 4;
+			return color;
+		}
+
+		private static CONSOLE_SCREEN_BUFFER_INFO GetBufferInfo(bool throwOnNoConsole, out bool succeeded)
+		{
+			succeeded = false;
+			if (!(conOut == INVALID_HANDLE_VALUE))
+			{
+				CONSOLE_SCREEN_BUFFER_INFO console_SCREEN_BUFFER_INFO;
+				if (!GetConsoleScreenBufferInfo(conOut, out console_SCREEN_BUFFER_INFO))
+				{
+					bool consoleScreenBufferInfo = GetConsoleScreenBufferInfo(GetStdHandle(-12), out console_SCREEN_BUFFER_INFO);
+					if (!consoleScreenBufferInfo)
+						consoleScreenBufferInfo = GetConsoleScreenBufferInfo(GetStdHandle(-10), out console_SCREEN_BUFFER_INFO);
+
+					if (!consoleScreenBufferInfo)
+						if (Marshal.GetLastWin32Error() == 6 && !throwOnNoConsole)
+							return default(CONSOLE_SCREEN_BUFFER_INFO);
+				}
+
+				succeeded = true;
+				return console_SCREEN_BUFFER_INFO;
+			}
+
+			if (!throwOnNoConsole)
+				return default(CONSOLE_SCREEN_BUFFER_INFO);
+			throw new Exception("IO.IO_NoConsole");
+		}
+
+		private static void SetConsoleColor(bool isBackground, ConsoleColor c)
+		{
+			new UIPermission(UIPermissionWindow.SafeTopLevelWindows).Demand();
+			var color = ConsoleColorToColorAttribute((short)c, isBackground);
+			bool flag;
+			var bufferInfo = GetBufferInfo(false, out flag);
+			if (!flag)
+				return;
+			var num = bufferInfo.wAttributes;
+			num &= (short)(isBackground ? -241 : -16);
+			num = (short)((ushort)num | (ushort)color);
+			SetConsoleTextAttribute(conOut, num);
+		}
+
+		private static ConsoleColor GetConsoleColor(bool isBackground)
+		{
+			bool flag;
+			var bufferInfo = GetBufferInfo(false, out flag);
+			if (!flag)
+				return isBackground ? ConsoleColor.Black : ConsoleColor.Gray;
+			return ColorAttributeToConsoleColor((short)(bufferInfo.wAttributes & 240));
+		}
+
+		private static ConsoleColor ColorAttributeToConsoleColor(short c)
+		{
+			if ((short)(c & 255) != 0)
+				c >>= 4;
+			return (ConsoleColor)c;
+		}
+
+		internal static IntPtr conOut = IntPtr.Zero;
+
+		#endregion
+
+		#region Public
+
+		public static void ResetConsoleColor()
+		{
+			SetConsoleColor(true, ConsoleColor.Black);
+			SetConsoleColor(false, ConsoleColor.Gray);
+		}
+
+		public static ConsoleColor ForegroundColor
+		{
+			get { return GetConsoleColor(false); }
+			set { SetConsoleColor(false, value); }
+		}
+
+		public static ConsoleColor BackgroundColor
+		{
+			get { return GetConsoleColor(true); }
+			set { SetConsoleColor(true, value); }
+		}
+
+		#endregion
+	}
+}

+ 45 - 45
BepInEx/ConsoleUtil/SafeConsole.cs

@@ -8,59 +8,59 @@ using System.Reflection;
 
 namespace UnityInjector.ConsoleUtil
 {
-    public static class SafeConsole
-    {
-        private static GetColorDelegate _getBackgroundColor;
-        private static GetColorDelegate _getForegroundColor;
-        private static SetColorDelegate _setBackgroundColor;
-        private static SetColorDelegate _setForegroundColor;
+	public static class SafeConsole
+	{
+		private static GetColorDelegate _getBackgroundColor;
+		private static GetColorDelegate _getForegroundColor;
+		private static SetColorDelegate _setBackgroundColor;
+		private static SetColorDelegate _setForegroundColor;
 
-        public static ConsoleColor BackgroundColor
-        {
-            get { return _getBackgroundColor(); }
-            set { _setBackgroundColor(value); }
-        }
+		public static ConsoleColor BackgroundColor
+		{
+			get { return _getBackgroundColor(); }
+			set { _setBackgroundColor(value); }
+		}
 
-        public static ConsoleColor ForegroundColor
-        {
-            get { return _getForegroundColor(); }
-            set { _setForegroundColor(value); }
-        }
+		public static ConsoleColor ForegroundColor
+		{
+			get { return _getForegroundColor(); }
+			set { _setForegroundColor(value); }
+		}
 
-        static SafeConsole()
-        {
-            var tConsole = typeof(Console);
-            InitColors(tConsole);
-        }
+		static SafeConsole()
+		{
+			var tConsole = typeof(Console);
+			InitColors(tConsole);
+		}
 
-        private static void InitColors(Type tConsole)
-        {
-            const BindingFlags BINDING_FLAGS = BindingFlags.Public | BindingFlags.Static;
+		private static void InitColors(Type tConsole)
+		{
+			const BindingFlags BINDING_FLAGS = BindingFlags.Public | BindingFlags.Static;
 
-            var sfc = tConsole.GetMethod("set_ForegroundColor", BINDING_FLAGS);
-            var sbc = tConsole.GetMethod("set_BackgroundColor", BINDING_FLAGS);
-            var gfc = tConsole.GetMethod("get_ForegroundColor", BINDING_FLAGS);
-            var gbc = tConsole.GetMethod("get_BackgroundColor", BINDING_FLAGS);
+			var sfc = tConsole.GetMethod("set_ForegroundColor", BINDING_FLAGS);
+			var sbc = tConsole.GetMethod("set_BackgroundColor", BINDING_FLAGS);
+			var gfc = tConsole.GetMethod("get_ForegroundColor", BINDING_FLAGS);
+			var gbc = tConsole.GetMethod("get_BackgroundColor", BINDING_FLAGS);
 
-            _setForegroundColor = sfc != null
-                                      ? (SetColorDelegate) Delegate.CreateDelegate(typeof(SetColorDelegate), sfc)
-                                      : (value => {});
+			_setForegroundColor = sfc != null
+				? (SetColorDelegate)Delegate.CreateDelegate(typeof(SetColorDelegate), sfc)
+				: (value => { });
 
-            _setBackgroundColor = sbc != null
-                                      ? (SetColorDelegate) Delegate.CreateDelegate(typeof(SetColorDelegate), sbc)
-                                      : (value => {});
+			_setBackgroundColor = sbc != null
+				? (SetColorDelegate)Delegate.CreateDelegate(typeof(SetColorDelegate), sbc)
+				: (value => { });
 
-            _getForegroundColor = gfc != null
-                                      ? (GetColorDelegate) Delegate.CreateDelegate(typeof(GetColorDelegate), gfc)
-                                      : (() => ConsoleColor.Gray);
+			_getForegroundColor = gfc != null
+				? (GetColorDelegate)Delegate.CreateDelegate(typeof(GetColorDelegate), gfc)
+				: (() => ConsoleColor.Gray);
 
-            _getBackgroundColor = gbc != null
-                                      ? (GetColorDelegate) Delegate.CreateDelegate(typeof(GetColorDelegate), gbc)
-                                      : (() => ConsoleColor.Black);
-        }
+			_getBackgroundColor = gbc != null
+				? (GetColorDelegate)Delegate.CreateDelegate(typeof(GetColorDelegate), gbc)
+				: (() => ConsoleColor.Black);
+		}
 
-        private delegate ConsoleColor GetColorDelegate();
+		private delegate ConsoleColor GetColorDelegate();
 
-        private delegate void SetColorDelegate(ConsoleColor value);
-    }
-}
+		private delegate void SetColorDelegate(ConsoleColor value);
+	}
+}

+ 155 - 158
BepInEx/Contract/Attributes.cs

@@ -4,131 +4,131 @@ using System.Linq;
 
 namespace BepInEx
 {
-    #region BaseUnityPlugin
-
-    /// <summary>
-    /// This attribute denotes that a class is a plugin, and specifies the required metadata.
-    /// </summary>
-    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
-    public class BepInPlugin : Attribute
-    {
-        /// <summary>
-        /// The unique identifier of the plugin. Should not change between plugin versions.
-        /// </summary>
-        public string GUID { get; protected set; }
-
-        
-        /// <summary>
-        /// The user friendly name of the plugin. Is able to be changed between versions.
-        /// </summary>
-        public string Name { get; protected set; }
-
-        
-        /// <summary>
-        /// The specfic version of the plugin.
-        /// </summary>
-        public Version Version { get; protected set; }
-        
-        /// <param name="GUID">The unique identifier of the plugin. Should not change between plugin versions.</param>
-        /// <param name="Name">The user friendly name of the plugin. Is able to be changed between versions.</param>
-        /// <param name="Version">The specfic version of the plugin.</param>
-        public BepInPlugin(string GUID, string Name, string Version)
-        {
-            this.GUID = GUID;
-            this.Name = Name;
-            this.Version = new Version(Version);
-        }
-    }
-
-    /// <summary>
-    /// This attribute specifies any dependencies that this plugin has on other plugins.
-    /// </summary>
-    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
-    public class BepInDependency : Attribute
-    {
-        public enum DependencyFlags
-        {
-            /// <summary>
-            /// The plugin has a hard dependency on the referenced plugin, and will not run without it.
-            /// </summary>
-            HardDependency = 1,
-
-            /// <summary>
-            /// This plugin has a soft dependency on the referenced plugin, and is able to run without it.
-            /// </summary>
-            SoftDependency = 2,
-        }
-
-        /// <summary>
-        /// The GUID of the referenced plugin.
-        /// </summary>
-        public string DependencyGUID { get; protected set; }
-
-        /// <summary>
-        /// The flags associated with this dependency definition.
-        /// </summary>
-        public DependencyFlags Flags { get; protected set; }
-        
-        /// <param name="DependencyGUID">The GUID of the referenced plugin.</param>
-        /// <param name="Flags">The flags associated with this dependency definition.</param>
-        public BepInDependency(string DependencyGUID, DependencyFlags Flags = DependencyFlags.HardDependency)
-        {
-            this.DependencyGUID = DependencyGUID;
-            this.Flags = Flags;
-        }
-    }
-
-    /// <summary>
-    /// This attribute specifies which processes this plugin should be run for. Not specifying this attribute will load the plugin under every process.
-    /// </summary>
-    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
-    public class BepInProcess : Attribute
-    {
-        /// <summary>
-        /// The name of the process that this plugin will run under.
-        /// </summary>
-        public string ProcessName { get; protected set; }
-        
-        /// <param name="ProcessName">The name of the process that this plugin will run under.</param>
-        public BepInProcess(string ProcessName)
-        {
-            this.ProcessName = ProcessName;
-        }
-    }
-
-    #endregion
-
-    #region MetadataHelper
+	#region BaseUnityPlugin
+
+	/// <summary>
+	/// This attribute denotes that a class is a plugin, and specifies the required metadata.
+	/// </summary>
+	[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+	public class BepInPlugin : Attribute
+	{
+		/// <summary>
+		/// The unique identifier of the plugin. Should not change between plugin versions.
+		/// </summary>
+		public string GUID { get; protected set; }
+
+
+		/// <summary>
+		/// The user friendly name of the plugin. Is able to be changed between versions.
+		/// </summary>
+		public string Name { get; protected set; }
+
+
+		/// <summary>
+		/// The specfic version of the plugin.
+		/// </summary>
+		public Version Version { get; protected set; }
+
+		/// <param name="GUID">The unique identifier of the plugin. Should not change between plugin versions.</param>
+		/// <param name="Name">The user friendly name of the plugin. Is able to be changed between versions.</param>
+		/// <param name="Version">The specfic version of the plugin.</param>
+		public BepInPlugin(string GUID, string Name, string Version)
+		{
+			this.GUID = GUID;
+			this.Name = Name;
+			this.Version = new Version(Version);
+		}
+	}
+
+	/// <summary>
+	/// This attribute specifies any dependencies that this plugin has on other plugins.
+	/// </summary>
+	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+	public class BepInDependency : Attribute
+	{
+		public enum DependencyFlags
+		{
+			/// <summary>
+			/// The plugin has a hard dependency on the referenced plugin, and will not run without it.
+			/// </summary>
+			HardDependency = 1,
+
+			/// <summary>
+			/// This plugin has a soft dependency on the referenced plugin, and is able to run without it.
+			/// </summary>
+			SoftDependency = 2,
+		}
+
+		/// <summary>
+		/// The GUID of the referenced plugin.
+		/// </summary>
+		public string DependencyGUID { get; protected set; }
+
+		/// <summary>
+		/// The flags associated with this dependency definition.
+		/// </summary>
+		public DependencyFlags Flags { get; protected set; }
+
+		/// <param name="DependencyGUID">The GUID of the referenced plugin.</param>
+		/// <param name="Flags">The flags associated with this dependency definition.</param>
+		public BepInDependency(string DependencyGUID, DependencyFlags Flags = DependencyFlags.HardDependency)
+		{
+			this.DependencyGUID = DependencyGUID;
+			this.Flags = Flags;
+		}
+	}
+
+	/// <summary>
+	/// This attribute specifies which processes this plugin should be run for. Not specifying this attribute will load the plugin under every process.
+	/// </summary>
+	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+	public class BepInProcess : Attribute
+	{
+		/// <summary>
+		/// The name of the process that this plugin will run under.
+		/// </summary>
+		public string ProcessName { get; protected set; }
+
+		/// <param name="ProcessName">The name of the process that this plugin will run under.</param>
+		public BepInProcess(string ProcessName)
+		{
+			this.ProcessName = ProcessName;
+		}
+	}
+
+	#endregion
+
+	#region MetadataHelper
 
 	/// <summary>
 	/// Helper class to use for retrieving metadata about a plugin, defined as attributes.
 	/// </summary>
-    public static class MetadataHelper
-    {
+	public static class MetadataHelper
+	{
 		/// <summary>
 		/// Retrieves the BepInPlugin metadata from a plugin instance.
 		/// </summary>
 		/// <param name="plugin">The plugin instance.</param>
 		/// <returns>The BepInPlugin metadata of the plugin instance.</returns>
-        public static BepInPlugin GetMetadata(object plugin)
-        {
-            return GetMetadata(plugin.GetType());
-        }
-		
-	    /// <summary>
-	    /// Retrieves the BepInPlugin metadata from a plugin type.
-	    /// </summary>
-	    /// <param name="plugin">The plugin type.</param>
-	    /// <returns>The BepInPlugin metadata of the plugin type.</returns>
-        public static BepInPlugin GetMetadata(Type pluginType)
-        {
-            object[] attributes = pluginType.GetCustomAttributes(typeof(BepInPlugin), false);
-
-            if (attributes.Length == 0)
-                return null;
-
-            return (BepInPlugin)attributes[0];
-        }
+		public static BepInPlugin GetMetadata(object plugin)
+		{
+			return GetMetadata(plugin.GetType());
+		}
+
+		/// <summary>
+		/// Retrieves the BepInPlugin metadata from a plugin type.
+		/// </summary>
+		/// <param name="plugin">The plugin type.</param>
+		/// <returns>The BepInPlugin metadata of the plugin type.</returns>
+		public static BepInPlugin GetMetadata(Type pluginType)
+		{
+			object[] attributes = pluginType.GetCustomAttributes(typeof(BepInPlugin), false);
+
+			if (attributes.Length == 0)
+				return null;
+
+			return (BepInPlugin)attributes[0];
+		}
 
 		/// <summary>
 		/// Gets the specified attributes of an instance, if they exist.
@@ -136,21 +136,21 @@ namespace BepInEx
 		/// <typeparam name="T">The attribute type to retrieve.</typeparam>
 		/// <param name="plugin">The plugin instance.</param>
 		/// <returns>The attributes of the instance, if existing.</returns>
-        public static IEnumerable<T> GetAttributes<T>(object plugin) where T : Attribute
-        {
-            return GetAttributes<T>(plugin.GetType());
-        }
-		
-	    /// <summary>
-	    /// Gets the specified attributes of a type, if they exist.
-	    /// </summary>
-	    /// <typeparam name="T">The attribute type to retrieve.</typeparam>
-	    /// <param name="plugin">The plugin type.</param>
-	    /// <returns>The attributes of the type, if existing.</returns>
-        public static IEnumerable<T> GetAttributes<T>(Type pluginType) where T : Attribute
-        {
-            return pluginType.GetCustomAttributes(typeof(T), true).Cast<T>();
-        }
+		public static IEnumerable<T> GetAttributes<T>(object plugin) where T : Attribute
+		{
+			return GetAttributes<T>(plugin.GetType());
+		}
+
+		/// <summary>
+		/// Gets the specified attributes of a type, if they exist.
+		/// </summary>
+		/// <typeparam name="T">The attribute type to retrieve.</typeparam>
+		/// <param name="plugin">The plugin type.</param>
+		/// <returns>The attributes of the type, if existing.</returns>
+		public static IEnumerable<T> GetAttributes<T>(Type pluginType) where T : Attribute
+		{
+			return pluginType.GetCustomAttributes(typeof(T), true).Cast<T>();
+		}
 
 		/// <summary>
 		/// Retrieves the dependencies of the specified plugin type.
@@ -158,44 +158,41 @@ namespace BepInEx
 		/// <param name="Plugin">The plugin type.</param>
 		/// <param name="AllPlugins">All currently loaded plugin types.</param>
 		/// <returns>A list of all plugin types that the specified plugin type depends upon.</returns>
-        public static IEnumerable<Type> GetDependencies(Type Plugin, IEnumerable<Type> AllPlugins)
-        {
-            object[] attributes = Plugin.GetCustomAttributes(typeof(BepInDependency), true);
+		public static IEnumerable<Type> GetDependencies(Type Plugin, IEnumerable<Type> AllPlugins)
+		{
+			object[] attributes = Plugin.GetCustomAttributes(typeof(BepInDependency), true);
+
+			List<Type> dependencyTypes = new List<Type>();
 
-            List<Type> dependencyTypes = new List<Type>();
+			foreach (BepInDependency dependency in attributes)
+			{
+				Type dependencyType = AllPlugins.FirstOrDefault(x => GetMetadata(x)?.GUID == dependency.DependencyGUID);
 
-            foreach (BepInDependency dependency in attributes)
-            {
-                Type dependencyType = AllPlugins.FirstOrDefault(x => GetMetadata(x)?.GUID == dependency.DependencyGUID);
+				if (dependencyType == null)
+				{
+					if ((dependency.Flags & BepInDependency.DependencyFlags.SoftDependency) != 0)
+						continue; //skip on soft dependencies
 
-                if (dependencyType == null)
-                {
-                    if ((dependency.Flags & BepInDependency.DependencyFlags.SoftDependency) != 0)
-                        continue; //skip on soft dependencies
+					throw new MissingDependencyException("Cannot find dependency type.");
+				}
 
-                    throw new MissingDependencyException("Cannot find dependency type.");
-                }
-                    
 
-                dependencyTypes.Add(dependencyType);
-            }
+				dependencyTypes.Add(dependencyType);
+			}
 
-            return dependencyTypes;
-        }
-    }
+			return dependencyTypes;
+		}
+	}
 
 	/// <summary>
 	/// An exception which is thrown when a plugin's dependencies cannot be found.
 	/// </summary>
-    public class MissingDependencyException : Exception
-    {
-        public MissingDependencyException(string message) : base(message)
-        {
-
-        }
-    }
+	public class MissingDependencyException : Exception
+	{
+		public MissingDependencyException(string message) : base(message) { }
+	}
 
-    #endregion
+	#endregion
 
 	#region Build configuration
 

+ 4 - 7
BepInEx/Contract/BaseUnityPlugin.cs

@@ -2,11 +2,8 @@
 
 namespace BepInEx
 {
-    /// <summary>
-    /// The base plugin type that is used by the BepInEx plugin loader.
-    /// </summary>
-    public abstract class BaseUnityPlugin : MonoBehaviour
-    {
-
-    }
+	/// <summary>
+	/// The base plugin type that is used by the BepInEx plugin loader.
+	/// </summary>
+	public abstract class BaseUnityPlugin : MonoBehaviour { }
 }

+ 56 - 56
BepInEx/Logging/BaseLogger.cs

@@ -7,93 +7,93 @@ namespace BepInEx.Logging
 	/// <summary>
 	/// The base implementation of a logging class.
 	/// </summary>
-    public abstract class BaseLogger : TextWriter
-    {
-	    protected BaseLogger()
-	    {
-		    DisplayedLevels = GetDefaultLogLevel();
-	    }
+	public abstract class BaseLogger : TextWriter
+	{
+		protected BaseLogger()
+		{
+			DisplayedLevels = GetDefaultLogLevel();
+		}
 
 		/// <summary>
 		/// The encoding that the underlying text writer should use. Defaults to UTF-8 BOM.
 		/// </summary>
-        public override Encoding Encoding { get; } = new UTF8Encoding(true);
+		public override Encoding Encoding { get; } = new UTF8Encoding(true);
 
 
-        /// <summary>
-        /// The handler for a entry logged event.
-        /// </summary>
-        /// <param name="entry">The text element of the log itself.</param>
-        /// <param name="show">Whether or not it should be dislpayed to the user.</param>
-        public delegate void EntryLoggedEventHandler(LogLevel level, object entry);
+		/// <summary>
+		/// The handler for a entry logged event.
+		/// </summary>
+		/// <param name="entry">The text element of the log itself.</param>
+		/// <param name="show">Whether or not it should be dislpayed to the user.</param>
+		public delegate void EntryLoggedEventHandler(LogLevel level, object entry);
 
-        /// <summary>
-        /// The listener event for an entry being logged.
-        /// </summary>
-        public event EntryLoggedEventHandler EntryLogged;
+		/// <summary>
+		/// The listener event for an entry being logged.
+		/// </summary>
+		public event EntryLoggedEventHandler EntryLogged;
 
 		/// <summary>
 		/// Retrieves the default log level to use for this logger.
 		/// </summary>
 		/// <returns>The default log level to use.</returns>
-	    protected virtual LogLevel GetDefaultLogLevel()
+		protected virtual LogLevel GetDefaultLogLevel()
 		{
 			LogLevel lowest;
 
-		    try
-		    {
-			    lowest = (LogLevel)Enum.Parse(typeof(LogLevel), Config.GetEntry("logger-displayed-levels", nameof(LogLevel.Info), "BepInEx"));
-		    }
-		    catch
-		    {
-			    return LogLevel.All;
-		    }
+			try
+			{
+				lowest = (LogLevel)Enum.Parse(typeof(LogLevel), Config.GetEntry("logger-displayed-levels", nameof(LogLevel.Info), "BepInEx"));
+			}
+			catch
+			{
+				return LogLevel.All;
+			}
 
-		    if (lowest == LogLevel.None)
-			    return LogLevel.None;
+			if (lowest == LogLevel.None)
+				return LogLevel.None;
 
-		    LogLevel value = lowest;
+			LogLevel value = lowest;
 
-		    foreach (LogLevel e in Enum.GetValues(typeof(LogLevel)))
-		    {
-			    if (lowest > e)
-				    value |= e;
-		    }
+			foreach (LogLevel e in Enum.GetValues(typeof(LogLevel)))
+			{
+				if (lowest > e)
+					value |= e;
+			}
 
-		    return value;
-	    }
+			return value;
+		}
 
-        /// <summary>
+		/// <summary>
 		/// A filter which is used to specify which log levels are not ignored by the logger.
 		/// </summary>
-        public LogLevel DisplayedLevels { get; set; }
+		public LogLevel DisplayedLevels { get; set; }
 
-        private readonly object logLockObj = new object();
+		private readonly object logLockObj = new object();
 
 		/// <summary>
 		/// Logs an entry to the Logger instance.
 		/// </summary>
 		/// <param name="level">The level of the entry.</param>
 		/// <param name="entry">The textual value of the entry.</param>
-        public virtual void Log(LogLevel level, object entry)
-        {
-            if ((DisplayedLevels & level) != LogLevel.None)
-            {
-                lock (logLockObj)
-                {
-                    EntryLogged?.Invoke(level, entry);
-                    WriteLine($"[{level.GetHighestLevel()}] {entry}");
-                }
-            }
-        }
+		public virtual void Log(LogLevel level, object entry)
+		{
+			if ((DisplayedLevels & level) != LogLevel.None)
+			{
+				lock (logLockObj)
+				{
+					EntryLogged?.Invoke(level, entry);
+					WriteLine($"[{level.GetHighestLevel()}] {entry}");
+				}
+			}
+		}
 
 		/// <summary>
 		/// Logs an entry to the Logger instance, with a <see cref="LogLevel"/> of Message.
 		/// </summary>
 		/// <param name="entry">The text value of this log entry.</param>
-        public virtual void Log(object entry)
-        {
-            Log(LogLevel.Message, entry);
-        }
-    }
-}
+		public virtual void Log(object entry)
+		{
+			Log(LogLevel.Message, entry);
+		}
+	}
+}

+ 47 - 47
BepInEx/Logging/LogLevel.cs

@@ -5,96 +5,96 @@ namespace BepInEx.Logging
 	/// <summary>
 	/// The level, or severity of a log entry.
 	/// </summary>
-    [Flags]
-    public enum LogLevel
-    {
+	[Flags]
+	public enum LogLevel
+	{
 		/// <summary>
 		/// No level selected.
 		/// </summary>
-        None = 0,
+		None = 0,
 
 		/// <summary>
 		/// A fatal error has occurred, which cannot be recovered from.
 		/// </summary>
-        Fatal = 1,
+		Fatal = 1,
 
 		/// <summary>
 		/// An error has occured, but can be recovered from.
 		/// </summary>
-        Error = 2,
+		Error = 2,
 
 		/// <summary>
 		/// A warning has been produced, but does not necessarily mean that something wrong has happened.
 		/// </summary>
-        Warning = 4,
+		Warning = 4,
 
 		/// <summary>
 		/// An important message that should be displayed to the user.
 		/// </summary>
-        Message = 8,
+		Message = 8,
 
 		/// <summary>
 		/// A message of low importance.
 		/// </summary>
-        Info = 16,
+		Info = 16,
 
 		/// <summary>
 		/// A message that would likely only interest a developer.
 		/// </summary>
-        Debug = 32,
+		Debug = 32,
 
 		/// <summary>
 		/// All log levels.
 		/// </summary>
-        All = Fatal | Error | Warning | Message | Info | Debug
-    }
+		All = Fatal | Error | Warning | Message | Info | Debug
+	}
 
-    public static class LogLevelExtensions
-    {
+	public static class LogLevelExtensions
+	{
 		/// <summary>
 		/// Gets the highest log level when there could potentially be multiple levels provided.
 		/// </summary>
 		/// <param name="levels">The log level(s).</param>
 		/// <returns>The highest log level supplied.</returns>
-        public static LogLevel GetHighestLevel(this LogLevel levels)
-        {
-            var enums = Enum.GetValues(typeof(LogLevel));
-            Array.Sort(enums);
+		public static LogLevel GetHighestLevel(this LogLevel levels)
+		{
+			var enums = Enum.GetValues(typeof(LogLevel));
+			Array.Sort(enums);
 
-            foreach (LogLevel e in enums)
-            {
-                if ((levels & e) != LogLevel.None)
-                    return e;
-            }
+			foreach (LogLevel e in enums)
+			{
+				if ((levels & e) != LogLevel.None)
+					return e;
+			}
 
-            return LogLevel.None;
-        }
+			return LogLevel.None;
+		}
 
 		/// <summary>
 		/// Returns a translation of a log level to it's associated console colour.
 		/// </summary>
 		/// <param name="level">The log level(s).</param>
 		/// <returns>A console color associated with the highest log level supplied.</returns>
-        public static ConsoleColor GetConsoleColor(this LogLevel level)
-        {
-            level = GetHighestLevel(level);
+		public static ConsoleColor GetConsoleColor(this LogLevel level)
+		{
+			level = GetHighestLevel(level);
 
-            switch (level)
-            {
-                case LogLevel.Fatal:
-                    return ConsoleColor.Red;
-                case LogLevel.Error:
-                    return ConsoleColor.DarkRed;
-                case LogLevel.Warning:
-                    return ConsoleColor.Yellow;
-                case LogLevel.Message:
-                    return ConsoleColor.White;
-                case LogLevel.Info:
-                default:
-                    return ConsoleColor.Gray;
-                case LogLevel.Debug:
-                    return ConsoleColor.DarkGray;
-            }
-        }
-    }
-}
+			switch (level)
+			{
+				case LogLevel.Fatal:
+					return ConsoleColor.Red;
+				case LogLevel.Error:
+					return ConsoleColor.DarkRed;
+				case LogLevel.Warning:
+					return ConsoleColor.Yellow;
+				case LogLevel.Message:
+					return ConsoleColor.White;
+				case LogLevel.Info:
+				default:
+					return ConsoleColor.Gray;
+				case LogLevel.Debug:
+					return ConsoleColor.DarkGray;
+			}
+		}
+	}
+}

+ 19 - 19
BepInEx/Logging/Logger.cs

@@ -5,39 +5,39 @@ namespace BepInEx
 	/// <summary>
 	/// A static <see cref="BaseLogger"/> instance.
 	/// </summary>
-    public static class Logger
-    {
+	public static class Logger
+	{
 		/// <summary>
 		/// The current instance of a <see cref="BaseLogger"/> that is being used.
 		/// </summary>
-        public static BaseLogger CurrentLogger { get; set; }
+		public static BaseLogger CurrentLogger { get; set; }
 
-        /// <summary>
-        /// The listener event for an entry being logged.
-        /// </summary>
-        public static event BaseLogger.EntryLoggedEventHandler EntryLogged;
+		/// <summary>
+		/// The listener event for an entry being logged.
+		/// </summary>
+		public static event BaseLogger.EntryLoggedEventHandler EntryLogged;
 
 		/// <summary>
 		/// Logs an entry to the current logger instance.
 		/// </summary>
 		/// <param name="level">The level of the entry.</param>
 		/// <param name="entry">The textual value of the entry.</param>
-        public static void Log(LogLevel level, object entry)
-        {
-            EntryLogged?.Invoke(level, entry);
+		public static void Log(LogLevel level, object entry)
+		{
+			EntryLogged?.Invoke(level, entry);
 
-            CurrentLogger?.Log(level, entry);
-        }
+			CurrentLogger?.Log(level, entry);
+		}
 
 		/// <summary>
 		/// Sets the instance being used by the static <see cref="Logger"/> class.
 		/// </summary>
 		/// <param name="logger">The instance to use in the static class.</param>
-        public static void SetLogger(BaseLogger logger)
-        {
-            CurrentLogger?.Dispose();
+		public static void SetLogger(BaseLogger logger)
+		{
+			CurrentLogger?.Dispose();
 
-            CurrentLogger = logger;
-        }
-    }
-}
+			CurrentLogger = logger;
+		}
+	}
+}

+ 126 - 127
BepInEx/Logging/LoggerTraceListener.cs

@@ -10,142 +10,141 @@ namespace BepInEx.Logging
 	/// A trace listener that writes to an underlying <see cref="BaseLogger"/> instance.
 	/// </summary>
 	/// <inheritdoc cref="TraceListener"/>
-    public class LoggerTraceListener : TraceListener
-    {
+	public class LoggerTraceListener : TraceListener
+	{
 		/// <summary>
 		/// The logger instance that is being written to.
 		/// </summary>
-        public BaseLogger Logger { get; }
-
-        static LoggerTraceListener()
-        {
-            try
-            {
-                TraceFixer.ApplyFix();
-            }
-            catch { } //ignore everything, if it's thrown an exception, we're using an assembly that has already fixed this
-        }
-		
+		public BaseLogger Logger { get; }
+
+		static LoggerTraceListener()
+		{
+			try
+			{
+				TraceFixer.ApplyFix();
+			}
+			catch { } //ignore everything, if it's thrown an exception, we're using an assembly that has already fixed this
+		}
+
 		/// <param name="logger">The logger instance to write to.</param>
-        public LoggerTraceListener(BaseLogger logger)
-        {
-            Logger = logger;
-        }
-		
+		public LoggerTraceListener(BaseLogger logger)
+		{
+			Logger = logger;
+		}
+
 		/// <summary>
 		/// Writes a message to the underlying <see cref="BaseLogger"/> instance.
 		/// </summary>
 		/// <param name="message">The message to write.</param>
-        public override void Write(string message)
-        {
-            Logger.Write(message);
-        }
-		
-	    /// <summary>
-	    /// Writes a message and a newline to the underlying <see cref="BaseLogger"/> instance.
-	    /// </summary>
-	    /// <param name="message">The message to write.</param>
-        public override void WriteLine(string message)
-        {
-            Logger.WriteLine(message);
-        }
-
-        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
-            => TraceEvent(eventCache, source, eventType, id, string.Format(format, args));
-
-        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
-        {
-            LogLevel level;
-
-            switch (eventType)
-            {
-                case TraceEventType.Critical:
-                    level = LogLevel.Fatal;
-                    break;
-                case TraceEventType.Error:
-                    level = LogLevel.Error;
-                    break;
-                case TraceEventType.Warning:
-                    level = LogLevel.Warning;
-                    break;
-                case TraceEventType.Information:
-                    level = LogLevel.Info;
-                    break;
-                case TraceEventType.Verbose:
-                default:
-                    level = LogLevel.Debug;
-                    break;
-            }
-
-            Logger.Log(level, $"{source} : {message}");
-        }
+		public override void Write(string message)
+		{
+			Logger.Write(message);
+		}
+
+		/// <summary>
+		/// Writes a message and a newline to the underlying <see cref="BaseLogger"/> instance.
+		/// </summary>
+		/// <param name="message">The message to write.</param>
+		public override void WriteLine(string message)
+		{
+			Logger.WriteLine(message);
+		}
+
+		public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
+			=> TraceEvent(eventCache, source, eventType, id, string.Format(format, args));
+
+		public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
+		{
+			LogLevel level;
+
+			switch (eventType)
+			{
+				case TraceEventType.Critical:
+					level = LogLevel.Fatal;
+					break;
+				case TraceEventType.Error:
+					level = LogLevel.Error;
+					break;
+				case TraceEventType.Warning:
+					level = LogLevel.Warning;
+					break;
+				case TraceEventType.Information:
+					level = LogLevel.Info;
+					break;
+				case TraceEventType.Verbose:
+				default:
+					level = LogLevel.Debug;
+					break;
+			}
+
+			Logger.Log(level, $"{source} : {message}");
+		}
 
 		/// <summary>
 		/// This exists because the Mono implementation of <see cref="Trace"/> is/was broken, and would call Write directly instead of calling TraceEvent. This class fixes that with a <see cref="Harmony"/> hook.
 		/// </summary>
-        private static class TraceFixer
-        {
-            private static Type TraceImplType;
-            
-            private static object ListenersSyncRoot;
-            private static TraceListenerCollection Listeners;
-            private static PropertyInfo prop_AutoFlush;
-
-            private static bool AutoFlush => (bool)prop_AutoFlush.GetValue(null, null);
-
-
-            public static void ApplyFix()
-            {
-                TraceImplType = AppDomain.CurrentDomain.GetAssemblies()
-                    .First(x => x.GetName().Name == "System")
-                    .GetTypes()
-                    .First(x => x.Name == "TraceImpl");
-
-                
-                ListenersSyncRoot = AccessTools.Property(TraceImplType, "ListenersSyncRoot").GetValue(null, null);
-                
-                Listeners = (TraceListenerCollection)AccessTools.Property(TraceImplType, "Listeners").GetValue(null, null);
-                
-                prop_AutoFlush = AccessTools.Property(TraceImplType, "AutoFlush");
-
-
-
-                HarmonyInstance instance = HarmonyInstance.Create("com.bepis.bepinex.tracefix");
-
-                instance.Patch(
-                    typeof(Trace).GetMethod("DoTrace", BindingFlags.Static | BindingFlags.NonPublic),
-                    new HarmonyMethod(typeof(TraceFixer).GetMethod(nameof(DoTraceReplacement), BindingFlags.Static | BindingFlags.Public)),
-                    null);
-            }
-
-
-            public static bool DoTraceReplacement(string kind, Assembly report, string message)
-            {
-                string arg = string.Empty;
-                try
-                {
-                    arg = report.GetName().Name;
-                }
-                catch (MethodAccessException) { }
-
-                TraceEventType type = (TraceEventType)Enum.Parse(typeof(TraceEventType), kind);
-
-                lock (ListenersSyncRoot)
-                {
-                    foreach (object obj in Listeners)
-                    {
-                        TraceListener traceListener = (TraceListener)obj;
-                        traceListener.TraceEvent(new TraceEventCache(), arg, type, 0, message);
-
-                        if (AutoFlush)
-                        {
-                            traceListener.Flush();
-                        }
-                    }
-                }
-
-                return false;
-            }
-        }
-    }
-}
+		private static class TraceFixer
+		{
+			private static Type TraceImplType;
+
+			private static object ListenersSyncRoot;
+			private static TraceListenerCollection Listeners;
+			private static PropertyInfo prop_AutoFlush;
+
+			private static bool AutoFlush => (bool)prop_AutoFlush.GetValue(null, null);
+
+
+			public static void ApplyFix()
+			{
+				TraceImplType = AppDomain.CurrentDomain.GetAssemblies()
+										 .First(x => x.GetName().Name == "System")
+										 .GetTypes()
+										 .First(x => x.Name == "TraceImpl");
+
+
+				ListenersSyncRoot = AccessTools.Property(TraceImplType, "ListenersSyncRoot").GetValue(null, null);
+
+				Listeners = (TraceListenerCollection)AccessTools.Property(TraceImplType, "Listeners").GetValue(null, null);
+
+				prop_AutoFlush = AccessTools.Property(TraceImplType, "AutoFlush");
+
+
+				HarmonyInstance instance = HarmonyInstance.Create("com.bepis.bepinex.tracefix");
+
+				instance.Patch(
+					typeof(Trace).GetMethod("DoTrace", BindingFlags.Static | BindingFlags.NonPublic),
+					new HarmonyMethod(typeof(TraceFixer).GetMethod(nameof(DoTraceReplacement), BindingFlags.Static | BindingFlags.Public)),
+					null);
+			}
+
+
+			public static bool DoTraceReplacement(string kind, Assembly report, string message)
+			{
+				string arg = string.Empty;
+				try
+				{
+					arg = report.GetName().Name;
+				}
+				catch (MethodAccessException) { }
+
+				TraceEventType type = (TraceEventType)Enum.Parse(typeof(TraceEventType), kind);
+
+				lock (ListenersSyncRoot)
+				{
+					foreach (object obj in Listeners)
+					{
+						TraceListener traceListener = (TraceListener)obj;
+						traceListener.TraceEvent(new TraceEventCache(), arg, type, 0, message);
+
+						if (AutoFlush)
+						{
+							traceListener.Flush();
+						}
+					}
+				}
+
+				return false;
+			}
+		}
+	}
+}

+ 90 - 89
BepInEx/Logging/PreloaderLogWriter.cs

@@ -11,117 +11,118 @@ namespace BepInEx.Logging
 	/// A log writer specific to the <see cref="Preloader"/>.
 	/// </summary>
 	/// <inheritdoc cref="BaseLogger"/>
-    public class PreloaderLogWriter : BaseLogger
-    {
+	public class PreloaderLogWriter : BaseLogger
+	{
 		/// <summary>
 		/// The <see cref="System.Text.StringBuilder"/> which contains all logged entries, so it may be passed onto another log writer.
 		/// </summary>
-        public StringBuilder StringBuilder { get; protected set; } = new StringBuilder();
+		public StringBuilder StringBuilder { get; protected set; } = new StringBuilder();
 
 		/// <summary>
 		/// Whether or not the log writer is redirecting console output, so it can be logged.
 		/// </summary>
-        public bool IsRedirectingConsole { get; protected set; }
+		public bool IsRedirectingConsole { get; protected set; }
 
-        protected TextWriter stdout;
-        protected LoggerTraceListener traceListener;
+		protected TextWriter stdout;
+		protected LoggerTraceListener traceListener;
 
-        private bool _enabled = false;
+		private bool _enabled = false;
 
 		/// <summary>
 		/// Whether or not the log writer is writing and/or redirecting.
 		/// </summary>
-        public bool Enabled {
-            get => _enabled;
-            set
-            {
-                if (value)
-                    Enable();
-                else
-                    Disable();
-            }
-        }
-		
+		public bool Enabled
+		{
+			get => _enabled;
+			set
+			{
+				if (value)
+					Enable();
+				else
+					Disable();
+			}
+		}
+
 		/// <param name="redirectConsole">Whether or not to redirect the console standard output.</param>
-        public PreloaderLogWriter(bool redirectConsole)
-        {
-            IsRedirectingConsole = redirectConsole;
-            
-            stdout = Console.Out;
-            traceListener = new LoggerTraceListener(this);
-        }
+		public PreloaderLogWriter(bool redirectConsole)
+		{
+			IsRedirectingConsole = redirectConsole;
+
+			stdout = Console.Out;
+			traceListener = new LoggerTraceListener(this);
+		}
 
 		/// <summary>
 		/// Enables the log writer.
 		/// </summary>
-        public void Enable()
-        {
-            if (_enabled)
-                return;
+		public void Enable()
+		{
+			if (_enabled)
+				return;
 
-            if (IsRedirectingConsole)
-                Console.SetOut(this);
-            else
-                Console.SetOut(Null);
+			if (IsRedirectingConsole)
+				Console.SetOut(this);
+			else
+				Console.SetOut(Null);
 
-            Trace.Listeners.Add(traceListener);
+			Trace.Listeners.Add(traceListener);
 
-            _enabled = true;
-        }
+			_enabled = true;
+		}
 
 		/// <summary>
 		/// Disables the log writer.
 		/// </summary>
-        public void Disable()
-        {
-            if (!_enabled)
-                return;
-
-            Console.SetOut(stdout);
-
-            Trace.Listeners.Remove(traceListener);
-
-            _enabled = false;
-        }
-		
-	    /// <summary>
-	    /// Logs an entry to the Logger instance.
-	    /// </summary>
-	    /// <param name="level">The level of the entry.</param>
-	    /// <param name="entry">The textual value of the entry.</param>
-        public override void Log(LogLevel level, object entry)
-        {
-            Kon.ForegroundColor = level.GetConsoleColor();
-            base.Log(level, entry);
-            Kon.ForegroundColor = ConsoleColor.Gray;
-        }
-
-        public override void Write(char value)
-        {
-            StringBuilder.Append(value);
-
-            stdout.Write(value);
-        }
-
-        public override void Write(string value)
-        {
-            StringBuilder.Append(value);
-
-            stdout.Write(value);
-        }
-
-        protected override void Dispose(bool disposing)
-        {
-            Disable();
-            StringBuilder.Length = 0;
-            
-            traceListener?.Dispose();
-            traceListener = null;
-        }
-
-        public override string ToString()
-        {
-            return StringBuilder.ToString().Trim();
-        }
-    }
-}
+		public void Disable()
+		{
+			if (!_enabled)
+				return;
+
+			Console.SetOut(stdout);
+
+			Trace.Listeners.Remove(traceListener);
+
+			_enabled = false;
+		}
+
+		/// <summary>
+		/// Logs an entry to the Logger instance.
+		/// </summary>
+		/// <param name="level">The level of the entry.</param>
+		/// <param name="entry">The textual value of the entry.</param>
+		public override void Log(LogLevel level, object entry)
+		{
+			Kon.ForegroundColor = level.GetConsoleColor();
+			base.Log(level, entry);
+			Kon.ForegroundColor = ConsoleColor.Gray;
+		}
+
+		public override void Write(char value)
+		{
+			StringBuilder.Append(value);
+
+			stdout.Write(value);
+		}
+
+		public override void Write(string value)
+		{
+			StringBuilder.Append(value);
+
+			stdout.Write(value);
+		}
+
+		protected override void Dispose(bool disposing)
+		{
+			Disable();
+			StringBuilder.Length = 0;
+
+			traceListener?.Dispose();
+			traceListener = null;
+		}
+
+		public override string ToString()
+		{
+			return StringBuilder.ToString().Trim();
+		}
+	}
+}

+ 108 - 108
BepInEx/Logging/UnityLogWriter.cs

@@ -10,116 +10,116 @@ namespace BepInEx.Logging
 	/// <summary>
 	/// Logs entries using Unity specific outputs.
 	/// </summary>
-    public class UnityLogWriter : BaseLogger
+	public class UnityLogWriter : BaseLogger
 	{
-	    private static readonly Action<string> WriteStringToUnityLog;
-
-        [DllImport("mono.dll", EntryPoint = "mono_lookup_internal_call")]
-        private static extern IntPtr MonoLookupInternalCall(IntPtr gconstpointer);
-
-        static UnityLogWriter()
-        {
-            foreach (MethodInfo methodInfo in typeof(UnityEngine.UnityLogWriter).GetMethods(BindingFlags.Static | BindingFlags.Public))
-            {
-                if (MonoLookupInternalCall(methodInfo.MethodHandle.Value) == IntPtr.Zero)
-                    continue;
-
-                WriteStringToUnityLog = (Action<string>) Delegate.CreateDelegate(typeof(Action<string>), methodInfo);
-                break;
-            }
-        }
-
-        /// <summary>
-        /// Writes a string specifically to the game output log.
-        /// </summary>
-        /// <param name="value">The value to write.</param>
-        public void WriteToLog(string value)
-        {
-            WriteStringToUnityLog?.Invoke(value);
-        }
-
-        protected void InternalWrite(string value)
-        {
-            Console.Write(value);
-            WriteToLog(value);
-        }
-
-	    /// <summary>
-	    /// Logs an entry to the Logger instance.
-	    /// </summary>
-	    /// <param name="level">The level of the entry.</param>
-	    /// <param name="entry">The textual value of the entry.</param>
-        public override void Log(LogLevel level, object entry)
-        {
-            Kon.ForegroundColor = level.GetConsoleColor();
-            base.Log(level, entry);
-            Kon.ForegroundColor = ConsoleColor.Gray;
-
-            // If the display level got ignored, still write it to the log
-            if ((DisplayedLevels & level) == LogLevel.None)
-                WriteToLog($"[{level.GetHighestLevel()}] {entry}\r\n");
-        }
-
-        public override void WriteLine(string value) => InternalWrite($"{value}\r\n");
-        public override void Write(char value) => InternalWrite(value.ToString());
-        public override void Write(string value) => InternalWrite(value);
-
-        /// <summary>
-        /// Start listening to Unity's log message events and sending the messages to BepInEx logger.
-        /// </summary>
-        public static void ListenUnityLogs()
-        {
-            Type application = typeof(Application);
-			
-            EventInfo logEvent = application.GetEvent("logMessageReceived", BindingFlags.Public | BindingFlags.Static);
-            if (logEvent != null)
-            {
-                logEvent.AddEventHandler(null, new Application.LogCallback(OnUnityLogMessageReceived));
-            }
-            else
-            {
-                MethodInfo registerLogCallback = application.GetMethod("RegisterLogCallback", BindingFlags.Public | BindingFlags.Static);
-                registerLogCallback.Invoke(null, new object[] { new Application.LogCallback(OnUnityLogMessageReceived) });
-            }
-        }
-
-        private static void OnUnityLogMessageReceived(string message, string stackTrace, LogType type)
-        {
-            LogLevel logLevel = LogLevel.Message;
-
-            switch (type)
-            {
-                case LogType.Error:
-                case LogType.Assert:
-                case LogType.Exception:
-                    logLevel = LogLevel.Error;
-                    break;
-                case LogType.Warning:
-                    logLevel = LogLevel.Warning;
-                    break;
-                case LogType.Log:
-                default:
-                    logLevel = LogLevel.Info;
-                    break;
-            }
-
-            Logger.Log(logLevel, message);
-            if (type == LogType.Exception)
-            {
-                Logger.Log(logLevel, $"Stack trace:\n{stackTrace}");
-            }
-        }
-    }
+		private static readonly Action<string> WriteStringToUnityLog;
+
+		[DllImport("mono.dll", EntryPoint = "mono_lookup_internal_call")]
+		private static extern IntPtr MonoLookupInternalCall(IntPtr gconstpointer);
+
+		static UnityLogWriter()
+		{
+			foreach (MethodInfo methodInfo in typeof(UnityEngine.UnityLogWriter).GetMethods(BindingFlags.Static | BindingFlags.Public))
+			{
+				if (MonoLookupInternalCall(methodInfo.MethodHandle.Value) == IntPtr.Zero)
+					continue;
+
+				WriteStringToUnityLog = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), methodInfo);
+				break;
+			}
+		}
+
+		/// <summary>
+		/// Writes a string specifically to the game output log.
+		/// </summary>
+		/// <param name="value">The value to write.</param>
+		public void WriteToLog(string value)
+		{
+			WriteStringToUnityLog?.Invoke(value);
+		}
+
+		protected void InternalWrite(string value)
+		{
+			Console.Write(value);
+			WriteToLog(value);
+		}
+
+		/// <summary>
+		/// Logs an entry to the Logger instance.
+		/// </summary>
+		/// <param name="level">The level of the entry.</param>
+		/// <param name="entry">The textual value of the entry.</param>
+		public override void Log(LogLevel level, object entry)
+		{
+			Kon.ForegroundColor = level.GetConsoleColor();
+			base.Log(level, entry);
+			Kon.ForegroundColor = ConsoleColor.Gray;
+
+			// If the display level got ignored, still write it to the log
+			if ((DisplayedLevels & level) == LogLevel.None)
+				WriteToLog($"[{level.GetHighestLevel()}] {entry}\r\n");
+		}
+
+		public override void WriteLine(string value) => InternalWrite($"{value}\r\n");
+		public override void Write(char value) => InternalWrite(value.ToString());
+		public override void Write(string value) => InternalWrite(value);
+
+		/// <summary>
+		/// Start listening to Unity's log message events and sending the messages to BepInEx logger.
+		/// </summary>
+		public static void ListenUnityLogs()
+		{
+			Type application = typeof(Application);
+
+			EventInfo logEvent = application.GetEvent("logMessageReceived", BindingFlags.Public | BindingFlags.Static);
+			if (logEvent != null)
+			{
+				logEvent.AddEventHandler(null, new Application.LogCallback(OnUnityLogMessageReceived));
+			}
+			else
+			{
+				MethodInfo registerLogCallback = application.GetMethod("RegisterLogCallback", BindingFlags.Public | BindingFlags.Static);
+				registerLogCallback.Invoke(null, new object[] { new Application.LogCallback(OnUnityLogMessageReceived) });
+			}
+		}
+
+		private static void OnUnityLogMessageReceived(string message, string stackTrace, LogType type)
+		{
+			LogLevel logLevel = LogLevel.Message;
+
+			switch (type)
+			{
+				case LogType.Error:
+				case LogType.Assert:
+				case LogType.Exception:
+					logLevel = LogLevel.Error;
+					break;
+				case LogType.Warning:
+					logLevel = LogLevel.Warning;
+					break;
+				case LogType.Log:
+				default:
+					logLevel = LogLevel.Info;
+					break;
+			}
+
+			Logger.Log(logLevel, message);
+			if (type == LogType.Exception)
+			{
+				Logger.Log(logLevel, $"Stack trace:\n{stackTrace}");
+			}
+		}
+	}
 }
 
 namespace UnityEngine
 {
-    internal sealed class UnityLogWriter
-    {
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        public static extern void WriteStringToUnityLogImpl(string s);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        public static extern void WriteStringToUnityLog(string s);
-    }
-}
+	internal sealed class UnityLogWriter
+	{
+		[MethodImpl(MethodImplOptions.InternalCall)]
+		public static extern void WriteStringToUnityLogImpl(string s);
+
+		[MethodImpl(MethodImplOptions.InternalCall)]
+		public static extern void WriteStringToUnityLog(string s);
+	}
+}

+ 54 - 54
BepInEx/Paths.cs

@@ -4,66 +4,66 @@ using System.Reflection;
 
 namespace BepInEx
 {
-    /// <summary>
-    ///     Paths used by BepInEx
-    /// </summary>
-    public static class Paths
-    {
-        private static string executablePath;
+	/// <summary>
+	///     Paths used by BepInEx
+	/// </summary>
+	public static class Paths
+	{
+		private static string executablePath;
 
-        /// <summary>
-        ///     The directory that the core BepInEx DLLs reside in.
-        /// </summary>
-        public static string BepInExAssemblyDirectory { get; private set; }
+		/// <summary>
+		///     The directory that the core BepInEx DLLs reside in.
+		/// </summary>
+		public static string BepInExAssemblyDirectory { get; private set; }
 
-        /// <summary>
-        ///     The path to the core BepInEx DLL.
-        /// </summary>
-        public static string BepInExAssemblyPath { get; private set; }
+		/// <summary>
+		///     The path to the core BepInEx DLL.
+		/// </summary>
+		public static string BepInExAssemblyPath { get; private set; }
 
-        /// <summary>
-        ///     The path of the currently executing program BepInEx is encapsulated in.
-        /// </summary>
-        public static string ExecutablePath
-        {
-            get => executablePath;
-            internal set
-            {
-                executablePath = value;
-                ProcessName = Path.GetFileNameWithoutExtension(value);
-                GameRootPath = Path.GetDirectoryName(value);
-                ManagedPath = Utility.CombinePaths(GameRootPath, $"{ProcessName}_Data", "Managed");
-                PluginPath = Utility.CombinePaths(GameRootPath, "BepInEx");
-                PatcherPluginPath = Utility.CombinePaths(GameRootPath, "BepInEx", "patchers");
-                BepInExAssemblyDirectory = Utility.CombinePaths(GameRootPath, "BepInEx", "core");
-                BepInExAssemblyPath =
-                        Utility.CombinePaths(BepInExAssemblyDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.dll");
-            }
-        }
+		/// <summary>
+		///     The path of the currently executing program BepInEx is encapsulated in.
+		/// </summary>
+		public static string ExecutablePath
+		{
+			get => executablePath;
+			internal set
+			{
+				executablePath = value;
+				ProcessName = Path.GetFileNameWithoutExtension(value);
+				GameRootPath = Path.GetDirectoryName(value);
+				ManagedPath = Utility.CombinePaths(GameRootPath, $"{ProcessName}_Data", "Managed");
+				PluginPath = Utility.CombinePaths(GameRootPath, "BepInEx");
+				PatcherPluginPath = Utility.CombinePaths(GameRootPath, "BepInEx", "patchers");
+				BepInExAssemblyDirectory = Utility.CombinePaths(GameRootPath, "BepInEx", "core");
+				BepInExAssemblyPath =
+					Utility.CombinePaths(BepInExAssemblyDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.dll");
+			}
+		}
 
-        /// <summary>
-        ///     The directory that the currently executing process resides in.
-        /// </summary>
-        public static string GameRootPath { get; private set; }
+		/// <summary>
+		///     The directory that the currently executing process resides in.
+		/// </summary>
+		public static string GameRootPath { get; private set; }
 
-        /// <summary>
-        ///     The path to the Managed folder of the currently running Unity game.
-        /// </summary>
-        public static string ManagedPath { get; private set; }
+		/// <summary>
+		///     The path to the Managed folder of the currently running Unity game.
+		/// </summary>
+		public static string ManagedPath { get; private set; }
 
-        /// <summary>
-        ///     The path to the patcher plugin folder which resides in the BepInEx folder.
-        /// </summary>
-        public static string PatcherPluginPath { get; private set; }
+		/// <summary>
+		///     The path to the patcher plugin folder which resides in the BepInEx folder.
+		/// </summary>
+		public static string PatcherPluginPath { get; private set; }
 
-        /// <summary>
-        ///     The path to the main BepInEx folder.
-        /// </summary>
-        public static string PluginPath { get; private set; }
+		/// <summary>
+		///     The path to the main BepInEx folder.
+		/// </summary>
+		public static string PluginPath { get; private set; }
 
-        /// <summary>
-        ///     The name of the currently executing process.
-        /// </summary>
-        public static string ProcessName { get; private set; }
-    }
+		/// <summary>
+		///     The name of the currently executing process.
+		/// </summary>
+		public static string ProcessName { get; private set; }
+	}
 }

+ 1 - 1
BepInEx/Properties/AssemblyInfo.cs

@@ -37,4 +37,4 @@ using BepInEx;
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
 [assembly: AssemblyVersion("5.0.0.0")]
-[assembly: AssemblyFileVersion("5.0.0.0")]
+[assembly: AssemblyFileVersion("5.0.0.0")]

+ 110 - 110
BepInEx/Utility.cs

@@ -6,17 +6,17 @@ using System.Reflection;
 
 namespace BepInEx
 {
-    /// <summary>
-    /// Generic helper properties and methods.
-    /// </summary>
-    public static class Utility
-    {
-        /// <summary>
-        /// Combines multiple paths together, as the specfic method is not availble in .NET 3.5.
-        /// </summary>
-        /// <param name="parts">The multiple paths to combine together.</param>
-        /// <returns>A combined path.</returns>
-        public static string CombinePaths(params string[] parts) => parts.Aggregate(Path.Combine);
+	/// <summary>
+	/// Generic helper properties and methods.
+	/// </summary>
+	public static class Utility
+	{
+		/// <summary>
+		/// Combines multiple paths together, as the specfic method is not availble in .NET 3.5.
+		/// </summary>
+		/// <param name="parts">The multiple paths to combine together.</param>
+		/// <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.
@@ -24,107 +24,107 @@ namespace BepInEx
 		/// <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>
-        /// <param name="path">The file path to convert.</param>
-        /// <returns>A converted file path.</returns>
-        public static string ConvertToWWWFormat(string path)
-        {
-            return $"file://{path.Replace('\\', '/')}";
-        }
-
-        /// <summary>
-        /// Indicates whether a specified string is null, empty, or consists only of white-space characters.
-        /// </summary>
-        /// <param name="self">The string to test.</param>
-        /// <returns>True if the value parameter is null or empty, or if value consists exclusively of white-space characters.</returns>
-        public static bool IsNullOrWhiteSpace(this string self)
-        {
-            return self == null || self.Trim().Length == 0;
-        }
-
-        public static IEnumerable<TNode> TopologicalSort<TNode>(IEnumerable<TNode> nodes, Func<TNode, IEnumerable<TNode>> dependencySelector)
-        {
-		    List<TNode> sorted_list = new List<TNode>();
-
-		    HashSet<TNode> visited = new HashSet<TNode>();
-		    HashSet<TNode> sorted = new HashSet<TNode>();
-			
-	        foreach (TNode input in nodes)
-	        {
-		        Stack<TNode> currentStack = new Stack<TNode>();
-		        if (!Visit(input, currentStack))
-		        {
+		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>
+		/// <param name="path">The file path to convert.</param>
+		/// <returns>A converted file path.</returns>
+		public static string ConvertToWWWFormat(string path)
+		{
+			return $"file://{path.Replace('\\', '/')}";
+		}
+
+		/// <summary>
+		/// Indicates whether a specified string is null, empty, or consists only of white-space characters.
+		/// </summary>
+		/// <param name="self">The string to test.</param>
+		/// <returns>True if the value parameter is null or empty, or if value consists exclusively of white-space characters.</returns>
+		public static bool IsNullOrWhiteSpace(this string self)
+		{
+			return self == null || self.Trim().Length == 0;
+		}
+
+		public static IEnumerable<TNode> TopologicalSort<TNode>(IEnumerable<TNode> nodes, Func<TNode, IEnumerable<TNode>> dependencySelector)
+		{
+			List<TNode> sorted_list = new List<TNode>();
+
+			HashSet<TNode> visited = new HashSet<TNode>();
+			HashSet<TNode> sorted = new HashSet<TNode>();
+
+			foreach (TNode input in nodes)
+			{
+				Stack<TNode> currentStack = new Stack<TNode>();
+				if (!Visit(input, currentStack))
+				{
 					throw new Exception("Cyclic Dependency:\r\n" + currentStack
-						                    .Select(x => $" - {x}") //append dashes
-						                    .Aggregate((a, b) => $"{a}\r\n{b}")); //add new lines inbetween
-		        }
-	        }
-			    
-
-		    return sorted_list;
-
-		    bool Visit(TNode node, Stack<TNode> stack)
-		    {
-			    if (visited.Contains(node))
-			    {
-				    if (!sorted.Contains(node))
-				    {
-					    return false;
-				    }
-			    }
-			    else
-			    {
-				    visited.Add(node);
+																   .Select(x => $" - {x}") //append dashes
+																   .Aggregate((a, b) => $"{a}\r\n{b}")); //add new lines inbetween
+				}
+			}
+
+
+			return sorted_list;
+
+			bool Visit(TNode node, Stack<TNode> stack)
+			{
+				if (visited.Contains(node))
+				{
+					if (!sorted.Contains(node))
+					{
+						return false;
+					}
+				}
+				else
+				{
+					visited.Add(node);
 
 					stack.Push(node);
 
-				    foreach (var dep in dependencySelector(node))
-					    if (!Visit(dep, stack))
-						    return false;
-
-
-				    sorted.Add(node);
-				    sorted_list.Add(node);
-
-				    stack.Pop();
-			    }
-				
-			    return true;
-		    }
-        }
-
-        /// <summary>
-        /// Try to resolve and load the given assembly DLL.
-        /// </summary>
-        /// <param name="assemblyName">Name of the assembly, of the type <see cref="AssemblyName" />.</param>
-        /// <param name="directory">Directory to search the assembly from.</param>
-        /// <param name="assembly">The loaded assembly.</param>
-        /// <returns>True, if the assembly was found and loaded. Otherwise, false.</returns>
-        public static bool TryResolveDllAssembly(AssemblyName assemblyName, string directory, out Assembly assembly)
-        {
-            assembly = null;
-            string path = Path.Combine(directory, $"{assemblyName.Name}.dll");
-
-            if (!File.Exists(path))
-                return false;
-
-            try
-            {
-                assembly = Assembly.LoadFile(path);
-            }
-            catch (Exception)
-            {
-                return false;
-            }
-
-            return true;
-        }
-    }
+					foreach (var dep in dependencySelector(node))
+						if (!Visit(dep, stack))
+							return false;
+
+
+					sorted.Add(node);
+					sorted_list.Add(node);
+
+					stack.Pop();
+				}
+
+				return true;
+			}
+		}
+
+		/// <summary>
+		/// Try to resolve and load the given assembly DLL.
+		/// </summary>
+		/// <param name="assemblyName">Name of the assembly, of the type <see cref="AssemblyName" />.</param>
+		/// <param name="directory">Directory to search the assembly from.</param>
+		/// <param name="assembly">The loaded assembly.</param>
+		/// <returns>True, if the assembly was found and loaded. Otherwise, false.</returns>
+		public static bool TryResolveDllAssembly(AssemblyName assemblyName, string directory, out Assembly assembly)
+		{
+			assembly = null;
+			string path = Path.Combine(directory, $"{assemblyName.Name}.dll");
+
+			if (!File.Exists(path))
+				return false;
+
+			try
+			{
+				assembly = Assembly.LoadFile(path);
+			}
+			catch (Exception)
+			{
+				return false;
+			}
+
+			return true;
+		}
+	}
 }