|
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO.Compression;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Threading.Tasks;
- using System.Windows.Forms;
-
- namespace TBMM
- {
- partial class MainForm
- {
- public GameState CheckIfPatched() => CheckIfPatched(out _);
-
- public GameState CheckIfPatched(out bool patched)
- {
- Dictionary<GameState, (string Status, string Extra, string Play)> statusTexts = new()
- {
- { GameState.NotFound, ("Game not found", "Specify the game's location in settings", "") },
- { GameState.InGame, ("Game is running", "", "In-game") },
- { GameState.NoPatcher, ("Patcher missing", "Clicking Play will install it", "") },
- { GameState.OldPatcher, ("Patcher outdated", "nClicking play will update it", "") },
- { GameState.Unpatched, ("Unpatched", "", "") },
- { GameState.Patched, ("Patched", "", "") }
- };
-
- void SetStatusText(GameState state, bool patched)
- {
- var (statusText, extra, play) = statusTexts[state];
- if (extra.Length == 0) extra = patched ? "Cannot join online mode" : "Online mode available";
- if (play.Length == 0) play = "Play modded";
- status.Text = $"Status: {statusText}\n{extra}";
- if (Configuration.KeepPatched)
- status.Text += "\nUnpatch on exit disabled";
- playbtn.Text = play;
- }
-
- if (GetExe() == null)
- {
- patched = false;
- SetStatusText(GameState.NotFound, false);
- return GameState.NotFound;
- }
-
- bool gameIsRunning = CheckIfGameIsRunning();
- if (gameIsRunning)
- {
- UpdateButton(playbtn, false); //Don't allow (un)installing mods if game is running
- UpdateButton(installbtn, false);
- UpdateButton(uninstallbtn, false);
- modlist.Enabled = false;
- }
- else
- {
- if (!working) UpdateButton(playbtn, true);
- modlist.Enabled = true;
- }
-
- GameState GetPatchedState()
- {
- if (!File.Exists(GamePath(@"\IPA.exe")))
- return GameState.NoPatcher;
-
- if (gcipa.Updatable && !(gcipa.Version == new Version(1, 0, 0, 0) &&
- gcipa.LatestVersion == new Version(4, 0, 0, 0))
- && !gameIsRunning)
- return GameState.OldPatcher;
- string gc = GetExe(withExtension: false);
- string backups = GamePath(@"\IPA\Backups\" + gc);
- if (!Directory.Exists(backups))
- return GameState.Unpatched;
- string backup = Directory.EnumerateDirectories(backups)
- .OrderByDescending(Directory.GetLastWriteTimeUtc).FirstOrDefault();
- if (backup == null)
- 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")))
- return GameState.Unpatched;
- return GameState.Patched;
- }
-
- var patchedState = GetPatchedState();
- var finalState = gameIsRunning ? GameState.InGame : patchedState;
- patched = patchedState == GameState.Patched;
- SetStatusText(finalState, patchedState == GameState.Patched);
- return finalState;
- }
-
- public async Task<bool?> 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(status, 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(status);
- 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 TBMM 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(Configuration.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(status, false);
- return retOpenedWindowShouldStay;
- case GameState.Unpatched:
- { //TODO: Wine
- EnsureShown(false);
- var (handler, task) = CheckStartGame(command);
- var process = ExecutePatcher(true);
- 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;
- }
-
- private Process ExecutePatcher(bool patch)
- {
- var psi = new ProcessStartInfo(GamePath(@"\IPA.exe"), $"{GetExe()} --nowait {(patch ? "" : "--revert")}")
- {
- UseShellExecute = false,
- RedirectStandardError = true,
- RedirectStandardOutput = true,
- WorkingDirectory = Configuration.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;
- return process;
- }
-
- public enum GameState
- {
- NotFound,
- NoPatcher,
- OldPatcher,
- Unpatched,
- Patched,
- /// <summary>
- /// Doesn't matter if patched, don't do anything if the game is running
- /// </summary>
- InGame
- }
- }
- }
|