Techblox Mod Manager / Launcher
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

203 wiersze
8.3KB

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