Techblox Mod Manager / Launcher
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

221 lines
8.5KB

  1. using System;
  2. using System.Diagnostics;
  3. using System.Drawing;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Threading.Tasks;
  8. using System.Windows.Forms;
  9. using Microsoft.Win32;
  10. namespace TBMM
  11. {
  12. partial class MainForm
  13. {
  14. public void UpdateButton(Button button, bool enabled)
  15. {
  16. if (enabled)
  17. {
  18. button.ForeColor = Color.Lime;
  19. button.FlatAppearance.MouseOverBackColor = Color.FromArgb(0, 40, 0);
  20. button.FlatAppearance.MouseDownBackColor = Color.Green;
  21. }
  22. else
  23. {
  24. button.ForeColor = Color.Green;
  25. button.FlatAppearance.MouseOverBackColor = Color.Black;
  26. button.FlatAppearance.MouseDownBackColor = Color.Black;
  27. }
  28. }
  29. public string GetGameFolder()
  30. {
  31. using var key =
  32. Registry.LocalMachine.OpenSubKey(
  33. @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Techblox Launcher") ??
  34. Registry.LocalMachine.OpenSubKey(
  35. @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Techblox Launcher");
  36. string launcherPath = key?.GetValue("DisplayIcon") is string launcherExecutable
  37. ? Directory.GetParent(launcherExecutable)?.FullName
  38. : null;
  39. launcherPath ??= Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
  40. "Techblox Launcher");
  41. string launcherConfig = Path.Combine(launcherPath, "launcher_settings.ini");
  42. if (!File.Exists(launcherConfig)) return null;
  43. string path = File.ReadLines(launcherConfig)
  44. .FirstOrDefault(line => line.StartsWith("133062..GAME_PATH="))
  45. ?.Substring("133062..GAME_PATH=".Length).Replace("/TBMM/", "/");
  46. if (path != null) path = (path + "StandaloneWindows64").Replace('/', Path.DirectorySeparatorChar);
  47. if (path != null && GetExe(path) != null) return path;
  48. return null;
  49. }
  50. public string SelectGameFolder()
  51. {
  52. var ofd = new OpenFileDialog();
  53. ofd.Filter = "Techblox executable|Techblox.exe|Techblox Preview executable|TechbloxPreview.exe";
  54. ofd.Title = "Game location";
  55. ofd.CheckFileExists = true;
  56. ofd.ShowDialog();
  57. return string.IsNullOrWhiteSpace(ofd.FileName) ? null : Directory.GetParent(ofd.FileName)?.FullName;
  58. }
  59. private (EventHandler, Task) CheckStartGame(string command)
  60. {
  61. var tcs = new TaskCompletionSource<object>();
  62. return ((sender, e) =>
  63. {
  64. Action act = async () =>
  65. {
  66. if (((sender as Process)?.ExitCode ?? 0) != 0)
  67. {
  68. status.Text = "Status: Patching failed";
  69. return;
  70. }
  71. if (CheckIfPatched() == GameState.Patched || unpatched.Checked)
  72. if (command != null)
  73. {
  74. if (sender is Process) //Patched just now
  75. CheckCompatibilityAndDisableMods();
  76. await CheckModUpdatesAsync();
  77. Process.Start(command);
  78. }
  79. else if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  80. Process.Start(new ProcessStartInfo(GamePath("\\" + GetExe()))
  81. {
  82. WorkingDirectory = GamePath("\\") //Mods are only loaded if the working directory is correct
  83. });
  84. EndWork(false);
  85. tcs.SetResult(null);
  86. };
  87. if (InvokeRequired)
  88. Invoke(act);
  89. else
  90. act();
  91. }, tcs.Task);
  92. }
  93. private void CheckCompatibilityAndDisableMods()
  94. {
  95. if (!unpatched.Checked && MessageBox.Show("If the game updated just now, some mods may be incompatible or they may work just fine." +
  96. " Do you want to try running with mods?" +
  97. "\n\nClick Yes to start the game with mods (after a small update or if you just installed TBMM)" +
  98. "\nClick No to disable mods before starting the game (after a major update)" +
  99. "\n\nYou can always enable/disable mods by launching TBMM.",
  100. "Possible incompatibility warning", MessageBoxButtons.YesNo) == DialogResult.No)
  101. unpatched.Checked = true;
  102. }
  103. private async Task CheckModUpdatesAsync()
  104. {
  105. var updatable = mods.Values.Where(mod => mod.Updatable).ToArray();
  106. if (updatable.Length == 0)
  107. return;
  108. if (MessageBox.Show("Mod update(s) available!\n\n"
  109. + updatable.Select(mod => mod.Name + " " + mod.LatestVersion).Aggregate((a, b) => a + "\n")
  110. + "\n\nDo you want to update them now? You can also update later by opening TBMM.",
  111. "Update(s) available", MessageBoxButtons.YesNo) == DialogResult.No)
  112. return;
  113. foreach (var mod in updatable)
  114. await InstallMod(mod);
  115. MessageBox.Show("Mods updated");
  116. }
  117. public WebClient GetClient()
  118. {
  119. var client = new WebClient();
  120. client.Headers.Clear();
  121. client.Headers[HttpRequestHeader.Accept] = "application/json";
  122. client.BaseAddress = "https://git.exmods.org";
  123. return client;
  124. }
  125. private bool working = false;
  126. /// <summary>
  127. /// Some simple "locking", only allow one operation at a time
  128. /// </summary>
  129. /// <returns>Whether the work can begin</returns>
  130. public bool BeginWork()
  131. {
  132. if (working) return false;
  133. working = true;
  134. UpdateButton(playbtn, false);
  135. UpdateButton(installbtn, false);
  136. UpdateButton(uninstallbtn, false);
  137. UpdateButton(settingsbtn, false);
  138. unpatched.Enabled = false;
  139. return true;
  140. }
  141. public void EndWork(bool desc = true)
  142. {
  143. working = false;
  144. UpdateButton(playbtn, true);
  145. UpdateButton(settingsbtn, true);
  146. if (desc)
  147. modlist_SelectedIndexChanged(modlist, null);
  148. unpatched.Enabled = true;
  149. }
  150. /// <summary>
  151. /// Path must start with \
  152. /// </summary>
  153. /// <param name="path"></param>
  154. /// <param name="gamepath"></param>
  155. /// <returns></returns>
  156. public string GamePath(string path, string gamepath = null)
  157. {
  158. return ((gamepath ?? Configuration.GamePath) + path).Replace('\\', Path.DirectorySeparatorChar);
  159. }
  160. public string GetExe(string path = null)
  161. {
  162. if (File.Exists(GamePath("\\Techblox.exe", path)))
  163. return "Techblox.exe";
  164. if (File.Exists(GamePath("\\TechbloxPreview.exe", path)))
  165. return "TechbloxPreview.exe";
  166. return null;
  167. }
  168. private bool CheckNoExe()
  169. {
  170. return CheckNoExe(out _);
  171. }
  172. private bool CheckNoExe(out string path)
  173. {
  174. path = GetExe();
  175. if (path == null)
  176. {
  177. MessageBox.Show("Techblox not found! Set the correct path in Settings.");
  178. return true;
  179. }
  180. return false;
  181. }
  182. public DateTime GetGameVersionAsDate()
  183. {
  184. if (Configuration.GamePath == null) return default;
  185. using var fs = File.OpenRead(GamePath("\\TechbloxPreview_Data\\globalgamemanagers"));
  186. using var sr = new StreamReader(fs);
  187. char[] data = new char[512];
  188. while(!sr.EndOfStream)
  189. {
  190. Array.Copy(data, 256, data, 0, 256);
  191. int read = sr.ReadBlock(data, 256, 256);
  192. for (int i = 0; i < data.Length - 11; i++)
  193. {
  194. if (data[i] == '2')
  195. {
  196. string date = new string(data, i, 11);
  197. if (date.StartsWith("202") && DateTime.TryParse(date, out var ret))
  198. return ret;
  199. }
  200. }
  201. }
  202. return default;
  203. }
  204. }
  205. }