소스 검색

Merge pull request #14 from bbepis/unstable

Merge v3 unstable
Bepis 6 년 전
부모
커밋
7bc400b633

+ 14 - 11
BepInEx.Patcher/BepInEx.Patcher.csproj

@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="..\packages\MSBuild.ILMerge.Task.1.0.5\build\MSBuild.ILMerge.Task.props" Condition="Exists('..\packages\MSBuild.ILMerge.Task.1.0.5\build\MSBuild.ILMerge.Task.props')" />
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -21,7 +20,7 @@
     <DebugSymbols>true</DebugSymbols>
     <DebugType>full</DebugType>
     <Optimize>false</Optimize>
-    <OutputPath>K:\</OutputPath>
+    <OutputPath>..\bin\</OutputPath>
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
@@ -31,15 +30,18 @@
     <PlatformTarget>AnyCPU</PlatformTarget>
     <DebugType>pdbonly</DebugType>
     <Optimize>true</Optimize>
-    <OutputPath>K:\</OutputPath>
+    <OutputPath>..\bin\</OutputPath>
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <Prefer32Bit>false</Prefer32Bit>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
-      <HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath>
+    <Reference Include="Costura, Version=2.0.0.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
+      <HintPath>..\packages\Costura.Fody.2.0.0\lib\net452\Costura.dll</HintPath>
+    </Reference>
+    <Reference Include="Mono.Cecil, Version=0.10.0.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
+      <HintPath>..\packages\Mono.Cecil.0.10.0\lib\net40\Mono.Cecil.dll</HintPath>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
@@ -53,12 +55,12 @@
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>
-    <None Include="ILMerge.props" />
-    <None Include="ILMergeConfig.json" />
     <None Include="packages.config" />
   </ItemGroup>
   <ItemGroup>
-    <Content Include="ILMergeOrder.txt" />
+    <Content Include="FodyWeavers.xml">
+      <SubType>Designer</SubType>
+    </Content>
   </ItemGroup>
   <Import Project="..\BepInEx.Common\BepInEx.Common.projitems" Label="Shared" />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
@@ -66,8 +68,9 @@
     <PropertyGroup>
       <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
     </PropertyGroup>
-    <Error Condition="!Exists('..\packages\MSBuild.ILMerge.Task.1.0.5\build\MSBuild.ILMerge.Task.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSBuild.ILMerge.Task.1.0.5\build\MSBuild.ILMerge.Task.props'))" />
-    <Error Condition="!Exists('..\packages\MSBuild.ILMerge.Task.1.0.5\build\MSBuild.ILMerge.Task.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSBuild.ILMerge.Task.1.0.5\build\MSBuild.ILMerge.Task.targets'))" />
+    <Error Condition="!Exists('..\packages\Costura.Fody.2.0.0\build\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.2.0.0\build\Costura.Fody.targets'))" />
+    <Error Condition="!Exists('..\packages\Fody.3.0.3\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.3.0.3\build\Fody.targets'))" />
   </Target>
-  <Import Project="..\packages\MSBuild.ILMerge.Task.1.0.5\build\MSBuild.ILMerge.Task.targets" Condition="Exists('..\packages\MSBuild.ILMerge.Task.1.0.5\build\MSBuild.ILMerge.Task.targets')" />
+  <Import Project="..\packages\Costura.Fody.2.0.0\build\Costura.Fody.targets" Condition="Exists('..\packages\Costura.Fody.2.0.0\build\Costura.Fody.targets')" />
+  <Import Project="..\packages\Fody.3.0.3\build\Fody.targets" Condition="Exists('..\packages\Fody.3.0.3\build\Fody.targets')" />
 </Project>

+ 4 - 0
BepInEx.Patcher/FodyWeavers.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Weavers>
+  <Costura />
+</Weavers>

+ 0 - 67
BepInEx.Patcher/ILMerge.props

