A fork of Eusth's IPA
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.

300 lines
11KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Windows.Forms;
  7. using Mono.Cecil;
  8. using Mono.Cecil.Cil;
  9. using System.Threading;
  10. namespace QuietLauncher
  11. {
  12. static class Program
  13. {
  14. private static string[] TABOO_NAMES = {
  15. //"Start",
  16. //"Update",
  17. //"Awake",
  18. //"OnDestroy"
  19. };
  20. private static string[] ENTRY_TYPES = { "Display" };
  21. /// <summary>
  22. /// The main entry point for the application.
  23. /// </summary>
  24. [STAThread]
  25. static void Main()
  26. {
  27. try {
  28. var execPath = Application.ExecutablePath;
  29. var fileName = Path.GetFileNameWithoutExtension(execPath);
  30. if (fileName.IndexOf("VR") == -1 && fileName.IndexOf("_") == -1) return;
  31. bool vrMode = fileName.IndexOf("VR") > 0;
  32. bool directMode = Application.ExecutablePath.EndsWith("_DirectToRift.exe");
  33. string baseName = execPath.Substring(0, vrMode
  34. ? execPath.LastIndexOf("VR")
  35. : execPath.LastIndexOf("_"));
  36. string executable = baseName + ".exe";
  37. var file = new FileInfo(executable);
  38. if (file.Exists)
  39. {
  40. var args = Environment.GetCommandLineArgs().ToList();
  41. bool created = false;
  42. var dataFolder = Path.Combine(file.DirectoryName, Path.GetFileNameWithoutExtension(file.Name) + "_Data");
  43. var assemblyPath = Path.Combine(Path.Combine(dataFolder, "Managed"), "Assembly-CSharp.dll");
  44. var enginePath = Path.Combine(Path.Combine(dataFolder, "Managed"), "UnityEngine.dll");
  45. var directToRiftPath = baseName + "_DirectToRift.exe";
  46. try
  47. {
  48. if (directMode)
  49. {
  50. //args[Array.IndexOf(args, "--direct")] = "-force-d3d11";
  51. if (!File.Exists(directToRiftPath))
  52. {
  53. File.WriteAllBytes(directToRiftPath, Resources.DirectToRift);
  54. created = true;
  55. }
  56. file = new FileInfo(directToRiftPath);
  57. }
  58. if (vrMode) args.Add("--vr");
  59. var arguments = string.Join(" ", args.ToArray());
  60. try
  61. {
  62. Patch(assemblyPath, enginePath);
  63. }
  64. catch (Exception ex)
  65. {
  66. MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  67. }
  68. Process.Start(file.FullName, arguments);
  69. }
  70. finally
  71. {
  72. if (created && directMode)
  73. {
  74. var thread = new Thread(new ThreadStart(delegate
  75. {
  76. int attempts = 0;
  77. while (File.Exists(directToRiftPath) && attempts++ < 20)
  78. {
  79. Thread.Sleep(1000);
  80. try
  81. {
  82. File.Delete(directToRiftPath);
  83. }
  84. catch (Exception ex)
  85. {
  86. }
  87. }
  88. }));
  89. thread.Start();
  90. thread.Join();
  91. // Clean up
  92. }
  93. }
  94. }
  95. else
  96. {
  97. MessageBox.Show("Could not find: " + file.FullName, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  98. }
  99. } catch(Exception globalException) {
  100. MessageBox.Show(globalException.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  101. }
  102. }
  103. static string PrepareBackup(FileInfo file)
  104. {
  105. string backup = file.FullName + ".Original";
  106. if (File.Exists(backup))
  107. {
  108. int i = 1;
  109. string backupBase = backup;
  110. while (File.Exists(backup))
  111. {
  112. backup = backupBase + i++;
  113. }
  114. }
  115. return backup;
  116. }
  117. static void Patch(string assemblyFile, string engineFile)
  118. {
  119. var input = new FileInfo(assemblyFile);
  120. var engineInput = new FileInfo(engineFile);
  121. string assemblyBackup = PrepareBackup(input);
  122. string engineBackup = PrepareBackup(engineInput);
  123. if (!input.Exists) Fail("File does not exist.");
  124. var directory = input.DirectoryName;
  125. var injectorPath = Path.Combine(directory, "IllusionInjector.dll");
  126. if (!File.Exists(injectorPath)) Fail("You're missing IllusionInjector.dll. Please make sure to extract all files correctly.");
  127. var resolver = new DefaultAssemblyResolver();
  128. resolver.AddSearchDirectory(directory);
  129. var parameters = new ReaderParameters
  130. {
  131. // SymbolReaderProvider = GetSymbolReaderProvider(),
  132. AssemblyResolver = resolver,
  133. };
  134. var assemblyModule = ModuleDefinition.ReadModule(input.FullName, parameters);
  135. var engineModule = ModuleDefinition.ReadModule(engineInput.FullName, parameters);
  136. if (!IsPatched(engineModule)) //|| !isVirtualized)
  137. {
  138. // Make backup
  139. input.CopyTo(engineBackup);
  140. // First, let's add the reference
  141. var nameReference = new AssemblyNameReference("IllusionInjector", new Version(1, 0, 0, 0));
  142. engineModule.AssemblyReferences.Add(nameReference);
  143. var targetType = FindEntryType(engineModule);
  144. if (targetType == null) Fail("Couldn't find entry class. Aborting.");
  145. var awakeMethod = targetType.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic);
  146. if (awakeMethod == null)
  147. {
  148. Fail("Couldn't find awake method. Aborting.");
  149. }
  150. var injector = ModuleDefinition.ReadModule(injectorPath);
  151. var methodReference = engineModule.Import(injector.GetType("IllusionInjector.Injector").Methods.First(m => m.Name == "Inject"));
  152. //var methodReference = module.GetMemberReferences().FirstOrDefault(r => r.FullName == "IllusionInjector.Injector");
  153. awakeMethod.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, methodReference));
  154. engineModule.Write(engineInput.FullName);
  155. }
  156. if(!IsVirtualized(assemblyModule))
  157. {
  158. input.CopyTo(assemblyBackup);
  159. Virtualize(assemblyModule);
  160. assemblyModule.Write(input.FullName);
  161. }
  162. }
  163. /// <summary>
  164. /// The forbidden deed of the gods -- make ALL methods virtual and public
  165. /// </summary>
  166. /// <param name="module"></param>
  167. private static void Virtualize(ModuleDefinition module)
  168. {
  169. foreach (var type in module.Types)
  170. {
  171. VirtualizeType(type);
  172. }
  173. }
  174. private static void VirtualizeType(TypeDefinition type)
  175. {
  176. if (type.IsSealed) return;
  177. if (type.IsInterface) return;
  178. if (type.IsAbstract) return;
  179. // These two don't seem to work.
  180. if (type.Name == "SceneControl" || type.Name == "ConfigUI") return;
  181. //if (type.FullName.Contains("RootMotion")) return;
  182. //if (type.Methods.Any(m => m.Body != null && m.Body.Variables.Any(v => v.VariableType.FullName.Contains("<")))) return;
  183. //if (!type.FullName.Contains("H_VoiceControl")) return;
  184. //if (!type.FullName.Contains("Human")) return;
  185. //if (type.Namespace.Length > 1) return;
  186. // Take care of sub types
  187. foreach (var subType in type.NestedTypes)
  188. {
  189. VirtualizeType(subType);
  190. }
  191. foreach (var method in type.Methods)
  192. {
  193. Console.WriteLine(method.Name);
  194. if (method.IsManaged
  195. && !TABOO_NAMES.Contains(method.Name)
  196. && method.IsIL
  197. && !method.IsStatic
  198. && !method.IsVirtual
  199. && !method.IsAbstract
  200. && !method.IsAddOn
  201. && !method.IsConstructor
  202. && !method.IsSpecialName
  203. && !method.IsGenericInstance
  204. && !method.HasOverrides)
  205. {
  206. method.IsVirtual = true;
  207. method.IsPublic = true;
  208. method.IsPrivate = false;
  209. method.IsNewSlot = true;
  210. method.IsHideBySig = true;
  211. }
  212. }
  213. foreach (var field in type.Fields)
  214. {
  215. if (field.IsPrivate) field.IsFamily = true;
  216. //field.IsPublic = true;
  217. }
  218. //foreach (var property in type.Properties)
  219. //{
  220. // property.GetMethod.IsVirtual = true;
  221. // property.GetMethod.IsPublic = true;
  222. // property.SetMethod.IsVirtual = true;
  223. // property.SetMethod.IsPublic = true;
  224. //}
  225. }
  226. private static bool IsPatched(ModuleDefinition module)
  227. {
  228. foreach (var @ref in module.AssemblyReferences)
  229. {
  230. if (@ref.Name == "IllusionInjector") return true;
  231. }
  232. return false;
  233. }
  234. private static bool IsVirtualized(ModuleDefinition module)
  235. {
  236. return module.GetTypes().SelectMany(t => t.Methods.Where(m => m.Name == "Awake")).All(m => m.IsVirtual);
  237. }
  238. private static void Fail(string reason) {
  239. throw new Exception(reason);
  240. }
  241. private static TypeDefinition FindEntryType(ModuleDefinition module)
  242. {
  243. return module.GetTypes().FirstOrDefault(IsEntryType);
  244. }
  245. private static bool IsEntryType(TypeDefinition type)
  246. {
  247. return ENTRY_TYPES.Contains(type.Name);
  248. }
  249. }
  250. }