Techblox Mod Manager / Launcher
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

342 rindas
13KB

  1. using GCMM.Properties;
  2. using Microsoft.Win32;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Drawing;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Net;
  10. using System.Text;
  11. using System.Text.RegularExpressions;
  12. using System.Threading.Tasks;
  13. using System.Windows.Forms;
  14. namespace GCMM
  15. {
  16. partial class MainForm
  17. {
  18. public void UpdateButton(Button button, bool enabled)
  19. {
  20. if (enabled)
  21. {
  22. button.ForeColor = Color.Lime;
  23. button.FlatAppearance.MouseOverBackColor = Color.FromArgb(0, 40, 0);
  24. button.FlatAppearance.MouseDownBackColor = Color.Green;
  25. }
  26. else
  27. {
  28. button.ForeColor = Color.Green;
  29. button.FlatAppearance.MouseOverBackColor = Color.Black;
  30. button.FlatAppearance.MouseDownBackColor = Color.Black;
  31. }
  32. }
  33. public string GetGameFolder()
  34. {
  35. #if USING_STEAM
  36. string libs;
  37. if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  38. libs = steamPath + @"\steamapps\libraryfolders.vdf";
  39. else
  40. return null;
  41. foreach (var line in File.ReadAllLines(libs).Concat(new[] { "\t\"19\"\t\t\"" + steamPath + "\"" }))
  42. {
  43. var regex = new Regex("\\t\"\\d+\"\\t\\t\"(.+)\"");
  44. var match = regex.Match(line);
  45. if (!match.Success)
  46. continue;
  47. string library = match.Groups[1].Value.Replace("\\\\", "\\");
  48. library += @"\steamapps\common\";
  49. if (GetExe(library + "Techblox") != null)
  50. return library + "Techblox";
  51. }
  52. return null;
  53. #endif
  54. return null; //TODO
  55. }
  56. public string SelectGameFolder()
  57. {
  58. var ofd = new OpenFileDialog();
  59. ofd.Filter = "Techblox executable|Techblox.exe|Techblox Preview executable|TechbloxPreview.exe";
  60. ofd.Title = "Game location";
  61. #if USING_STEAM
  62. ofd.InitialDirectory = steamPath + @"\steamapps\common\";
  63. #endif //TODO
  64. ofd.CheckFileExists = true;
  65. ofd.ShowDialog();
  66. if (string.IsNullOrWhiteSpace(ofd.FileName))
  67. return null;
  68. return Directory.GetParent(ofd.FileName).FullName;
  69. }
  70. #if USING_STEAM
  71. public (string, int) AskForSteamLogin()
  72. {
  73. while (MessageBox.Show("Couid not find your Steam configuration to set launch options.\n\n" +
  74. "Please make sure you are logged into Steam and click Retry or click Cancel to skip setting this up.",
  75. "Steam config not found", MessageBoxButtons.RetryCancel) != DialogResult.Cancel)
  76. {
  77. var ret = GetSteamLocationAndUser();
  78. if (ret != (null, 0))
  79. return ret;
  80. }
  81. return (null, 0);
  82. }
  83. /// <summary>
  84. /// Does not return the current value if setting it is not possible because Steam is running.
  85. /// </summary>
  86. /// <param name="autoLaunch">The value to set or null to keep as is</param>
  87. /// <returns>The current value, unless setting it while Steam is running</returns>
  88. public bool UpdateOrGetSteamConfigToAutoStart(bool? autoLaunch)
  89. {
  90. string commandToUse = Application.ExecutablePath + " -start %command%";
  91. if (autoLaunch.HasValue && Process.GetProcessesByName("steam").Length > 0)
  92. { //Setting it while Steam is running
  93. if (MessageBox.Show("Cannot set launch options while Steam is running." +
  94. (autoLaunch.Value
  95. ? " Do you want to do it manually? If so, it will be copied on your clipboard." +
  96. " Right click the game, select Properties and paste it in the launch options field."
  97. : " Do you want to do it manually?" +
  98. " If so, right click the game, select Properties and remove the text from the launch options field."),
  99. "Launch options in Steam", MessageBoxButtons.YesNo) == DialogResult.Yes && autoLaunch.Value)
  100. Clipboard.SetText(commandToUse);
  101. return false;
  102. }
  103. var regex = new Regex(@"(\t{6}""LaunchOptions""\t+"")(.*)("")");
  104. string path = steamPath + @"\userdata\" + Settings.Default.SteamUserID + @"\config\localconfig.vdf";
  105. var lines = File.ReadAllLines(path);
  106. bool shouldMatch = false;
  107. bool ret = false;
  108. for (int i = 0; i < lines.Length; i++)
  109. {
  110. if (lines[i] == "\t\t\t\t\t\"1078000\"")
  111. shouldMatch = true; //Found the game
  112. else if(shouldMatch)
  113. {
  114. var match = regex.Match(lines[i]);
  115. if (!match.Success)
  116. continue;
  117. ret = match.Groups[2].Value.Contains("GCMM.exe");
  118. string enabledCommand = match.Groups[1].Value + commandToUse.Replace("\\", "\\\\") + match.Groups[3].Value;
  119. if (autoLaunch.HasValue)
  120. {
  121. if (autoLaunch.Value)
  122. lines[i] = enabledCommand;
  123. else
  124. lines[i] = match.Groups[1].Value + match.Groups[3].Value;
  125. File.WriteAllLines(path, lines);
  126. }
  127. else if (ret && lines[i] != enabledCommand) //GCMM got moved or something and it's only queried, not set
  128. {
  129. lines[i] = enabledCommand;
  130. File.WriteAllLines(path, lines);
  131. }
  132. break;
  133. }
  134. }
  135. return ret;
  136. }
  137. public (string, int) GetSteamLocationAndUser()
  138. {
  139. if (Environment.OSVersion.Platform != PlatformID.Win32NT) return (null, 0);
  140. using (var key = Registry.CurrentUser.OpenSubKey(@"Software\Valve\Steam\ActiveProcess"))
  141. {
  142. string path = (string)key?.GetValue("SteamClientDll");
  143. path = path != null ? Directory.GetParent(path).FullName : null;
  144. return (path, (int)(key?.GetValue("ActiveUser") ?? 0));
  145. }
  146. }
  147. public void DetectConfigLocationAndAutoStart(string steamPath, ref int user)
  148. {
  149. string path = steamPath + @"\userdata";
  150. if (user == 0)
  151. {
  152. var dirs = Directory.GetDirectories(path);
  153. var goodPaths = (from dir in dirs
  154. where File.Exists(dir + @"\config\localconfig.vdf")
  155. select dir).ToArray();
  156. if (goodPaths.Length != 1)
  157. {
  158. (_, user) = AskForSteamLogin();
  159. path += user;
  160. }
  161. else
  162. {
  163. path = goodPaths[0];
  164. user = int.Parse(Path.GetFileName(path));
  165. }
  166. }
  167. else
  168. path += "\\" + user;
  169. path += @"\config\localconfig.vdf";
  170. if (path != null && user != 0 && File.Exists(path))
  171. {
  172. Settings.Default.SteamUserID = user;
  173. UpdateOrGetSteamConfigToAutoStart(true);
  174. }
  175. else
  176. Settings.Default.AutoLaunch = false;
  177. }
  178. #endif
  179. private (EventHandler, Task) CheckStartGame(string command)
  180. {
  181. var tcs = new TaskCompletionSource<object>();
  182. return ((sender, e) =>
  183. {
  184. Action act = async () =>
  185. {
  186. if (((sender as Process)?.ExitCode ?? 0) != 0)
  187. {
  188. status.Text = "Status: Patching failed";
  189. return;
  190. }
  191. if (CheckIfPatched() == GameState.Patched || unpatched.Checked)
  192. if (command != null)
  193. {
  194. if (sender is Process) //Patched just now
  195. CheckCompatibilityAndDisableMods();
  196. await CheckModUpdatesAsync();
  197. Process.Start(command);
  198. }
  199. #if USING_STEAM
  200. else if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  201. Process.Start("steam://run/1078000/");
  202. else
  203. Process.Start("xdg-open", "steam://run/1078000/");
  204. #else
  205. if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  206. Process.Start(GamePath("\\" + GetExe()));
  207. #endif
  208. EndWork(false);
  209. tcs.SetResult(null);
  210. };
  211. if (InvokeRequired)
  212. Invoke(act);
  213. else
  214. act();
  215. }, tcs.Task);
  216. }
  217. private void CheckCompatibilityAndDisableMods()
  218. {
  219. if (!unpatched.Checked && MessageBox.Show("If the game updated just now, some mods may be incompatible or they may work just fine." +
  220. " Do you want to try running with mods?" +
  221. "\n\nClick Yes to start the game with mods (after a small update or if you just installed GCMM)" +
  222. "\nClick No to disable mods before starting the game (after a major update)" +
  223. "\n\nYou can always enable/disable mods by launching GCMM.",
  224. "Possible incompatibility warning", MessageBoxButtons.YesNo) == DialogResult.No)
  225. unpatched.Checked = true;
  226. }
  227. private async Task CheckModUpdatesAsync()
  228. {
  229. var updatable = mods.Values.Where(mod => mod.Updatable).ToArray();
  230. if (updatable.Length == 0)
  231. return;
  232. if (MessageBox.Show("Mod update(s) available!\n\n"
  233. + updatable.Select(mod => mod.Name + " " + mod.LatestVersion).Aggregate((a, b) => a + "\n")
  234. + "\n\nDo you want to update them now? You can also update later by opening GCMM.",
  235. "Update(s) available", MessageBoxButtons.YesNo) == DialogResult.No)
  236. return;
  237. foreach (var mod in updatable)
  238. await InstallMod(mod);
  239. MessageBox.Show("Mods updated");
  240. }
  241. public WebClient GetClient()
  242. {
  243. var client = new WebClient();
  244. if (!Settings.Default.UseProxy)
  245. client.Proxy = null;
  246. client.Headers.Clear();
  247. client.Headers[HttpRequestHeader.Accept] = "application/json";
  248. client.BaseAddress = "https://git.exmods.org";
  249. return client;
  250. }
  251. private bool working = false;
  252. /// <summary>
  253. /// Some simple "locking", only allow one operation at a time
  254. /// </summary>
  255. /// <returns>Whether the work can begin</returns>
  256. public bool BeginWork()
  257. {
  258. if (working) return false;
  259. working = true;
  260. UpdateButton(playbtn, false);
  261. UpdateButton(installbtn, false);
  262. UpdateButton(uninstallbtn, false);
  263. UpdateButton(settingsbtn, false);
  264. unpatched.Enabled = false;
  265. return true;
  266. }
  267. public void EndWork(bool desc = true)
  268. {
  269. working = false;
  270. UpdateButton(playbtn, true);
  271. UpdateButton(settingsbtn, true);
  272. if (desc)
  273. modlist_SelectedIndexChanged(modlist, null);
  274. unpatched.Enabled = true;
  275. }
  276. /// <summary>
  277. /// Path must start with \
  278. /// </summary>
  279. /// <param name="path"></param>
  280. /// <param name="gamepath"></param>
  281. /// <returns></returns>
  282. public string GamePath(string path, string gamepath = null)
  283. {
  284. return ((gamepath ?? Settings.Default.GamePath) + path).Replace('\\', Path.DirectorySeparatorChar);
  285. }
  286. public string GetExe(string path = null)
  287. {
  288. if (File.Exists(GamePath("\\Techblox.exe", path)))
  289. return "Techblox.exe";
  290. if (File.Exists(GamePath("\\TechbloxPreview.exe", path)))
  291. return "TechbloxPreview.exe";
  292. return null;
  293. }
  294. private bool CheckNoExe()
  295. {
  296. return CheckNoExe(out _);
  297. }
  298. private bool CheckNoExe(out string path)
  299. {
  300. path = GetExe();
  301. if (path == null)
  302. {
  303. MessageBox.Show("Techblox not found! Set the correct path in Settings.");
  304. return true;
  305. }
  306. return false;
  307. }
  308. public async Task<DateTime> GetLastGameUpdateTime()
  309. {
  310. /*using (var client = GetClient())
  311. {
  312. string html = await client.DownloadStringTaskAsync("https://api.steamcmd.net/v1/info/1078000");
  313. var regex = new Regex("<i>timeupdated:</i>[^<]*<b>([^<]*)</b>");
  314. var match = regex.Match(html);
  315. if (!match.Success)
  316. return default;
  317. return new DateTime(1970, 1, 1).AddSeconds(long.Parse(match.Groups[1].Value));
  318. }*/
  319. //return new DateTime(2020, 12, 28);
  320. return default;
  321. }
  322. }
  323. }