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.

MainPatcher.cs 7.8KB

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