@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <!--                                                                                   -->
-    <!-- ILMerge project-specific settings. Almost never need to be set explicitly.       -->
-    <!-- for details, see http://research.microsoft.com/en-us/people/mbarnett/ilmerge.aspx -->
-    <!--                                                                                   -->
-    <!-- *** set this file to Type=None, CopyToOutput=Never ***                            -->
-
-    <!-- If True, all copy local dependencies will also be merged from referenced projects whether they are referenced in the current project explicitly or not -->
-    <ILMergeTransitive>false</ILMergeTransitive>
-
-    <!-- Extra ILMerge library paths (semicolon-separated). Dont put your package dependencies here, they will be added automagically -->
-    <ILMergeLibraryPath></ILMergeLibraryPath>
-
-    <!-- The solution NuGet package directory if not standard 'SOLUTION\packages' -->
-    <ILMergePackagesPath></ILMergePackagesPath>
-
-    <!-- The merge order file name if differs from standard 'ILMergeOrder.txt' -->
-    <ILMergeOrderFile></ILMergeOrderFile>
-
-    <!-- The strong key file name if not specified in the project -->
-    <ILMergeKeyFile></ILMergeKeyFile>
-
-    <!-- The assembly version if differs for the version of the main assembly -->
-    <ILMergeAssemblyVersion></ILMergeAssemblyVersion>
-
-    <!-- added in Version 1.0.4 -->
-    <ILMergeFileAlignment></ILMergeFileAlignment>
-
-    <!-- added in Version 1.0.4, default=none -->
-    <ILMergeAllowDuplicateType></ILMergeAllowDuplicateType>
-
-    <!-- If the <see cref="CopyAttributes"/> is also set, any assembly-level attributes names that have the same type are copied over into the target assembly -->
-    <ILMergeAllowMultipleAssemblyLevelAttributes></ILMergeAllowMultipleAssemblyLevelAttributes>
-
-    <!-- See ILMerge documentation -->
-    <ILMergeAllowZeroPeKind></ILMergeAllowZeroPeKind>
-
-    <!-- The assembly level attributes of each input assembly are copied over into the target assembly -->
-    <ILMergeCopyAttributes></ILMergeCopyAttributes>
-    
-    <!-- Creates a .pdb file for the output assembly and merges into it any .pdb files found for input assemblies, default=true -->
-    <ILMergeDebugInfo>false</ILMergeDebugInfo>
-
-    <!-- Target assembly will be delay signed -->
-    <ILMergeDelaySign></ILMergeDelaySign>
-
-    <!-- Types in assemblies other than the primary assembly have their visibility modified -->
-    <ILMergeInternalize></ILMergeInternalize>
-
-    <!-- The path name of the file that will be used to identify types that are not to have their visibility modified -->
-    <ILMergeInternalizeExcludeFile></ILMergeInternalizeExcludeFile>
-
-    <!-- XML documentation files are merged to produce an XML documentation file for the target assembly -->
-    <ILMergeXmlDocumentation></ILMergeXmlDocumentation>
-
-    <!-- External assembly references in the manifest of the target assembly will use full public keys (false) or public key tokens (true, default value) -->
-    <ILMergePublicKeyTokens></ILMergePublicKeyTokens>
-
-    <!-- Types with the same name are all merged into a single type in the target assembly -->
-    <ILMergeUnionMerge></ILMergeUnionMerge>
-
-    <!-- The version of the target framework, default 40 (works for 45 too) -->
-    <ILTargetPlatform>45</ILTargetPlatform>
-  </PropertyGroup>
-</Project>

+ 0 - 5
BepInEx.Patcher/ILMergeConfig.json

@@ -1,5 +0,0 @@
-{
-  "General": {
-    "OutputFile": "$(TargetDir)$(TargetFileName)"
-  }
-}

+ 0 - 4
BepInEx.Patcher/ILMergeOrder.txt

