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.

206 lines
7.3 KiB

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