|
- using System;
- using System.Diagnostics;
- using System.Drawing;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- using Microsoft.Win32;
-
- namespace TBMM
- {
- partial class MainForm
- {
- public void UpdateButton(Button button, bool enabled)
- {
- if (enabled)
- {
- if (button.ForeColor == Color.Green)
- button.ForeColor = Color.Lime;
- else if(button.ForeColor == Color.DarkRed)
- button.ForeColor = Color.Red;
- else if (button.ForeColor == Color.FromArgb(127, 65, 0))
- button.ForeColor = Color.DarkOrange;
- button.FlatAppearance.MouseOverBackColor = Color.FromArgb(0, 40, 0);
- button.FlatAppearance.MouseDownBackColor = Color.Green;
- }
- else
- {
- if (button.ForeColor == Color.Lime)
- button.ForeColor = Color.Green;
- else if (button.ForeColor == Color.Red)
- button.ForeColor = Color.DarkRed;
- else if (button.ForeColor == Color.DarkOrange)
- button.ForeColor = Color.FromArgb(127, 65, 0);
- button.FlatAppearance.MouseOverBackColor = Color.Black;
- button.FlatAppearance.MouseDownBackColor = Color.Black;
- }
- }
-
- public string GetGameFolder()
- {
- using var key =
- Registry.LocalMachine.OpenSubKey(
- @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Techblox Launcher") ??
- Registry.LocalMachine.OpenSubKey(
- @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Techblox Launcher");
- string launcherPath = key?.GetValue("DisplayIcon") is string launcherExecutable
- ? Directory.GetParent(launcherExecutable)?.FullName
- : null;
- launcherPath ??= Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
- "Techblox Launcher");
- string launcherConfig = Path.Combine(launcherPath, "launcher_settings.ini");
- if (!File.Exists(launcherConfig)) return null;
- string path = File.ReadLines(launcherConfig)
- .FirstOrDefault(line => line.StartsWith("133062..GAME_PATH="))
- ?.Substring("133062..GAME_PATH=".Length).Replace("/TBMM/", "/");
- if (path != null) path = (path + "StandaloneWindows64").Replace('/', Path.DirectorySeparatorChar);
- if (path != null && GetExe(path) != null) return path;
- return null;
- }
-
- public string SelectGameFolder()
- {
- var ofd = new OpenFileDialog();
- ofd.Filter = "Techblox executable|Techblox.exe|Techblox Preview executable|TechbloxPreview.exe";
- ofd.Title = "Game location";
- ofd.CheckFileExists = true;
- ofd.ShowDialog();
- return string.IsNullOrWhiteSpace(ofd.FileName) ? null : Directory.GetParent(ofd.FileName)?.FullName;
- }
-
- private (EventHandler, Task) CheckStartGame(string command)
- {
- var tcs = new TaskCompletionSource<object>();
- return ((sender, e) =>
- {
- Action act = async () =>
- {
- if (((sender as Process)?.ExitCode ?? 0) != 0)
- {
- status.Text = "Status: Patching failed";
- return;
- }
-
- var patched = CheckIfPatched();
- if (patched == GameState.Patched)
- {
- Process process = null;
- if (command != null)
- {
- await CheckModUpdatesAsync();
- process = Process.Start(command);
- }
- else if (Environment.OSVersion.Platform == PlatformID.Win32NT)
- {
- process = Process.Start(new ProcessStartInfo(GamePath("\\" + GetExe()))
- {
- WorkingDirectory = GamePath("\\") //Mods are only loaded if the working directory is correct
- });
- }
- if (process is null)
- throw new NullReferenceException("Game process is null");
- process.EnableRaisingEvents = true;
- process.Exited += HandleGameExit;
- _gameProcess = (process, true);
- patched = CheckIfPatched(); // Set in-game status
- }
-
- EndWork(patched, false);
- tcs.SetResult(null);
- };
- if (InvokeRequired)
- Invoke(act);
- else
- act();
- }, tcs.Task);
- }
-
- private void HandleGameExit(object sender, EventArgs e)
- {
- Debug.WriteLine("Handling game exit");
- _gameProcess = (null, false);
- if (InvokeMethod(CheckIfPatched) != GameState.Patched || Configuration.KeepPatched)
- return;
- InvokeMethod(() => ExecutePatcher(false)).Exited += (_, _) =>
- {
- if (InvokeMethod(CheckIfPatched) == GameState.Patched)
- {
- MessageBox.Show("Failed to unpatch game, launching through the launcher will fail because of anticheat. " +
- "Check the output in the panel on the right.\n\n" +
- "Please try starting the game again by clicking Play.", "Patcher error",
- MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- };
- }
-
- private (Process Process, bool ExitHandlerAdded) _gameProcess = (null, false);
-
- private bool CheckIfGameIsRunning()
- {
- Debug.WriteLine($"Check if game is running: {_gameProcess}");
- switch (_gameProcess.Process)
- {
- case { HasExited: false }:
- Debug.WriteLine("Game has not exited");
- return true;
- case { HasExited: true }:
- Debug.WriteLine("Game has seemingly exited without handling, probably as part of the exit handler");
- return false;
- default:
- Debug.WriteLine($"Process seems to be null: {_gameProcess}");
- _gameProcess = (Process.GetProcessesByName(GetExe(withExtension: false)).FirstOrDefault(), false);
- Debug.WriteLine($"Game process exited already, got new process object: {_gameProcess}");
-
- if (_gameProcess.Process == null) return false;
- if (_gameProcess.Process.HasExited)
- {
- Debug.WriteLine($"Game has exited already: {_gameProcess}");
- _gameProcess = (null, false);
- return false;
- }
- else
- {
- Debug.WriteLine($"Game is still running");
- if (_gameProcess.ExitHandlerAdded) return true;
- Debug.WriteLine("Game running and no exit handler yet, adding it");
- _gameProcess.Process.Exited += HandleGameExit;
- _gameProcess.Process.EnableRaisingEvents = true;
- _gameProcess.ExitHandlerAdded = true;
- return true;
- }
- }
- }
-
- private async Task CheckModUpdatesAsync()
- {
- var updatable = mods.Values.Where(mod => mod.Updatable).ToArray();
- if (updatable.Length == 0)
- return;
- if (MessageBox.Show("Mod update(s) available!\n\n"
- + updatable.Select(mod => mod.Name + " " + mod.LatestVersion).Aggregate((a, b) => a + "\n")
- + "\n\nDo you want to update them now? You can also update later by opening TBMM.",
- "Update(s) available", MessageBoxButtons.YesNo) == DialogResult.No)
- return;
- foreach (var mod in updatable)
- await InstallMod(mod);
- MessageBox.Show("Mods updated");
- }
-
- public WebClient GetClient()
- {
- var client = new WebClient();
- client.Headers.Clear();
- client.Headers[HttpRequestHeader.Accept] = "application/json";
- client.BaseAddress = "https://git.exmods.org";
- return client;
- }
-
- public T InvokeMethod<T>(Func<T> func)
- {
- if (InvokeRequired)
- return (T)Invoke(func);
- else
- return func();
- }
-
- private bool working = false;
- /// <summary>
- /// Some simple "locking", only allow one operation at a time
- /// </summary>
- /// <returns>Whether the work can begin</returns>
- public bool BeginWork()
- {
- if (working) return false;
- working = true;
- UpdateButton(playbtn, false);
- UpdateButton(installbtn, false);
- UpdateButton(uninstallbtn, false);
- UpdateButton(settingsbtn, false);
- return true;
- }
-
- public void EndWork(GameState state, bool desc = true)
- {
- working = false;
- if (state != GameState.InGame)
- UpdateButton(playbtn, true);
- UpdateButton(settingsbtn, true);
- if (desc)
- modlist_SelectedIndexChanged(modlist, null);
- }
-
- /// <summary>
- /// Path must start with \
- /// </summary>
- /// <param name="path"></param>
- /// <param name="gamepath"></param>
- /// <returns></returns>
- public string GamePath(string path, string gamepath = null)
- {
- return ((gamepath ?? Configuration.GamePath) + path).Replace('\\', Path.DirectorySeparatorChar);
- }
-
- public string GetExe(string path = null, bool withExtension = true)
- {
- if (File.Exists(GamePath("\\Techblox.exe", path)))
- return "Techblox" + (withExtension ? ".exe" : "");
- if (File.Exists(GamePath("\\TechbloxPreview.exe", path)))
- return "TechbloxPreview" + (withExtension ? ".exe" : "");
- return null;
- }
-
- private bool CheckNoExe()
- {
- return CheckNoExe(out _);
- }
-
- private bool CheckNoExe(out string path)
- {
- path = GetExe();
- if (path == null)
- {
- MessageBox.Show("Techblox not found! Set the correct path in Settings.");
- return true;
- }
- return false;
- }
-
- public DateTime GetGameVersionAsDate()
- {
- if (Configuration.GamePath == null) return default;
- using var fs = File.OpenRead(GamePath($"\\{GetExe(withExtension: false)}_Data\\globalgamemanagers"));
- using var sr = new StreamReader(fs);
- char[] data = new char[512];
- while(!sr.EndOfStream)
- {
- Array.Copy(data, 256, data, 0, 256);
- int read = sr.ReadBlock(data, 256, 256);
- for (int i = 0; i < data.Length - 11; i++)
- {
- if (data[i] == '2')
- {
- string date = new string(data, i, 11);
- if (date.StartsWith("202") && DateTime.TryParse(date, out var ret))
- return ret;
- }
- }
- }
-
- return default;
- }
- }
- }
|