You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

193 lines
6.9 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. using IPA.Injector.Backups;
  2. using IPA.Loader;
  3. using IPA.Logging;
  4. using Mono.Cecil;
  5. using Mono.Cecil.Cil;
  6. using System;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Reflection;
  10. using System.Threading.Tasks;
  11. using IPA.Config;
  12. using UnityEngine;
  13. using static IPA.Logging.Logger;
  14. using MethodAttributes = Mono.Cecil.MethodAttributes;
  15. namespace IPA.Injector
  16. {
  17. // ReSharper disable once UnusedMember.Global
  18. public static class Injector
  19. {
  20. private static Task pluginAsyncLoadTask;
  21. // ReSharper disable once UnusedParameter.Global
  22. public static void Main(string[] args)
  23. { // entry point for doorstop
  24. // At this point, literally nothing but mscorlib is loaded,
  25. // and since this class doesn't have any static fields that
  26. // aren't defined in mscorlib, we can control exactly what
  27. // gets loaded.
  28. try
  29. {
  30. if (!Environment.GetCommandLineArgs().Contains("--no-console"))
  31. WinConsole.Initialize();
  32. SetupLibraryLoading();
  33. SelfConfig.Set();
  34. loader.Debug("Prepping bootstrapper");
  35. InstallBootstrapPatch();
  36. Updates.InstallPendingUpdates();
  37. pluginAsyncLoadTask = PluginLoader.LoadTask();
  38. }
  39. catch (Exception e)
  40. {
  41. Console.WriteLine(e);
  42. }
  43. }
  44. private static void InstallBootstrapPatch()
  45. {
  46. var cAsmName = Assembly.GetExecutingAssembly().GetName();
  47. loader.Debug("Finding backup");
  48. var backupPath = Path.Combine(Environment.CurrentDirectory, "IPA","Backups","Beat Saber");
  49. var bkp = BackupManager.FindLatestBackup(backupPath);
  50. if (bkp == null)
  51. loader.Warn("No backup found! Was BSIPA installed using the installer?");
  52. loader.Debug("Ensuring patch on UnityEngine.CoreModule exists");
  53. #region Insert patch into UnityEngine.CoreModule.dll
  54. var unityPath = Path.Combine(Environment.CurrentDirectory, "Beat Saber_Data", "Managed", "UnityEngine.CoreModule.dll");
  55. var unityAsmDef = AssemblyDefinition.ReadAssembly(unityPath, new ReaderParameters
  56. {
  57. ReadWrite = false,
  58. InMemory = true,
  59. ReadingMode = ReadingMode.Immediate
  60. });
  61. var unityModDef = unityAsmDef.MainModule;
  62. bool modified = false;
  63. foreach (var asmref in unityModDef.AssemblyReferences)
  64. {
  65. if (asmref.Name == cAsmName.Name)
  66. {
  67. if (asmref.Version != cAsmName.Version)
  68. {
  69. asmref.Version = cAsmName.Version;
  70. modified = true;
  71. }
  72. }
  73. }
  74. var application = unityModDef.GetType("UnityEngine", "Application");
  75. MethodDefinition cctor = null;
  76. foreach (var m in application.Methods)
  77. if (m.IsRuntimeSpecialName && m.Name == ".cctor")
  78. cctor = m;
  79. var cbs = unityModDef.ImportReference(((Action)CreateBootstrapper).Method);
  80. if (cctor == null)
  81. {
  82. cctor = new MethodDefinition(".cctor", MethodAttributes.RTSpecialName | MethodAttributes.Static | MethodAttributes.SpecialName, unityModDef.TypeSystem.Void);
  83. application.Methods.Add(cctor);
  84. modified = true;
  85. var ilp = cctor.Body.GetILProcessor();
  86. ilp.Emit(OpCodes.Call, cbs);
  87. ilp.Emit(OpCodes.Ret);
  88. }
  89. else
  90. {
  91. var ilp = cctor.Body.GetILProcessor();
  92. for (var i = 0; i < Math.Min(2, cctor.Body.Instructions.Count); i++)
  93. {
  94. var ins = cctor.Body.Instructions[i];
  95. switch (i)
  96. {
  97. case 0 when ins.OpCode != OpCodes.Call:
  98. ilp.Replace(ins, ilp.Create(OpCodes.Call, cbs));
  99. modified = true;
  100. break;
  101. case 0:
  102. {
  103. var methodRef = ins.Operand as MethodReference;
  104. if (methodRef?.FullName != cbs.FullName)
  105. {
  106. ilp.Replace(ins, ilp.Create(OpCodes.Call, cbs));
  107. modified = true;
  108. }
  109. break;
  110. }
  111. case 1 when ins.OpCode != OpCodes.Ret:
  112. ilp.Replace(ins, ilp.Create(OpCodes.Ret));
  113. modified = true;
  114. break;
  115. }
  116. }
  117. }
  118. if (modified)
  119. {
  120. bkp?.Add(unityPath);
  121. unityAsmDef.Write(unityPath);
  122. }
  123. #endregion
  124. loader.Debug("Ensuring Assembly-CSharp is virtualized");
  125. #region Virtualize Assembly-CSharp.dll
  126. var ascPath = Path.Combine(Environment.CurrentDirectory, "Beat Saber_Data", "Managed", "Assembly-CSharp.dll");
  127. var ascModule = VirtualizedModule.Load(ascPath);
  128. ascModule.Virtualize(cAsmName, () => bkp?.Add(ascPath));
  129. #endregion
  130. }
  131. private static bool _bootstrapped;
  132. private static void CreateBootstrapper()
  133. {
  134. if (_bootstrapped) return;
  135. _bootstrapped = true;
  136. Application.logMessageReceived += delegate (string condition, string stackTrace, LogType type)
  137. {
  138. var level = UnityLogInterceptor.LogTypeToLevel(type);
  139. UnityLogInterceptor.UnityLogger.Log(level, $"{condition.Trim()}");
  140. UnityLogInterceptor.UnityLogger.Log(level, $"{stackTrace.Trim()}");
  141. };
  142. // need to reinit streams singe Unity seems to redirect stdout
  143. WinConsole.InitializeStreams();
  144. var bootstrapper = new GameObject("NonDestructiveBootstrapper").AddComponent<Bootstrapper>();
  145. bootstrapper.Destroyed += Bootstrapper_Destroyed;
  146. }
  147. private static bool _loadingDone;
  148. private static void SetupLibraryLoading()
  149. {
  150. if (_loadingDone) return;
  151. _loadingDone = true;
  152. AppDomain.CurrentDomain.AssemblyResolve += LibLoader.AssemblyLibLoader;
  153. }
  154. private static void Bootstrapper_Destroyed()
  155. {
  156. // wait for plugins to finish loading
  157. pluginAsyncLoadTask.Wait();
  158. log.Debug("Plugins loaded");
  159. log.Debug(string.Join(", ", PluginLoader.PluginsMetadata));
  160. PluginComponent.Create();
  161. }
  162. }
  163. }