@@ -1,4 +0,0 @@
-# this file contains the partial list of the merged assemblies in the merge order
-# you can fill it from the obj\CONFIG\PROJECT.ilmerge generated on every build
-# and finetune merge order to your satisfaction
-

+ 3 - 3
BepInEx.Patcher/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="ILMerge" version="2.14.1208" targetFramework="net452" />
-  <package id="Mono.Cecil" version="0.9.6.4" targetFramework="net452" />
-  <package id="MSBuild.ILMerge.Task" version="1.0.5" targetFramework="net452" />
+  <package id="Costura.Fody" version="2.0.0" targetFramework="net452" developmentDependency="true" />
+  <package id="Fody" version="3.0.3" targetFramework="net452" developmentDependency="true" />
+  <package id="Mono.Cecil" version="0.10.0" targetFramework="net452" />
 </packages>

+ 52 - 0
BepInEx/Attributes.cs

@@ -0,0 +1,52 @@
+using System;
+
+namespace BepInEx
+{
+    [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; }
+
+        public BepInPlugin(string GUID, string Name, string Version)
+        {
+            this.GUID = GUID;
+            this.Name = Name;
+            this.Version = new Version(Version);
+        }
+    }
+
+    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+    public class BepInDependency : Attribute
+    {
+        public enum DependencyFlags
+        {
+            HardDependency = 1,
+            SoftDependency = 2
+        }
+
+        public string DependencyGUID { get; protected set; }
+
+        public DependencyFlags Flags { get; protected set; }
+
+        public BepInDependency(string DependencyGUID, DependencyFlags Flags = DependencyFlags.HardDependency)
+        {
+            this.DependencyGUID = DependencyGUID;
+            this.Flags = Flags;
+        }
+    }
+}

+ 1 - 14
BepInEx/BaseUnityPlugin.cs

@@ -8,19 +8,6 @@ namespace BepInEx
     /// </summary>
     public abstract class BaseUnityPlugin : MonoBehaviour
     {
-        /// <summary>
-        /// The unique identifier of the plugin. Should not change between plugin versions.
-        /// </summary>
-        public abstract string ID { get; }
 
-        /// <summary>
-        /// The user friendly name of the plugin. Is able to be changed between versions.
-        /// </summary>
-        public abstract string Name { get; }
-
-        /// <summary>
-        /// The specfic version of the plugin.
-        /// </summary>
-        public abstract Version Version { get; }
     }
-}
+}

+ 4 - 1
BepInEx/BepInEx.csproj

@@ -17,7 +17,7 @@
     <PlatformTarget>AnyCPU</PlatformTarget>
     <DebugType>none</DebugType>
     <Optimize>true</Optimize>
-    <OutputPath>K:\KoikatuTrial_Data\Managed\</OutputPath>
+    <OutputPath>..\bin\KoikatuTrial_Data\Managed\</OutputPath>
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
@@ -49,6 +49,7 @@
     </Reference>
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Attributes.cs" />
     <Compile Include="Config.cs" />
     <Compile Include="ConfigWrapper.cs" />
     <Compile Include="ConsoleUtil\ConsoleEncoding\ConsoleEncoding.Buffers.cs" />
@@ -57,11 +58,13 @@
     <Compile Include="ConsoleUtil\ConsoleMirror.cs" />
     <Compile Include="ConsoleUtil\ConsoleWindow.cs" />
     <Compile Include="ConsoleUtil\Extensions.cs" />
+    <Compile Include="ConsoleUtil\Kon.cs" />
     <Compile Include="ConsoleUtil\SafeConsole.cs" />
     <Compile Include="Chainloader.cs" />
     <Compile Include="BaseUnityPlugin.cs" />
     <Compile Include="Logger.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="TypeLoader.cs" />
   </ItemGroup>
   <Import Project="..\BepInEx.Common\BepInEx.Common.projitems" Label="Shared" />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

+ 139 - 127
BepInEx/Chainloader.cs

