|
@@ -1,24 +1,21 @@
|
|
-using System;
|
|
|
|
-using System.Collections.Generic;
|
|
|
|
|
|
+using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Reflection;
|
|
using BepInEx.Common;
|
|
using BepInEx.Common;
|
|
using Mono.Cecil;
|
|
using Mono.Cecil;
|
|
|
|
|
|
-namespace BepInEx
|
|
|
|
|
|
+namespace BepInEx.Bootstrap
|
|
{
|
|
{
|
|
- internal static class AssemblyPatcher
|
|
|
|
- {
|
|
|
|
- public delegate void AssemblyLoadEventHandler(AssemblyDefinition assembly);
|
|
|
|
-
|
|
|
|
- public static AssemblyLoadEventHandler AssemblyLoad;
|
|
|
|
|
|
+ public delegate void AssemblyPatcherDelegate(AssemblyDefinition assembly);
|
|
|
|
|
|
-
|
|
|
|
- public static void PatchAll(string directory)
|
|
|
|
|
|
+ public static class AssemblyPatcher
|
|
|
|
+ {
|
|
|
|
+ public static void PatchAll(string directory, Dictionary<string, IList<AssemblyPatcherDelegate>> patcherMethodDictionary)
|
|
{
|
|
{
|
|
//load all the requested assemblies
|
|
//load all the requested assemblies
|
|
List<AssemblyDefinition> assemblies = new List<AssemblyDefinition>();
|
|
List<AssemblyDefinition> assemblies = new List<AssemblyDefinition>();
|
|
|
|
+ Dictionary<AssemblyDefinition, string> assemblyFilenames = new Dictionary<AssemblyDefinition, string>();
|
|
|
|
|
|
foreach (string assemblyPath in Directory.GetFiles(directory, "*.dll"))
|
|
foreach (string assemblyPath in Directory.GetFiles(directory, "*.dll"))
|
|
{
|
|
{
|
|
@@ -29,18 +26,18 @@ namespace BepInEx
|
|
//It's also generally dangerous to change system.dll since so many things rely on it,
|
|
//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
|
|
// 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"
|
|
if (assembly.Name.Name == "System"
|
|
- || assembly.Name.Name == "System.Core"
|
|
|
|
- || assembly.Name.Name == "mscorlib")
|
|
|
|
|
|
+ || assembly.Name.Name == "mscorlib") //mscorlib is already loaded into the appdomain so it can't be patched
|
|
{
|
|
{
|
|
assembly.Dispose();
|
|
assembly.Dispose();
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
assemblies.Add(assembly);
|
|
assemblies.Add(assembly);
|
|
|
|
+ assemblyFilenames[assembly] = Path.GetFileName(assemblyPath);
|
|
}
|
|
}
|
|
|
|
|
|
//generate a dictionary of each assembly's dependencies
|
|
//generate a dictionary of each assembly's dependencies
|
|
- Dictionary<AssemblyDefinition, List<AssemblyDefinition>> assemblyDependencyDict = new Dictionary<AssemblyDefinition, List<AssemblyDefinition>>();
|
|
|
|
|
|
+ Dictionary<AssemblyDefinition, IList<AssemblyDefinition>> assemblyDependencyDict = new Dictionary<AssemblyDefinition, IList<AssemblyDefinition>>();
|
|
|
|
|
|
foreach (AssemblyDefinition assembly in assemblies)
|
|
foreach (AssemblyDefinition assembly in assemblies)
|
|
{
|
|
{
|
|
@@ -51,35 +48,34 @@ namespace BepInEx
|
|
var dependencyAssembly = assemblies.FirstOrDefault(x => x.FullName == dependencyRef.FullName);
|
|
var dependencyAssembly = assemblies.FirstOrDefault(x => x.FullName == dependencyRef.FullName);
|
|
|
|
|
|
if (dependencyAssembly != null)
|
|
if (dependencyAssembly != null)
|
|
- {
|
|
|
|
assemblyDependencyDict[assembly].Add(dependencyAssembly);
|
|
assemblyDependencyDict[assembly].Add(dependencyAssembly);
|
|
- }
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//sort the assemblies so load the assemblies that are dependant upon first
|
|
//sort the assemblies so load the assemblies that are dependant upon first
|
|
- List<AssemblyDefinition> sortedAssemblies = Utility.TopologicalSort(assemblies, x => assemblyDependencyDict[x]);
|
|
|
|
|
|
+ IEnumerable<AssemblyDefinition> sortedAssemblies = Utility.TopologicalSort(assemblies, x => assemblyDependencyDict[x]);
|
|
|
|
|
|
- //special casing for UnityEngine, needs to be reordered to the front
|
|
|
|
- var unityEngine = sortedAssemblies.FirstOrDefault(x => x.Name.Name == "UnityEngine");
|
|
|
|
- if (unityEngine != null)
|
|
|
|
|
|
+ //call the patchers on the assemblies
|
|
|
|
+ foreach (var assembly in sortedAssemblies)
|
|
{
|
|
{
|
|
- sortedAssemblies.Remove(unityEngine);
|
|
|
|
- sortedAssemblies.Insert(0, unityEngine);
|
|
|
|
|
|
+ //skip if we aren't patching it
|
|
|
|
+ if (!patcherMethodDictionary.TryGetValue(Path.GetFileName(assemblyFilenames[assembly]), out IList<AssemblyPatcherDelegate> patcherMethods))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ Patch(assembly, patcherMethods);
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- //call the patchers on the assemblies
|
|
|
|
- foreach (var assembly in sortedAssemblies)
|
|
|
|
|
|
+ public static void Patch(AssemblyDefinition assembly, IEnumerable<AssemblyPatcherDelegate> patcherMethods)
|
|
|
|
+ {
|
|
|
|
+ using (assembly)
|
|
|
|
+ using (MemoryStream assemblyStream = new MemoryStream())
|
|
{
|
|
{
|
|
- using (MemoryStream assemblyStream = new MemoryStream())
|
|
|
|
- {
|
|
|
|
- AssemblyLoad?.Invoke(assembly);
|
|
|
|
-
|
|
|
|
- assembly.Write(assemblyStream);
|
|
|
|
- Assembly.Load(assemblyStream.ToArray());
|
|
|
|
- }
|
|
|
|
|
|
+ foreach (AssemblyPatcherDelegate method in patcherMethods)
|
|
|
|
+ method.Invoke(assembly);
|
|
|
|
|
|
- assembly.Dispose();
|
|
|
|
|
|
+ assembly.Write(assemblyStream);
|
|
|
|
+ Assembly.Load(assemblyStream.ToArray());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|