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.

208 lines
7.4 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. using IPA.Config;
  2. using IPA.Injector.Backups;
  3. using IPA.Loader;
  4. using IPA.Logging;
  5. using Mono.Cecil;
  6. using Mono.Cecil.Cil;
  7. using System;
  8. using System.IO;
  9. using System.Linq;
  10. using System.Reflection;
  11. using System.Threading.Tasks;
  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 SetupLibraryLoading()
  45. {
  46. if (_loadingDone) return;
  47. _loadingDone = true;
  48. AppDomain.CurrentDomain.AssemblyResolve += LibLoader.AssemblyLibLoader;
  49. }
  50. private static void InstallBootstrapPatch()
  51. {
  52. var cAsmName = Assembly.GetExecutingAssembly().GetName();
  53. loader.Debug("Finding backup");
  54. var backupPath = Path.Combine(Environment.CurrentDirectory, "IPA", "Backups", "Beat Saber");
  55. var bkp = BackupManager.FindLatestBackup(backupPath);
  56. if (bkp == null)
  57. loader.Warn("No backup found! Was BSIPA installed using the installer?");
  58. loader.Debug("Ensuring patch on UnityEngine.CoreModule exists");
  59. #region Insert patch into UnityEngine.CoreModule.dll
  60. {
  61. var unityPath = Path.Combine(Environment.CurrentDirectory, "Beat Saber_Data", "Managed",
  62. "UnityEngine.CoreModule.dll");
  63. var unityAsmDef = AssemblyDefinition.ReadAssembly(unityPath, new ReaderParameters
  64. {
  65. ReadWrite = false,
  66. InMemory = true,
  67. ReadingMode = ReadingMode.Immediate
  68. });
  69. var unityModDef = unityAsmDef.MainModule;
  70. bool modified = false;
  71. foreach (var asmref in unityModDef.AssemblyReferences)
  72. {
  73. if (asmref.Name == cAsmName.Name)
  74. {
  75. if (asmref.Version != cAsmName.Version)
  76. {
  77. asmref.Version = cAsmName.Version;
  78. modified = true;
  79. }
  80. }
  81. }
  82. var application = unityModDef.GetType("UnityEngine", "Application");
  83. MethodDefinition cctor = null;
  84. foreach (var m in application.Methods)
  85. if (m.IsRuntimeSpecialName && m.Name == ".cctor")
  86. cctor = m;
  87. var cbs = unityModDef.ImportReference(((Action)CreateBootstrapper).Method);
  88. if (cctor == null)
  89. {
  90. cctor = new MethodDefinition(".cctor",
  91. MethodAttributes.RTSpecialName | MethodAttributes.Static | MethodAttributes.SpecialName,
  92. unityModDef.TypeSystem.Void);
  93. application.Methods.Add(cctor);
  94. modified = true;
  95. var ilp = cctor.Body.GetILProcessor();
  96. ilp.Emit(OpCodes.Call, cbs);
  97. ilp.Emit(OpCodes.Ret);
  98. }
  99. else
  100. {
  101. var ilp = cctor.Body.GetILProcessor();
  102. for (var i = 0; i < Math.Min(2, cctor.Body.Instructions.Count); i++)
  103. {
  104. var ins = cctor.Body.Instructions[i];
  105. switch (i)
  106. {
  107. case 0 when ins.OpCode != OpCodes.Call:
  108. ilp.Replace(ins, ilp.Create(OpCodes.Call, cbs));
  109. modified = true;
  110. break;
  111. case 0:
  112. {
  113. var methodRef = ins.Operand as MethodReference;
  114. if (methodRef?.FullName != cbs.FullName)
  115. {
  116. ilp.Replace(ins, ilp.Create(OpCodes.Call, cbs));
  117. modified = true;
  118. }
  119. break;
  120. }
  121. case 1 when ins.OpCode != OpCodes.Ret:
  122. ilp.Replace(ins, ilp.Create(OpCodes.Ret));
  123. modified = true;
  124. break;
  125. }
  126. }
  127. }
  128. if (modified)
  129. {
  130. bkp?.Add(unityPath);
  131. unityAsmDef.Write(unityPath);
  132. }
  133. }
  134. #endregion Insert patch into UnityEngine.CoreModule.dll
  135. loader.Debug("Ensuring Assembly-CSharp is virtualized");
  136. #region Virtualize Assembly-CSharp.dll
  137. {
  138. var ascPath = Path.Combine(Environment.CurrentDirectory, "Beat Saber_Data", "Managed",
  139. "Assembly-CSharp.dll");
  140. var ascModule = VirtualizedModule.Load(ascPath);
  141. ascModule.Virtualize(cAsmName, () => bkp?.Add(ascPath));
  142. }
  143. #endregion Virtualize Assembly-CSharp.dll
  144. }
  145. private static bool _bootstrapped;
  146. private static void CreateBootstrapper()
  147. {
  148. if (_bootstrapped) return;
  149. _bootstrapped = true;
  150. Application.logMessageReceived += delegate (string condition, string stackTrace, LogType type)
  151. {
  152. var level = UnityLogInterceptor.LogTypeToLevel(type);
  153. UnityLogInterceptor.UnityLogger.Log(level, $"{condition.Trim()}");
  154. UnityLogInterceptor.UnityLogger.Log(level, $"{stackTrace.Trim()}");
  155. };
  156. // need to reinit streams singe Unity seems to redirect stdout
  157. WinConsole.InitializeStreams();
  158. var bootstrapper = new GameObject("NonDestructiveBootstrapper").AddComponent<Bootstrapper>();
  159. bootstrapper.Destroyed += Bootstrapper_Destroyed;
  160. }
  161. private static bool _loadingDone;
  162. private static void Bootstrapper_Destroyed()
  163. {
  164. // wait for plugins to finish loading
  165. pluginAsyncLoadTask.Wait();
  166. log.Debug("Plugins loaded");
  167. log.Debug(string.Join(", ", PluginLoader.PluginsMetadata));
  168. PluginComponent.Create();
  169. }
  170. }
  171. }