@@ -8,130 +8,142 @@ using UnityEngine;
 
 namespace BepInEx
 {
-    /// <summary>
-    /// The manager and loader for all plugins, and the entry point for BepInEx.
-    /// </summary>
-    public class Chainloader
-    {
-        /// <summary>
-        /// The loaded and initialized list of plugins.
-        /// </summary>
-        public static List<BaseUnityPlugin> Plugins { get; protected set; } = new List<BaseUnityPlugin>();
-
-        /// <summary>
-        /// The GameObject that all plugins are attached to as components.
-        /// </summary>
-        public static GameObject ManagerObject { get; protected set; } = new GameObject("BepInEx_Manager");
-
-
-        static bool loaded = false;
-
-        /// <summary>
-        /// The entry point for BepInEx, called on the very first LoadScene() from UnityEngine.
-        /// </summary>
-        public static void Initialize()
-        {
-            if (loaded)
-                return;
-
-            if (bool.Parse(Config.GetEntry("console", "false")))
-            {
-                UnityInjector.ConsoleUtil.ConsoleWindow.Attach();
-
-                if (bool.Parse(Config.GetEntry("console-shiftjis", "false")))
-                    UnityInjector.ConsoleUtil.ConsoleEncoding.ConsoleCodePage = 932;
-            }
-
-            try
-            {
-                BepInLogger.Log($"Chainloader started");
-
-                UnityEngine.Object.DontDestroyOnLoad(ManagerObject);
-
-                if (Directory.Exists(Utility.PluginsDirectory))
-                {
-                    var pluginTypes = LoadTypes<BaseUnityPlugin>(Utility.PluginsDirectory);
-
-                    BepInLogger.Log($"{pluginTypes.Count} plugins found");
-
-                    foreach (Type t in pluginTypes)
-                    {
-                        try
-                        {
-                            var plugin = (BaseUnityPlugin)ManagerObject.AddComponent(t);
-                            Plugins.Add(plugin);
-                            BepInLogger.Log($"Loaded [{plugin.Name}]");
-                        }
-                        catch (Exception ex)
-                        {
-                            BepInLogger.Log($"Error loading [{t.Name}] : {ex.Message}");
-                        }
-                    }
-                }
-            }
-            catch (Exception ex)
-            {
-                UnityInjector.ConsoleUtil.ConsoleWindow.Attach();
-
-                Console.WriteLine("Error occurred starting the game");
-                Console.WriteLine(ex.ToString());
-            }
-
-            loaded = true;
-        }
-
-        /// <summary>
-        /// Checks all plugins to see if a plugin with a certain ID is loaded.
-        /// </summary>
-        /// <param name="ID">The ID to check for.</param>
-        /// <returns></returns>
-        public static bool IsIDLoaded(string ID)
-        {
-            foreach (var plugin in Plugins)
-                if (plugin != null && plugin.enabled && plugin.ID == ID)
-                    return true;
-
-            return false;
-        }
-
-        /// <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 List<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 (Type type in assembly.GetTypes())
-                    {
-                        if (type.IsInterface || type.IsAbstract)
-                        {
-                            continue;
-                        }
-                        else
-                        {
-                            if (type.BaseType == pluginType)
-                                types.Add(type);
-                        }
-                    }
-                }
-                catch (BadImageFormatException)
-                {
-
-                }
-            }
-
-            return types;
-        }
-    }
-}
+	/// <summary>
+	/// The manager and loader for all plugins, and the entry point for BepInEx.
+	/// </summary>
+	public class Chainloader
+	{
+		/// <summary>
+		/// The loaded and initialized list of plugins.
+		/// </summary>
+		public static List<BaseUnityPlugin> Plugins { get; protected set; } = new List<BaseUnityPlugin>();
+
+		/// <summary>
+		/// The GameObject that all plugins are attached to as components.
+		/// </summary>
+		public static GameObject ManagerObject { get; protected set; } = new GameObject("BepInEx_Manager");
+
+
+		static bool loaded = false;
+
+		/// <summary>
+		/// The entry point for BepInEx, called on the very first LoadScene() from UnityEngine.
+		/// </summary>
+		public static void Initialize()
+		{
+			if (loaded)
+				return;
+
+			if (bool.Parse(Config.GetEntry("console", "false")))
+			{
+				try
+				{
+					UnityInjector.ConsoleUtil.ConsoleWindow.Attach();
+
+					if (bool.Parse(Config.GetEntry("console-shiftjis", "false")))
+						UnityInjector.ConsoleUtil.ConsoleEncoding.ConsoleCodePage = 932;
+				}
+				catch
+				{
+					BepInLogger.Log("Failed to allocate console!", true);
+				}
+			}
+
+			try
+			{
+				BepInLogger.Log($"BepInEx {Assembly.GetExecutingAssembly().GetName().Version}");
+				BepInLogger.Log($"Chainloader started");
+
+				UnityEngine.Object.DontDestroyOnLoad(ManagerObject);
+
+				if (Directory.Exists(Utility.PluginsDirectory))
+				{
+					var pluginTypes = TypeLoader.LoadTypes<BaseUnityPlugin>(Utility.PluginsDirectory).ToList();
+
+					BepInLogger.Log($"{pluginTypes.Count} plugins found");
+
+					Dictionary<Type, IEnumerable<Type>> dependencyDict = new Dictionary<Type, IEnumerable<Type>>();
+
+					foreach (Type t in pluginTypes)
+					{
+						try
+						{
+							IEnumerable<Type> dependencies = TypeLoader.GetDependencies(t, pluginTypes);
+
+							dependencyDict[t] = dependencies;
+						}
+						catch (MissingDependencyException)
+						{
+							var metadata = TypeLoader.GetMetadata(t);
+
+							BepInLogger.Log($"Cannot load [{metadata.Name}] due to missing dependencies.");
+						}
+					}
+
+					pluginTypes = TopologicalSort(dependencyDict.Keys, x => dependencyDict[x]).ToList();
+
+					foreach (Type t in pluginTypes)
+					{
+						try
+						{
+							var metadata = TypeLoader.GetMetadata(t);
+
+							var plugin = (BaseUnityPlugin) ManagerObject.AddComponent(t);
+
+							Plugins.Add(plugin);
+							BepInLogger.Log($"Loaded [{metadata.Name} {metadata.Version}]");
+						}
+						catch (Exception ex)
+						{
+							BepInLogger.Log($"Error loading [{t.Name}] : {ex.Message}");
+						}
+					}
+				}
+			}
+			catch (Exception ex)
+			{
+				UnityInjector.ConsoleUtil.ConsoleWindow.Attach();
+
+				Console.WriteLine("Error occurred starting the game");
+				Console.WriteLine(ex.ToString());
+			}
+
+			loaded = true;
+		}
+
+		protected 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)
+				Visit(input);
+
+			return sorted_list;
+
+			void Visit(TNode node)
+			{
+				if (visited.Contains(node))
+				{
+					if (!sorted.Contains(node))
+						throw new Exception("Cyclic Dependency");
+				}
+				else
+				{
+					visited.Add(node);
+
+					foreach (var dep in dependencySelector(node))
+						Visit(dep);
+
+					sorted.Add(node);
+					sorted_list.Add(node);
+				}
+			}
+		}
+	}
+}

