Techblox Mod Manager / Launcher
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.

183 lines
7.9KB

  1. using GCMM.Properties;
  2. using Newtonsoft.Json.Linq;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.IO.Compression;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Net;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. using System.Windows.Forms;
  13. using System.Reflection;
  14. namespace GCMM
  15. {
  16. partial class MainForm
  17. {
  18. public GameState CheckIfPatched()
  19. {
  20. if (GetExe() == null)
  21. {
  22. status.Text = "Status: Game not found";
  23. return GameState.NotFound;
  24. }
  25. string pnp = "Patch && Play";
  26. if (!File.Exists(GamePath(@"\IPA.exe")))
  27. {
  28. status.Text = "Status: Patcher missing\nClicking Play will install it";
  29. playbtn.Text = pnp;
  30. return GameState.NoPatcher;
  31. }
  32. if (gcipa.Updatable && !(gcipa.Version == new Version(1, 0, 0, 0) && gcipa.LatestVersion == new Version(4, 0, 0, 0)))
  33. {
  34. status.Text = "Status: Patcher outdated\nClicking play will update it";
  35. playbtn.Text = pnp;
  36. return GameState.OldPatcher;
  37. }
  38. string nopatch = "Status: Unpatched\nClicking Play patches it";
  39. string gc = GetExe().Replace(".exe", "");
  40. string backups = GamePath(@"\IPA\Backups\" + gc);
  41. if (!Directory.Exists(backups))
  42. {
  43. status.Text = nopatch;
  44. playbtn.Text = pnp;
  45. return GameState.Unpatched;
  46. }
  47. string backup = Directory.EnumerateDirectories(backups).OrderByDescending(name => Directory.GetLastWriteTimeUtc(name)).FirstOrDefault();
  48. if (backup == null)
  49. {
  50. status.Text = nopatch;
  51. playbtn.Text = pnp;
  52. return GameState.Unpatched;
  53. }
  54. if (File.GetLastWriteTime(GamePath($@"\{gc}_Data\Managed\Assembly-CSharp.dll"))
  55. > //If the file was updated at least 2 minutes after patching
  56. Directory.GetLastWriteTime(backup).AddMinutes(2)
  57. || !File.Exists(GamePath($@"\{gc}_Data\Managed\IllusionInjector.dll")))
  58. {
  59. status.Text = nopatch;
  60. playbtn.Text = pnp;
  61. return GameState.Unpatched;
  62. }
  63. status.Text = "Status: " + (unpatched.Checked ? "Mods disabled" : "Patched");
  64. playbtn.Text = "Play" + (unpatched.Checked ? " unmodded" : "");
  65. return GameState.Patched;
  66. }
  67. public async Task<bool?> PatchStartGame(string command = null)
  68. {
  69. if (!BeginWork()) return false;
  70. foreach (ListViewItem item in modlist.SelectedItems)
  71. item.Selected = false;
  72. bool? retOpenedWindowShouldStay = null;
  73. void EnsureShown(bool stay)
  74. {
  75. if (!Visible)
  76. {
  77. Show();
  78. retOpenedWindowShouldStay = stay;
  79. TopMost = true; //It opens in the background otherwise - should be fine since it only shows for a couple seconds
  80. }
  81. }
  82. var status = CheckIfPatched();
  83. //bool justDownloadedPatcherSoDontWarnAboutIncompatibility = false;
  84. switch (status)
  85. {
  86. case GameState.NotFound:
  87. MessageBox.Show("Techblox not found! Set the correct path in Settings.");
  88. EndWork(false);
  89. return retOpenedWindowShouldStay;
  90. case GameState.NoPatcher:
  91. case GameState.OldPatcher:
  92. {
  93. EnsureShown(false);
  94. if (MessageBox.Show((status == GameState.NoPatcher
  95. ? "The patcher (GCIPA) is not found. It's necessary to load the mods."
  96. : "There is a patcher update available!"
  97. ) + "\n\nIt will be downloaded from https://git.exmods.org/modtainers/GCIPA/releases and ran to patch the game. You can validate the game to restore the original game files or simply disable mods at any time.",
  98. "Patcher download needed", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
  99. {
  100. EndWork();
  101. return retOpenedWindowShouldStay;
  102. }
  103. this.status.Text = "Status: Patching...";
  104. int C = 0;
  105. while (gcipa.DownloadURL == null && C < 20)
  106. await Task.Delay(500); //The EnsureShown() call should download info about GCIPA
  107. if (gcipa.DownloadURL == null)
  108. {
  109. MessageBox.Show("Could not get information about GCIPA in time. Please run GCMM manually.");
  110. return retOpenedWindowShouldStay;
  111. }
  112. using (WebClient client = GetClient())
  113. {
  114. string url = gcipa.DownloadURL;
  115. await client.DownloadFileTaskAsync(url, "IPA.zip");
  116. using (var fs = new FileStream("IPA.zip", FileMode.Open))
  117. using (var za = new ZipArchive(fs))
  118. za.ExtractToDirectory(Settings.Default.GamePath, true); //Overwrite files that were left from a previous install of the patcher
  119. File.Delete("IPA.zip");
  120. }
  121. }
  122. GetInstalledMods(); //Update patcher state, should be fine for this rare event
  123. status = CheckIfPatched();
  124. break;
  125. }
  126. switch (status)
  127. {
  128. case GameState.NoPatcher: //Make sure it actually worked
  129. case GameState.OldPatcher:
  130. EndWork(false);
  131. return retOpenedWindowShouldStay;
  132. case GameState.Unpatched:
  133. { //TODO: Wine
  134. EnsureShown(false);
  135. var psi = new ProcessStartInfo(GamePath(@"\IPA.exe"), GetExe() + " --nowait")
  136. {
  137. UseShellExecute = false,
  138. RedirectStandardError = true,
  139. RedirectStandardOutput = true,
  140. WorkingDirectory = Settings.Default.GamePath,
  141. CreateNoWindow = true
  142. };
  143. var process = Process.Start(psi);
  144. process.BeginErrorReadLine();
  145. process.BeginOutputReadLine();
  146. process.EnableRaisingEvents = true;
  147. modinfobox.Text = "";
  148. DataReceivedEventHandler onoutput = (sender, e) =>
  149. {
  150. Invoke((Action)(() => modinfobox.Text += e.Data + Environment.NewLine));
  151. };
  152. process.OutputDataReceived += onoutput;
  153. process.ErrorDataReceived += onoutput;
  154. var (handler, task) = CheckStartGame(command);
  155. process.Exited += handler;
  156. await task;
  157. }
  158. break;
  159. case GameState.Patched:
  160. {
  161. //CheckStartGame(command)(null, null);
  162. var (handler, task) = CheckStartGame(command);
  163. handler(null, null);
  164. await task;
  165. }
  166. break;
  167. }
  168. return retOpenedWindowShouldStay;
  169. }
  170. public enum GameState
  171. {
  172. NotFound,
  173. NoPatcher,
  174. OldPatcher,
  175. Unpatched,
  176. Patched
  177. }
  178. }
  179. }