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.

259 lines
8.5KB

  1. using IPA.Patcher;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Runtime.InteropServices;
  9. using System.Text;
  10. using System.Text.RegularExpressions;
  11. using System.Windows.Forms;
  12. namespace IPA
  13. {
  14. class Program
  15. {
  16. static void Main(string[] args)
  17. {
  18. if(args.Length < 1 || !args[0].EndsWith(".exe"))
  19. {
  20. Fail("Drag an (executable) file on the exe!");
  21. }
  22. try
  23. {
  24. var context = PatchContext.Create(args);
  25. bool isRevert = args.Contains("--revert") || Keyboard.IsKeyDown(Keys.LMenu);
  26. // Sanitizing
  27. Validate(context);
  28. if (isRevert)
  29. {
  30. Revert(context);
  31. }
  32. else
  33. {
  34. Install(context);
  35. StartIfNeedBe(context);
  36. }
  37. } catch(Exception e)
  38. {
  39. Fail(e.Message);
  40. }
  41. }
  42. private static void Validate(PatchContext c)
  43. {
  44. if (!File.Exists(c.LauncherPathSrc)) Fail("Couldn't find DLLs! Make sure you extracted all contents of the release archive.");
  45. if (!Directory.Exists(c.DataPathDst) || !File.Exists(c.EngineFile) || !File.Exists(c.AssemblyFile))
  46. {
  47. Fail("Game does not seem to be a Unity project. Could not find the libraries to patch.");
  48. }
  49. }
  50. private static void Install(PatchContext context)
  51. {
  52. try
  53. {
  54. // Copying
  55. Console.Write("Updating files... ");
  56. CopyAll(new DirectoryInfo(context.DataPathSrc), new DirectoryInfo(context.DataPathDst));
  57. Console.WriteLine("Successfully updated files!");
  58. if (!Directory.Exists(context.PluginsFolder))
  59. {
  60. Console.WriteLine("Creating plugins folder... ");
  61. Directory.CreateDirectory(context.PluginsFolder);
  62. }
  63. // Patching
  64. var patchedModule = PatchedModule.Load(context.EngineFile);
  65. if (!patchedModule.IsPatched)
  66. {
  67. Console.Write("Patching UnityEngine.dll... ");
  68. BackupManager.MakeBackup(context.EngineFile);
  69. patchedModule.Patch();
  70. Console.WriteLine("Done!");
  71. }
  72. // Virtualizing
  73. var virtualizedModule = VirtualizedModule.Load(context.AssemblyFile);
  74. if (!virtualizedModule.IsVirtualized)
  75. {
  76. Console.Write("Virtualizing Assembly-Csharp.dll... ");
  77. BackupManager.MakeBackup(context.AssemblyFile);
  78. virtualizedModule.Virtualize();
  79. Console.WriteLine("Done!");
  80. }
  81. // Creating shortcut
  82. if(!File.Exists(context.ShortcutPath))
  83. {
  84. Console.Write("Creating shortcut... ");
  85. Shortcut.Create(context.ShortcutPath, Assembly.GetExecutingAssembly().Location, Args(context.Executable, "--launch"), context.ProjectRoot, "Launches the game and makes sure it's in a patched state", "", context.Executable);
  86. Console.WriteLine("Created");
  87. }
  88. }
  89. catch (Exception e)
  90. {
  91. Fail("Oops! This should not have happened.\n\n" + e);
  92. }
  93. Console.WriteLine("Finished!");
  94. }
  95. private static void Revert(PatchContext context)
  96. {
  97. Console.Write("Restoring game assembly... ");
  98. if(BackupManager.Restore(context.AssemblyFile))
  99. {
  100. Console.WriteLine("Done!");
  101. } else
  102. {
  103. Console.WriteLine("Already vanilla!");
  104. }
  105. Console.Write("Restoring unity engine... ");
  106. if(BackupManager.Restore(context.EngineFile))
  107. {
  108. Console.WriteLine("Done!");
  109. }
  110. else
  111. {
  112. Console.WriteLine("Already vanilla!");
  113. }
  114. if (File.Exists(context.ShortcutPath))
  115. {
  116. Console.WriteLine("Deleting shortcut...");
  117. File.Delete(context.ShortcutPath);
  118. }
  119. Console.WriteLine("");
  120. Console.WriteLine("--- Done reverting ---");
  121. if (!Environment.CommandLine.Contains("--nowait"))
  122. {
  123. Console.WriteLine("\n\n[Press any key to quit]");
  124. Console.ReadKey();
  125. }
  126. }
  127. private static void StartIfNeedBe(PatchContext context)
  128. {
  129. var argList = context.Args.ToList();
  130. bool launch = argList.Remove("--launch");
  131. argList.RemoveAt(0);
  132. if(launch)
  133. {
  134. Process.Start(context.Executable, Args(argList.ToArray()));
  135. }
  136. }
  137. public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
  138. {
  139. Directory.CreateDirectory(target.FullName);
  140. // Copy each file into the new directory.
  141. foreach (FileInfo fi in source.GetFiles())
  142. {
  143. string targetFile = Path.Combine(target.FullName, fi.Name);
  144. if (!File.Exists(targetFile) || File.GetLastWriteTimeUtc(targetFile) < fi.LastWriteTimeUtc)
  145. {
  146. Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name);
  147. fi.CopyTo(Path.Combine(target.FullName, fi.Name), true);
  148. }
  149. }
  150. // Copy each subdirectory using recursion.
  151. foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
  152. {
  153. DirectoryInfo nextTargetSubDir =
  154. target.CreateSubdirectory(diSourceSubDir.Name);
  155. CopyAll(diSourceSubDir, nextTargetSubDir);
  156. }
  157. }
  158. static void Fail(string message)
  159. {
  160. Console.Error.Write("ERROR: " + message);
  161. if (!Environment.CommandLine.Contains("--nowait"))
  162. {
  163. Console.WriteLine("\n\n[Press any key to quit]");
  164. Console.ReadKey();
  165. }
  166. Environment.Exit(1);
  167. }
  168. public static string Args(params string[] args)
  169. {
  170. return string.Join(" ", args.Select(EncodeParameterArgument).ToArray());
  171. }
  172. /// <summary>
  173. /// Encodes an argument for passing into a program
  174. /// </summary>
  175. /// <param name="original">The value that should be received by the program</param>
  176. /// <returns>The value which needs to be passed to the program for the original value
  177. /// to come through</returns>
  178. public static string EncodeParameterArgument(string original)
  179. {
  180. if (string.IsNullOrEmpty(original))
  181. return original;
  182. string value = Regex.Replace(original, @"(\\*)" + "\"", @"$1\$0");
  183. value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\"");
  184. return value;
  185. }
  186. public abstract class Keyboard
  187. {
  188. [Flags]
  189. private enum KeyStates
  190. {
  191. None = 0,
  192. Down = 1,
  193. Toggled = 2
  194. }
  195. [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
  196. private static extern short GetKeyState(int keyCode);
  197. private static KeyStates GetKeyState(Keys key)
  198. {
  199. KeyStates state = KeyStates.None;
  200. short retVal = GetKeyState((int)key);
  201. //If the high-order bit is 1, the key is down
  202. //otherwise, it is up.
  203. if ((retVal & 0x8000) == 0x8000)
  204. state |= KeyStates.Down;
  205. //If the low-order bit is 1, the key is toggled.
  206. if ((retVal & 1) == 1)
  207. state |= KeyStates.Toggled;
  208. return state;
  209. }
  210. public static bool IsKeyDown(Keys key)
  211. {
  212. return KeyStates.Down == (GetKeyState(key) & KeyStates.Down);
  213. }
  214. public static bool IsKeyToggled(Keys key)
  215. {
  216. return KeyStates.Toggled == (GetKeyState(key) & KeyStates.Toggled);
  217. }
  218. }
  219. }
  220. }