using GCMM.Properties; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO.Compression; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Reflection; namespace GCMM { partial class MainForm { public GameState CheckIfPatched() { if (GetExe() == null) { status.Text = "Status: Game not found"; return GameState.NotFound; } string pnp = "Patch && Play"; if (!File.Exists(GamePath(@"\IPA.exe"))) { status.Text = "Status: Patcher missing\nClicking Play will install it"; playbtn.Text = pnp; return GameState.NoPatcher; } if (gcipa.Updatable && !(gcipa.Version == new Version(1, 0, 0, 0) && gcipa.LatestVersion == new Version(4, 0, 0, 0))) { status.Text = "Status: Patcher outdated\nClicking play will update it"; playbtn.Text = pnp; return GameState.OldPatcher; } string nopatch = "Status: Unpatched\nClicking Play patches it"; string gc = GetExe().Replace(".exe", ""); string backups = GamePath(@"\IPA\Backups\" + gc); if (!Directory.Exists(backups)) { status.Text = nopatch; playbtn.Text = pnp; return GameState.Unpatched; } string backup = Directory.EnumerateDirectories(backups).OrderByDescending(name => Directory.GetLastWriteTimeUtc(name)).FirstOrDefault(); if (backup == null) { status.Text = nopatch; playbtn.Text = pnp; return GameState.Unpatched; } if (File.GetLastWriteTime(GamePath($@"\{gc}_Data\Managed\Assembly-CSharp.dll")) > //If the file was updated at least 2 minutes after patching Directory.GetLastWriteTime(backup).AddMinutes(2) || !File.Exists(GamePath($@"\{gc}_Data\Managed\IllusionInjector.dll"))) { status.Text = nopatch; playbtn.Text = pnp; return GameState.Unpatched; } status.Text = "Status: " + (unpatched.Checked ? "Mods disabled" : "Patched"); playbtn.Text = "Play" + (unpatched.Checked ? " unmodded" : ""); return GameState.Patched; } public async Task PatchStartGame(string command = null) { if (!BeginWork()) return false; foreach (ListViewItem item in modlist.SelectedItems) item.Selected = false; bool? retOpenedWindowShouldStay = null; void EnsureShown(bool stay) { if (!Visible) { Show(); retOpenedWindowShouldStay = stay; TopMost = true; //It opens in the background otherwise - should be fine since it only shows for a couple seconds } } var status = CheckIfPatched(); //bool justDownloadedPatcherSoDontWarnAboutIncompatibility = false; switch (status) { case GameState.NotFound: MessageBox.Show("Techblox not found! Set the correct path in Settings."); EndWork(false); return retOpenedWindowShouldStay; case GameState.NoPatcher: case GameState.OldPatcher: { EnsureShown(false); if (MessageBox.Show((status == GameState.NoPatcher ? "The patcher (GCIPA) is not found. It's necessary to load the mods." : "There is a patcher update available!" ) + "\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.", "Patcher download needed", MessageBoxButtons.OKCancel) == DialogResult.Cancel) { EndWork(); return retOpenedWindowShouldStay; } this.status.Text = "Status: Patching..."; int C = 0; while (gcipa.DownloadURL == null && C < 20) await Task.Delay(500); //The EnsureShown() call should download info about GCIPA if (gcipa.DownloadURL == null) { MessageBox.Show("Could not get information about GCIPA in time. Please run GCMM manually."); return retOpenedWindowShouldStay; } using (WebClient client = GetClient()) { string url = gcipa.DownloadURL; await client.DownloadFileTaskAsync(url, "IPA.zip"); using (var fs = new FileStream("IPA.zip", FileMode.Open)) using (var za = new ZipArchive(fs)) za.ExtractToDirectory(Settings.Default.GamePath, true); //Overwrite files that were left from a previous install of the patcher File.Delete("IPA.zip"); } } GetInstalledMods(); //Update patcher state, should be fine for this rare event status = CheckIfPatched(); break; } switch (status) { case GameState.NoPatcher: //Make sure it actually worked case GameState.OldPatcher: EndWork(false); return retOpenedWindowShouldStay; case GameState.Unpatched: { //TODO: Wine EnsureShown(false); var psi = new ProcessStartInfo(GamePath(@"\IPA.exe"), GetExe() + " --nowait") { UseShellExecute = false, RedirectStandardError = true, RedirectStandardOutput = true, WorkingDirectory = Settings.Default.GamePath, CreateNoWindow = true }; var process = Process.Start(psi); process.BeginErrorReadLine(); process.BeginOutputReadLine(); process.EnableRaisingEvents = true; modinfobox.Text = ""; DataReceivedEventHandler onoutput = (sender, e) => { Invoke((Action)(() => modinfobox.Text += e.Data + Environment.NewLine)); }; process.OutputDataReceived += onoutput; process.ErrorDataReceived += onoutput; var (handler, task) = CheckStartGame(command); process.Exited += handler; await task; } break; case GameState.Patched: { //CheckStartGame(command)(null, null); var (handler, task) = CheckStartGame(command); handler(null, null); await task; } break; } return retOpenedWindowShouldStay; } public enum GameState { NotFound, NoPatcher, OldPatcher, Unpatched, Patched } } }