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.

203 lines
7.2 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 Harmony;
  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.Collections.Generic;
  9. using System.Linq;
  10. using System.IO;
  11. using System.Reflection;
  12. using System.Runtime.InteropServices;
  13. using UnityEngine;
  14. using static IPA.Logging.Logger;
  15. using MethodAttributes = Mono.Cecil.MethodAttributes;
  16. namespace IPA.Injector
  17. {
  18. public static class Injector
  19. {
  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. Windows.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 (int i = 0; i < Math.Min(2, cctor.Body.Instructions.Count); i++)
  84. {
  85. var ins = cctor.Body.Instructions[i];
  86. if (i == 0)
  87. {
  88. if (ins.OpCode != OpCodes.Call)
  89. {
  90. ilp.Replace(ins, ilp.Create(OpCodes.Call, cbs));
  91. modified = true;
  92. }
  93. else
  94. {
  95. var mref = ins.Operand as MethodReference;
  96. if (mref.FullName != cbs.FullName)
  97. {
  98. ilp.Replace(ins, ilp.Create(OpCodes.Call, cbs));
  99. modified = true;
  100. }
  101. }
  102. }
  103. if (i == 1 && ins.OpCode != OpCodes.Ret)
  104. {
  105. ilp.Replace(ins, ilp.Create(OpCodes.Ret));
  106. modified = true;
  107. }
  108. }
  109. }
  110. if (modified)
  111. {
  112. bkp?.Add(unityPath);
  113. unityAsmDef.Write(unityPath);
  114. }
  115. #endregion
  116. loader.Debug("Ensuring Assembly-CSharp is virtualized");
  117. #region Virtualize Assembly-CSharp.dll
  118. var ascPath = Path.Combine(Environment.CurrentDirectory, "Beat Saber_Data", "Managed", "Assembly-CSharp.dll");
  119. var ascModule = VirtualizedModule.Load(ascPath);
  120. ascModule.Virtualize(cAsmName, () => bkp?.Add(ascPath));
  121. #endregion
  122. }
  123. private static bool bootstrapped = false;
  124. private static void CreateBootstrapper()
  125. {
  126. if (bootstrapped) return;
  127. bootstrapped = true;
  128. Application.logMessageReceived += delegate (string condition, string stackTrace, LogType type)
  129. {
  130. var level = UnityLogInterceptor.LogTypeToLevel(type);
  131. UnityLogInterceptor.UnityLogger.Log(level, $"{condition.Trim()}");
  132. UnityLogInterceptor.UnityLogger.Log(level, $"{stackTrace.Trim()}");
  133. };
  134. // need to reinit streams singe Unity seems to redirect stdout
  135. Windows.WinConsole.InitializeStreams();
  136. var bootstrapper = new GameObject("NonDestructiveBootstrapper").AddComponent<Bootstrapper>();
  137. bootstrapper.Destroyed += Bootstrapper_Destroyed;
  138. }
  139. private static bool injected = false;
  140. public static void Inject()
  141. {
  142. if (!injected)
  143. {
  144. injected = true;
  145. Windows.WinConsole.Initialize();
  146. SetupLibraryLoading();
  147. var bootstrapper = new GameObject("Bootstrapper").AddComponent<Bootstrapper>();
  148. bootstrapper.Destroyed += Bootstrapper_Destroyed;
  149. }
  150. }
  151. private static bool loadingDone = false;
  152. public static void SetupLibraryLoading()
  153. {
  154. if (loadingDone) return;
  155. loadingDone = true;
  156. #region Add Library load locations
  157. AppDomain.CurrentDomain.AssemblyResolve += LibLoader.AssemblyLibLoader;
  158. /*try
  159. {
  160. if (!SetDllDirectory(LibLoader.NativeDir))
  161. {
  162. libLoader.Warn("Unable to add native library path to load path");
  163. }
  164. }
  165. catch (Exception) { }*/
  166. #endregion
  167. }
  168. [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
  169. [return: MarshalAs(UnmanagedType.Bool)]
  170. static extern bool SetDllDirectory(string lpPathName);
  171. private static void Bootstrapper_Destroyed()
  172. {
  173. PluginComponent.Create();
  174. }
  175. }
  176. }