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.

268 lines
8.7KB

  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 to IPA ({0})... ", context.IPA);
  85. Shortcut.Create(
  86. fileName: context.ShortcutPath,
  87. targetPath: context.IPA,
  88. arguments: Args(context.Executable, "--launch"),
  89. workingDirectory: context.ProjectRoot,
  90. description: "Launches the game and makes sure it's in a patched state",
  91. hotkey: "",
  92. iconPath: context.Executable
  93. );
  94. Console.WriteLine("Created");
  95. }
  96. }
  97. catch (Exception e)
  98. {
  99. Fail("Oops! This should not have happened.\n\n" + e);
  100. }
  101. Console.WriteLine("Finished!");
  102. }
  103. private static void Revert(PatchContext context)
  104. {
  105. Console.Write("Restoring game assembly... ");
  106. if(BackupManager.Restore(context.AssemblyFile))
  107. {
  108. Console.WriteLine("Done!");
  109. } else
  110. {
  111. Console.WriteLine("Already vanilla!");
  112. }
  113. Console.Write("Restoring unity engine... ");
  114. if(BackupManager.Restore(context.EngineFile))
  115. {
  116. Console.WriteLine("Done!");
  117. }
  118. else
  119. {
  120. Console.WriteLine("Already vanilla!");
  121. }
  122. if (File.Exists(context.ShortcutPath))
  123. {
  124. Console.WriteLine("Deleting shortcut...");
  125. File.Delete(context.ShortcutPath);
  126. }
  127. Console.WriteLine("");
  128. Console.WriteLine("--- Done reverting ---");
  129. if (!Environment.CommandLine.Contains("--nowait"))
  130. {
  131. Console.WriteLine("\n\n[Press any key to quit]");
  132. Console.ReadKey();
  133. }
  134. }
  135. private static void StartIfNeedBe(PatchContext context)
  136. {
  137. var argList = context.Args.ToList();
  138. bool launch = argList.Remove("--launch");
  139. argList.RemoveAt(0);
  140. if(launch)
  141. {
  142. Process.Start(context.Executable, Args(argList.ToArray()));
  143. }
  144. }
  145. public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
  146. {
  147. Directory.CreateDirectory(target.FullName);
  148. // Copy each file into the new directory.
  149. foreach (FileInfo fi in source.GetFiles())
  150. {
  151. string targetFile = Path.Combine(target.FullName, fi.Name);
  152. if (!File.Exists(targetFile) || File.GetLastWriteTimeUtc(targetFile) < fi.LastWriteTimeUtc)
  153. {
  154. Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name);
  155. fi.CopyTo(Path.Combine(target.FullName, fi.Name), true);
  156. }
  157. }
  158. // Copy each subdirectory using recursion.
  159. foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
  160. {
  161. DirectoryInfo nextTargetSubDir =
  162. target.CreateSubdirectory(diSourceSubDir.Name);
  163. CopyAll(diSourceSubDir, nextTargetSubDir);
  164. }
  165. }
  166. static void Fail(string message)
  167. {
  168. Console.Error.Write("ERROR: " + message);
  169. if (!Environment.CommandLine.Contains("--nowait"))
  170. {
  171. Console.WriteLine("\n\n[Press any key to quit]");
  172. Console.ReadKey();
  173. }
  174. Environment.Exit(1);
  175. }
  176. public static string Args(params string[] args)
  177. {
  178. return string.Join(" ", args.Select(EncodeParameterArgument).ToArray());
  179. }
  180. /// <summary>
  181. /// Encodes an argument for passing into a program
  182. /// </summary>
  183. /// <param name="original">The value that should be received by the program</param>
  184. /// <returns>The value which needs to be passed to the program for the original value
  185. /// to come through</returns>
  186. public static string EncodeParameterArgument(string original)
  187. {
  188. if (string.IsNullOrEmpty(original))
  189. return original;
  190. string value = Regex.Replace(original, @"(\\*)" + "\"", @"$1\$0");
  191. value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\"");
  192. return value;
  193. }
  194. public abstract class Keyboard
  195. {
  196. [Flags]
  197. private enum KeyStates
  198. {
  199. None = 0,
  200. Down = 1,
  201. Toggled = 2
  202. }
  203. [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
  204. private static extern short GetKeyState(int keyCode);
  205. private static KeyStates GetKeyState(Keys key)
  206. {
  207. KeyStates state = KeyStates.None;
  208. short retVal = GetKeyState((int)key);
  209. //If the high-order bit is 1, the key is down
  210. //otherwise, it is up.
  211. if ((retVal & 0x8000) == 0x8000)
  212. state |= KeyStates.Down;
  213. //If the low-order bit is 1, the key is toggled.
  214. if ((retVal & 1) == 1)
  215. state |= KeyStates.Toggled;
  216. return state;
  217. }
  218. public static bool IsKeyDown(Keys key)
  219. {
  220. return KeyStates.Down == (GetKeyState(key) & KeyStates.Down);
  221. }
  222. public static bool IsKeyToggled(Keys key)
  223. {
  224. return KeyStates.Toggled == (GetKeyState(key) & KeyStates.Toggled);
  225. }
  226. }
  227. }
  228. }