Program.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. using Mono.Cecil;
  2. using Mono.Cecil.Cil;
  3. using System;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using MethodAttributes = Mono.Cecil.MethodAttributes;
  8. namespace BepInEx.Patcher
  9. {
  10. class Program
  11. {
  12. static void WriteError(string message)
  13. {
  14. Console.ForegroundColor = ConsoleColor.Red;
  15. Console.WriteLine("Failed");
  16. Console.ResetColor();
  17. Console.WriteLine(message);
  18. }
  19. static void WriteSuccess()
  20. {
  21. Console.ForegroundColor = ConsoleColor.Green;
  22. Console.WriteLine("Success");
  23. Console.ResetColor();
  24. }
  25. static void Main(string[] args)
  26. {
  27. Console.WriteLine($"BepInEx Patcher v{Assembly.GetExecutingAssembly().GetName().Version}");
  28. if (args.Length >= 1) //short circuit for specific dll patch
  29. Environment.Exit(PatchUnityExe(Path.GetDirectoryName(args[0]), args[0], out string message) ? 0 : 9999);
  30. bool hasFound = false;
  31. int patchCount = 0;
  32. foreach (string exePath in Directory.GetFiles(Directory.GetCurrentDirectory()))
  33. {
  34. string gameName = Path.GetFileNameWithoutExtension(exePath);
  35. string managedDir = Environment.CurrentDirectory + $@"\{gameName}_Data\Managed";
  36. string unityOutputDLL = Path.GetFullPath($"{managedDir}\\UnityEngine.dll");
  37. if (!Directory.Exists(managedDir) || !File.Exists(unityOutputDLL))
  38. continue;
  39. hasFound = true;
  40. Console.Write($"Patching {gameName}... ");
  41. if (PatchUnityExe(managedDir, unityOutputDLL, out string message))
  42. {
  43. WriteSuccess();
  44. patchCount++;
  45. }
  46. else
  47. {
  48. WriteError(message);
  49. }
  50. }
  51. Console.WriteLine();
  52. if (!hasFound)
  53. Console.WriteLine("Didn't find any games to patch! Exiting.");
  54. else
  55. Console.WriteLine($"Patched {patchCount} assemblies!");
  56. System.Threading.Thread.Sleep(2000);
  57. }
  58. static bool PatchUnityExe(string managedDir, string unityOutputDLL, out string message)
  59. {
  60. message = null;
  61. try
  62. {
  63. string unityOriginalDLL = Path.GetFullPath($"{managedDir}\\UnityEngine.dll.bak");
  64. if (!File.Exists(unityOriginalDLL))
  65. File.Copy(unityOutputDLL, unityOriginalDLL);
  66. string harmony = Path.GetFullPath($"{managedDir}\\0Harmony.dll");
  67. File.WriteAllBytes(harmony, EmbeddedResource.Get("BepInEx.Patcher.0Harmony.dll"));
  68. string injectedDLL = Path.GetFullPath($"{managedDir}\\BepInEx.dll");
  69. File.WriteAllBytes(injectedDLL, EmbeddedResource.Get("BepInEx.Patcher.BepInEx.dll"));
  70. var defaultResolver = new DefaultAssemblyResolver();
  71. defaultResolver.AddSearchDirectory(managedDir);
  72. var rp = new ReaderParameters
  73. {
  74. AssemblyResolver = defaultResolver
  75. };
  76. using (AssemblyDefinition unity = AssemblyDefinition.ReadAssembly(unityOriginalDLL, rp))
  77. using (AssemblyDefinition injected = AssemblyDefinition.ReadAssembly(injectedDLL, rp))
  78. {
  79. InjectAssembly(unity, injected);
  80. unity.Write(unityOutputDLL);
  81. }
  82. }
  83. catch (Exception e)
  84. {
  85. message = e.ToString();
  86. return false;
  87. }
  88. return true;
  89. }
  90. static void InjectAssembly(AssemblyDefinition unity, AssemblyDefinition injected)
  91. {
  92. //Entry point
  93. var originalInjectMethod = injected.MainModule.Types.First(x => x.Name == "Chainloader")
  94. .Methods.First(x => x.Name == "Initialize");
  95. var injectMethod = unity.MainModule.ImportReference(originalInjectMethod);
  96. var sceneManager = unity.MainModule.Types.First(x => x.Name == "Application");
  97. var voidType = unity.MainModule.ImportReference(typeof(void));
  98. var cctor = new MethodDefinition(".cctor",
  99. MethodAttributes.Static
  100. | MethodAttributes.Private
  101. | MethodAttributes.HideBySig
  102. | MethodAttributes.SpecialName
  103. | MethodAttributes.RTSpecialName,
  104. voidType);
  105. var ilp = cctor.Body.GetILProcessor();
  106. ilp.Append(ilp.Create(OpCodes.Call, injectMethod));
  107. ilp.Append(ilp.Create(OpCodes.Ret));
  108. sceneManager.Methods.Add(cctor);
  109. }
  110. }
  111. }