Preloader.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using Mono.Cecil;
  8. using Mono.Cecil.Cil;
  9. using MethodAttributes = Mono.Cecil.MethodAttributes;
  10. namespace BepInEx.Bootstrap
  11. {
  12. public static class Preloader
  13. {
  14. #region Path Properties
  15. public static string ExecutablePath { get; private set; }
  16. public static string CurrentExecutingAssemblyPath => Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", "").Replace('/', '\\');
  17. public static string CurrentExecutingAssemblyDirectoryPath => Path.GetDirectoryName(CurrentExecutingAssemblyPath);
  18. public static string GameName => Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().ProcessName);
  19. public static string GameRootPath => Path.GetDirectoryName(ExecutablePath);
  20. public static string ManagedPath => Path.Combine(GameRootPath, Path.Combine($"{GameName}_Data", "Managed"));
  21. #endregion
  22. public static Dictionary<string, IList<AssemblyPatcherDelegate>> PatcherDictionary = new Dictionary<string, IList<AssemblyPatcherDelegate>>(StringComparer.OrdinalIgnoreCase);
  23. public static void AddPatcher(string dllName, AssemblyPatcherDelegate patcher)
  24. {
  25. if (PatcherDictionary.TryGetValue(dllName, out IList<AssemblyPatcherDelegate> patcherList))
  26. patcherList.Add(patcher);
  27. else
  28. {
  29. patcherList = new List<AssemblyPatcherDelegate>();
  30. patcherList.Add(patcher);
  31. PatcherDictionary[dllName] = patcherList;
  32. }
  33. }
  34. public static void Main(string[] args)
  35. {
  36. ExecutablePath = args[0];
  37. AppDomain.CurrentDomain.AssemblyResolve += LocalResolve;
  38. try
  39. {
  40. //AssemblyPatcher.AssemblyLoad += PatchEntrypoint;
  41. AddPatcher("UnityEngine.dll", PatchEntrypoint);
  42. AssemblyPatcher.PatchAll(ManagedPath, PatcherDictionary);
  43. }
  44. catch (Exception ex)
  45. {
  46. //File.WriteAllText("B:\\test.txt", ex.ToString());
  47. }
  48. }
  49. internal static void PatchEntrypoint(AssemblyDefinition assembly)
  50. {
  51. if (assembly.Name.Name == "UnityEngine")
  52. {
  53. using (AssemblyDefinition injected = AssemblyDefinition.ReadAssembly(CurrentExecutingAssemblyPath))
  54. {
  55. var originalInjectMethod = injected.MainModule.Types.First(x => x.Name == "Chainloader")
  56. .Methods.First(x => x.Name == "Initialize");
  57. var injectMethod = assembly.MainModule.ImportReference(originalInjectMethod);
  58. var sceneManager = assembly.MainModule.Types.First(x => x.Name == "Application");
  59. var voidType = assembly.MainModule.ImportReference(typeof(void));
  60. var cctor = new MethodDefinition(".cctor",
  61. MethodAttributes.Static
  62. | MethodAttributes.Private
  63. | MethodAttributes.HideBySig
  64. | MethodAttributes.SpecialName
  65. | MethodAttributes.RTSpecialName,
  66. voidType);
  67. var ilp = cctor.Body.GetILProcessor();
  68. ilp.Append(ilp.Create(OpCodes.Call, injectMethod));
  69. ilp.Append(ilp.Create(OpCodes.Ret));
  70. sceneManager.Methods.Add(cctor);
  71. }
  72. }
  73. }
  74. internal static Assembly LocalResolve(object sender, ResolveEventArgs args)
  75. {
  76. if (args.Name == "0Harmony, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null")
  77. return Assembly.LoadFile(Path.Combine(CurrentExecutingAssemblyDirectoryPath, "0Harmony.dll"));
  78. return null;
  79. }
  80. }
  81. }