+ 3 - 3
BepInEx/Config.cs

@@ -203,17 +203,17 @@ namespace BepInEx
 
         public static string GetEntry(this BaseUnityPlugin plugin, string key, string defaultValue = "")
         {
-            return GetEntry(key, defaultValue, plugin.ID);
+            return GetEntry(key, defaultValue, TypeLoader.GetMetadata(plugin).GUID);
         }
 
         public static void SetEntry(this BaseUnityPlugin plugin, string key, string value)
         {
-            SetEntry(key, value, plugin.ID);
+            SetEntry(key, value, TypeLoader.GetMetadata(plugin).GUID);
         }
 
         public static bool HasEntry(this BaseUnityPlugin plugin, string key)
         {
-            return HasEntry(key, plugin.ID);
+            return HasEntry(key, TypeLoader.GetMetadata(plugin).GUID);
         }
         #endregion Extensions
     }

+ 1 - 1
BepInEx/ConfigWrapper.cs

@@ -31,7 +31,7 @@ namespace BepInEx
 
         public ConfigWrapper(string key, BaseUnityPlugin plugin, T @default = default(T)) : this(key, @default)
         {
-            Section = plugin.ID;
+            Section = TypeLoader.GetMetadata(plugin).GUID;
         }
 
         public ConfigWrapper(string key, string section, T @default = default(T)) : this(key, @default)

