using GCMM.Properties; using Microsoft.Win32; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; namespace GCMM { partial class MainForm { public void UpdateButton(Button button, bool enabled) { if (enabled) { button.ForeColor = Color.Lime; button.FlatAppearance.MouseOverBackColor = Color.FromArgb(0, 40, 0); button.FlatAppearance.MouseDownBackColor = Color.Green; } else { button.ForeColor = Color.Green; button.FlatAppearance.MouseOverBackColor = Color.Black; button.FlatAppearance.MouseDownBackColor = Color.Black; } } public string GetGameFolder() { string libs; if (Environment.OSVersion.Platform == PlatformID.Win32NT) libs = steamPath + @"\steamapps\libraryfolders.vdf"; else return null; foreach (var line in File.ReadAllLines(libs).Concat(new[] { "\t\"19\"\t\t\"" + steamPath + "\"" })) { var regex = new Regex("\\t\"\\d+\"\\t\\t\"(.+)\""); var match = regex.Match(line); if (!match.Success) continue; string library = match.Groups[1].Value.Replace("\\\\", "\\"); library += @"\steamapps\common\"; if (GetExe(library + "Gamecraft") != null) return library + "Gamecraft"; if (GetExe(library + "RobocraftX") != null) return library + "RobocraftX"; } return null; } public string SelectGameFolder() { var ofd = new OpenFileDialog(); ofd.Filter = "Gamecraft executable|Gamecraft.exe|Gamecraft Preview executable|GamecraftPreview.exe"; ofd.Title = "Game location"; ofd.InitialDirectory = steamPath + @"\steamapps\common\"; ofd.CheckFileExists = true; ofd.ShowDialog(); if (string.IsNullOrWhiteSpace(ofd.FileName)) return null; return Directory.GetParent(ofd.FileName).FullName; } public (string, int) AskForSteamLogin() { while (MessageBox.Show("Couid not find your Steam configuration to set launch options.\n\n" + "Please make sure you are logged into Steam and click Retry or click Cancel to skip setting this up.", "Steam config not found", MessageBoxButtons.RetryCancel) != DialogResult.Cancel) { var ret = GetSteamLocationAndUser(); if (ret != (null, 0)) return ret; } return (null, 0); } /// /// Does not return the current value if setting it is not possible because Steam is running. /// /// The value to set or null to keep as is /// The current value, unless setting it while Steam is running public bool UpdateOrGetSteamConfigToAutoStart(bool? autoLaunch) { string commandToUse = Application.ExecutablePath + " -start %command%"; if (autoLaunch.HasValue && Process.GetProcessesByName("steam").Length > 0) { //Setting it while Steam is running if (MessageBox.Show("Cannot set launch options while Steam is running." + (autoLaunch.Value ? " Do you want to do it manually? If so, it will be copied on your clipboard." + " Right click the game, select Properties and paste it in the launch options field." : " Do you want to do it manually?" + " If so, right click the game, select Properties and remove the text from the launch options field."), "Launch options in Steam", MessageBoxButtons.YesNo) == DialogResult.Yes && autoLaunch.Value) Clipboard.SetText(commandToUse); return false; } var regex = new Regex(@"(\t{6}""LaunchOptions""\t+"")(.*)("")"); string path = steamPath + @"\userdata\" + Settings.Default.SteamUserID + @"\config\localconfig.vdf"; var lines = File.ReadAllLines(path); bool shouldMatch = false; bool ret = false; for (int i = 0; i < lines.Length; i++) { if (lines[i] == "\t\t\t\t\t\"1078000\"") shouldMatch = true; //Found the game else if(shouldMatch) { var match = regex.Match(lines[i]); if (!match.Success) continue; ret = match.Groups[2].Value.Contains("GCMM.exe"); string enabledCommand = match.Groups[1].Value + commandToUse.Replace("\\", "\\\\") + match.Groups[3].Value; if (autoLaunch.HasValue) { if (autoLaunch.Value) lines[i] = enabledCommand; else lines[i] = match.Groups[1].Value + match.Groups[3].Value; File.WriteAllLines(path, lines); } else if (ret && lines[i] != enabledCommand) //GCMM got moved or something and it's only queried, not set { lines[i] = enabledCommand; File.WriteAllLines(path, lines); } break; } } return ret; } public (string, int) GetSteamLocationAndUser() { if (Environment.OSVersion.Platform != PlatformID.Win32NT) return (null, 0); using (var key = Registry.CurrentUser.OpenSubKey(@"Software\Valve\Steam\ActiveProcess")) { string path = (string)key?.GetValue("SteamClientDll"); path = path != null ? Directory.GetParent(path).FullName : null; return (path, (int)(key?.GetValue("ActiveUser") ?? 0)); } } public void DetectConfigLocationAndAutoStart(string steamPath, ref int user) { string path = steamPath + @"\userdata"; if (user == 0) { var dirs = Directory.GetDirectories(path); var goodPaths = (from dir in dirs where File.Exists(dir + @"\config\localconfig.vdf") select dir).ToArray(); if (goodPaths.Length != 1) { (_, user) = AskForSteamLogin(); path += user; } else { path = goodPaths[0]; user = int.Parse(Path.GetFileName(path)); } } else path += "\\" + user; path += @"\config\localconfig.vdf"; if (path != null && user != 0 && File.Exists(path)) { Settings.Default.SteamUserID = user; UpdateOrGetSteamConfigToAutoStart(true); } else Settings.Default.AutoLaunch = false; } private (EventHandler, Task) CheckStartGame(string command) { var tcs = new TaskCompletionSource(); return ((sender, e) => { Action act = async () => { if (((sender as Process)?.ExitCode ?? 0) != 0) { status.Text = "Status: Patching failed"; return; } if (CheckIfPatched() == GameState.Patched || unpatched.Checked) if (command != null) { if (sender is Process) //Patched just now CheckCompatibilityAndDisableMods(); await CheckModUpdatesAsync(); Process.Start(command); } else if (Environment.OSVersion.Platform == PlatformID.Win32NT) Process.Start("steam://run/1078000/"); else Process.Start("xdg-open", "steam://run/1078000/"); EndWork(false); tcs.SetResult(null); }; if (InvokeRequired) Invoke(act); else act(); }, tcs.Task); } private void CheckCompatibilityAndDisableMods() { if (!unpatched.Checked && MessageBox.Show("If the game updated just now, some mods may be incompatible or they may work just fine." + " Do you want to try running with mods?" + "\n\nClick Yes to start the game with mods (after a small update or if you just installed GCMM)" + "\nClick No to disable mods before starting the game (after a major update)" + "\n\nYou can always enable/disable mods by launching GCMM.", "Possible incompatibility warning", MessageBoxButtons.YesNo) == DialogResult.No) unpatched.Checked = 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 GCMM.", "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(); if (!Settings.Default.UseProxy) client.Proxy = null; client.Headers.Clear(); client.Headers[HttpRequestHeader.Accept] = "application/json"; client.BaseAddress = "https://git.exmods.org"; return client; } private bool working = false; /// /// Some simple "locking", only allow one operation at a time /// /// Whether the work can begin public bool BeginWork() { if (working) return false; working = true; UpdateButton(playbtn, false); UpdateButton(installbtn, false); UpdateButton(uninstallbtn, false); UpdateButton(settingsbtn, false); unpatched.Enabled = false; return true; } public void EndWork(bool desc = true) { working = false; UpdateButton(playbtn, true); UpdateButton(settingsbtn, true); if (desc) modlist_SelectedIndexChanged(modlist, null); unpatched.Enabled = true; } /// /// Path must start with \ /// /// /// /// public string GamePath(string path, string gamepath = null) { return ((gamepath ?? Settings.Default.GamePath) + path).Replace('\\', Path.DirectorySeparatorChar); } public string GetExe(string path = null) { if (File.Exists(GamePath("\\Gamecraft.exe", path))) return "Gamecraft.exe"; if (File.Exists(GamePath("\\GamecraftPreview.exe", path))) return "GamecraftPreview.exe"; return null; } private bool CheckNoExe() { return CheckNoExe(out _); } private bool CheckNoExe(out string path) { path = GetExe(); if (path == null) { MessageBox.Show("Gamecraft not found! Set the correct path in Settings."); return true; } return false; } public async Task GetLastGameUpdateTime() { /*using (var client = GetClient()) { string html = await client.DownloadStringTaskAsync("https://api.steamcmd.net/v1/info/1078000"); var regex = new Regex("timeupdated:[^<]*([^<]*)"); var match = regex.Match(html); if (!match.Success) return default; return new DateTime(1970, 1, 1).AddSeconds(long.Parse(match.Groups[1].Value)); }*/ //return new DateTime(2020, 12, 28); return default; } } }