AssemblyPatcher.cs 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using BepInEx.Common;
  7. using Mono.Cecil;
  8. namespace BepInEx
  9. {
  10. internal static class AssemblyPatcher
  11. {
  12. public delegate void AssemblyLoadEventHandler(AssemblyDefinition assembly);
  13. public static AssemblyLoadEventHandler AssemblyLoad;
  14. public static void PatchAll(string directory)
  15. {
  16. //load all the requested assemblies
  17. List<AssemblyDefinition> assemblies = new List<AssemblyDefinition>();
  18. foreach (string assemblyPath in Directory.GetFiles(directory, "*.dll"))
  19. {
  20. var assembly = AssemblyDefinition.ReadAssembly(assemblyPath);
  21. //NOTE: this is special cased here because the dependency handling for System.dll is a bit wonky
  22. //System has an assembly reference to itself, and it also has a reference to Mono.Security causing a circular dependency
  23. //It's also generally dangerous to change system.dll since so many things rely on it,
  24. // and it's already loaded into the appdomain since this loader references it, so we might as well skip it
  25. if (assembly.Name.Name == "System"
  26. || assembly.Name.Name == "System.Core"
  27. || assembly.Name.Name == "mscorlib")
  28. {
  29. assembly.Dispose();
  30. continue;
  31. }
  32. assemblies.Add(assembly);
  33. }
  34. //generate a dictionary of each assembly's dependencies
  35. Dictionary<AssemblyDefinition, List<AssemblyDefinition>> assemblyDependencyDict = new Dictionary<AssemblyDefinition, List<AssemblyDefinition>>();
  36. foreach (AssemblyDefinition assembly in assemblies)
  37. {
  38. assemblyDependencyDict[assembly] = new List<AssemblyDefinition>();
  39. foreach (var dependencyRef in assembly.MainModule.AssemblyReferences)
  40. {
  41. var dependencyAssembly = assemblies.FirstOrDefault(x => x.FullName == dependencyRef.FullName);
  42. if (dependencyAssembly != null)
  43. {
  44. assemblyDependencyDict[assembly].Add(dependencyAssembly);
  45. }
  46. }
  47. }
  48. //sort the assemblies so load the assemblies that are dependant upon first
  49. List<AssemblyDefinition> sortedAssemblies = Utility.TopologicalSort(assemblies, x => assemblyDependencyDict[x]);
  50. //special casing for UnityEngine, needs to be reordered to the front
  51. var unityEngine = sortedAssemblies.FirstOrDefault(x => x.Name.Name == "UnityEngine");
  52. if (unityEngine != null)
  53. {
  54. sortedAssemblies.Remove(unityEngine);
  55. sortedAssemblies.Insert(0, unityEngine);
  56. }
  57. //call the patchers on the assemblies
  58. foreach (var assembly in sortedAssemblies)
  59. {
  60. using (MemoryStream assemblyStream = new MemoryStream())
  61. {
  62. AssemblyLoad?.Invoke(assembly);
  63. assembly.Write(assemblyStream);
  64. Assembly.Load(assemblyStream.ToArray());
  65. }
  66. assembly.Dispose();
  67. }
  68. }
  69. }
  70. }