+ 11 - 4
BepInEx/ConsoleUtil/ConsoleWindow.cs

@@ -26,13 +26,17 @@ namespace UnityInjector.ConsoleUtil
             // Store Current Window
             IntPtr currWnd = GetForegroundWindow();
 
-            if (!AllocConsole())
-                throw new Exception("AllocConsole() failed");
+            //Check for existing console before allocating
+            if (GetConsoleWindow() == IntPtr.Zero)
+                if (!AllocConsole())
+                    throw new Exception("AllocConsole() failed");
 
             // Restore Foreground
             SetForegroundWindow(currWnd);
 
-            _cOut = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero);
+            _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();
@@ -65,13 +69,16 @@ namespace UnityInjector.ConsoleUtil
         [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,
-            int desiredAccess,
+            uint desiredAccess,
             int shareMode,
             IntPtr securityAttributes,
             int creationDisposition,

+ 152 - 0
BepInEx/ConsoleUtil/Kon.cs

@@ -0,0 +1,152 @@
+using System;
+using System.Runtime.InteropServices;
+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
+    }
+}

+ 2 - 2
BepInEx/Properties/AssemblyInfo.cs

@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyVersion("3.0.0.0")]
+[assembly: AssemblyFileVersion("3.0.0.0")]

+ 96 - 0
BepInEx/TypeLoader.cs

@@ -0,0 +1,96 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+namespace BepInEx
+{
+    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 (Type type in assembly.GetTypes())
+                    {
+                        if (type.IsInterface || type.IsAbstract)
+                        {
+                            continue;
+                        }
+                        else
+                        {
+                            if (type.BaseType == pluginType)
+                                types.Add(type);
+                        }
+                    }
+                }
+                catch (BadImageFormatException) { }
+            }
+
+            return types;
+        }
+
+        public static BepInPlugin GetMetadata(object Plugin)
+        {
+            return GetMetadata(Plugin.GetType());
+        }
+
+        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 IEnumerable<Type> GetDependencies(Type Plugin, IEnumerable<Type> AllPlugins)
+        {
+            object[] attributes = Plugin.GetCustomAttributes(typeof(BepInDependency), true);
+
+            List<Type> dependencyTypes = new List<Type>();
+
+            foreach (BepInDependency dependency in attributes)
+            {
+                Type dependencyType = AllPlugins.FirstOrDefault(x => GetMetadata(x)?.GUID == dependency.DependencyGUID);
+
+                if (dependencyType == null)
+                    throw new MissingDependencyException("Cannot find dependency type.");
+
+                dependencyTypes.Add(dependencyType);
+            }
+
+            return dependencyTypes;
+        }
+    }
+
+    public class MissingDependencyException : Exception
+    {
+        public MissingDependencyException() : base()
+        {
+
+        }
+
+        public MissingDependencyException(string message) : base(message)
+        {
+
+        }
+    }
+}

BIN
lib/0Harmony.dll