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.

320 rindas
14KB

  1. using GCMM.Properties;
  2. using Newtonsoft.Json.Linq;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Drawing;
  7. using System.IO;
  8. using System.IO.Compression;
  9. using System.Linq;
  10. using System.Net;
  11. using System.Reflection;
  12. using System.Text.RegularExpressions;
  13. using System.Threading.Tasks;
  14. using System.Windows.Forms;
  15. namespace GCMM
  16. {
  17. public partial class MainForm : Form
  18. {
  19. public MainForm()
  20. {
  21. InitializeComponent();
  22. }
  23. private readonly Dictionary<string, ModInfo> mods = new Dictionary<string, ModInfo>();
  24. private readonly ModInfo gcipa = new ModInfo { Author = "modtainers", Name = "GCIPA" };
  25. private readonly ModInfo gcmm = new ModInfo { Author = "NorbiPeti", Name = "GCMM" };
  26. private DateTime lastGameUpdateTime;
  27. #if USING_STEAM
  28. private string steamPath;
  29. #endif
  30. private const string defaultInfo = @"
  31. Techblox Mod Manager
  32. If you click on a mod it will show some info about it. The install instructions there are usually for manual installs.
  33. To get started, click on a mod and select Install mod. Most mods need TechbloxModdingAPI as well so it'll be installed.
  34. Then launch Techblox: if you enabled auto-patching then you can use Steam but if you didn't then you must use the Play button.
  35. This will first download and run the patcher (GCIPA) if needed. If all goes well, after some time a modded Techblox should launch.
  36. After a Techblox update there's a good chance that mods will break. If this happens you may get errors when trying to start Techblox.
  37. Until updated versions are released, use the ""Disable mods"" checkbox at the bottom to launch the game without mods.
  38. If you enabled auto-patching you will get a warning about this.
  39. If you don't have auto-patching enabled then you will need to run the mod manager each time Techblox updates and click ""Patch & Play"".
  40. Disclaimer:
  41. This mod manager and the mods in the list are made by the ExMods developers. We are not associated with Freejam or Techblox. Modify Techblox at your own risk.
  42. If you encounter an issue while any mods are installed, report it to us. If you think it's an issue with the game, test again with the ""Disable mods"" option checked before reporting to Freejam.
  43. You may also want to verify the game's files by clicking on the Validate game button.
  44. ";
  45. private async void Form1_Load(object sender, EventArgs e)
  46. {
  47. if (mods.Values.All(mod => mod.LatestVersion == null)) //Not (fully) loaded yet
  48. await LoadEverything(true);
  49. }
  50. public async Task LoadEverything(bool evenMods)
  51. {
  52. if (Settings.Default.NeedsUpdate)
  53. {
  54. Settings.Default.Upgrade();
  55. Settings.Default.NeedsUpdate = false;
  56. Settings.Default.Save();
  57. }
  58. modlist.Items.Clear();
  59. mods.Clear(); //This method may get called twice when ran from the command line
  60. UpdateButton(installbtn, false);
  61. modinfobox.Text = defaultInfo;
  62. #if USING_STEAM
  63. var (steamPath, user) = GetSteamLocationAndUser();
  64. if (steamPath != null)
  65. this.steamPath = steamPath;
  66. else
  67. {
  68. MessageBox.Show("Steam not found! If you have Steam installed, please report this to ExMods.\n\nThe Steam install is checked to autodetect where the game is installed and to optionally configure auto-patching.", "Steam not found");
  69. status.Text = "Status: Steam not found";
  70. return;
  71. }
  72. if (Settings.Default.SteamUserID == 0 && Settings.Default.AutoLaunch)
  73. {
  74. if (MessageBox.Show("Do you want GCMM to change the game's launch settings so it can ensure the game is patched?\n\n" +
  75. "If you say yes, GCMM will do a quick check before the game is launched and patches if necessary. " +
  76. "This way you (hopefully) won't see crashes after a Techblox update.\n\n" +
  77. "Note that this also means that if you (re)move GCMM without disabling this in the settings then you won't be able to launch Techblox until you change the launch options in Steam.",
  78. "GCMM auto-patching", MessageBoxButtons.YesNo) == DialogResult.Yes)
  79. DetectConfigLocationAndAutoStart(steamPath, ref user);
  80. else
  81. Settings.Default.AutoLaunch = false;
  82. Settings.Default.Save();
  83. }
  84. #endif
  85. if (string.IsNullOrWhiteSpace(Settings.Default.GamePath) || GetExe() == null)
  86. {
  87. Settings.Default.GamePath = GetGameFolder();
  88. if (string.IsNullOrWhiteSpace(Settings.Default.GamePath))
  89. Settings.Default.GamePath = SelectGameFolder();
  90. else
  91. MessageBox.Show("Found game at " + Settings.Default.GamePath);
  92. Settings.Default.Save();
  93. }
  94. if(string.IsNullOrWhiteSpace(Settings.Default.GamePath))
  95. {
  96. status.Text = "Status: Game not found";
  97. return;
  98. }
  99. DeleteEmptyPluginsDir(out bool pexists, out bool dexists);
  100. if (!pexists && dexists)
  101. unpatched.Checked = true; //It will call the event but that won't do anything
  102. await RefreshEverything(evenMods);
  103. }
  104. private async void playbtn_Click(object sender, EventArgs e)
  105. {
  106. if (playbtn.ForeColor == Color.Green) return; //Disabled
  107. await UpdateAPI();
  108. await PatchStartGame(); //It will call EndWork();
  109. }
  110. private void settingsbtn_Click(object sender, EventArgs e)
  111. {
  112. if (settingsbtn.ForeColor == Color.Green) return; //Disabled
  113. var sf = new SettingsForm();
  114. sf.ShowDialog(this);
  115. }
  116. private void modlist_SelectedIndexChanged(object sender, EventArgs e)
  117. {
  118. if (working) return;
  119. modinfobox.Clear();
  120. switch (modlist.SelectedItems.Count)
  121. {
  122. case 0:
  123. modinfobox.Text = defaultInfo;
  124. UpdateButton(installbtn, false);
  125. UpdateButton(uninstallbtn, false);
  126. break;
  127. case 1:
  128. default:
  129. installbtn.Text = "Install mod";
  130. UpdateButton(installbtn, false);
  131. UpdateButton(uninstallbtn, false);
  132. bool install = false, update = false;
  133. Action<string, Color> addText = (txt, color) =>
  134. {
  135. int start = modinfobox.Text.Length;
  136. modinfobox.AppendText(txt + Environment.NewLine + Environment.NewLine);
  137. modinfobox.Select(start, txt.Length);
  138. modinfobox.SelectionColor = color;
  139. modinfobox.DeselectAll();
  140. modinfobox.SelectionColor = modinfobox.ForeColor;
  141. };
  142. foreach (ListViewItem item in modlist.SelectedItems)
  143. {
  144. var mod = mods[item.Name];
  145. if (modlist.SelectedItems.Count == 1)
  146. {
  147. if (mod.Updatable)
  148. addText("New version available! " + mod.UpdateDetails, Color.Aqua);
  149. if (mod.LastUpdated < lastGameUpdateTime)
  150. addText("Outdated mod! It may not work properly on the latest version of the game.", Color.DarkOrange);
  151. if (mod.Description != null)
  152. modinfobox.AppendText(mod.Description.Replace("\n", Environment.NewLine));
  153. }
  154. else
  155. modinfobox.Text = modlist.SelectedItems.Count + " mods selected";
  156. if (mod.DownloadURL != null && !(mod.LatestVersion <= mod.Version))
  157. {
  158. UpdateButton(installbtn, true);
  159. if (mod.Version != null)
  160. update = true;
  161. else
  162. install = true;
  163. }
  164. if (mod.Version != null)
  165. UpdateButton(uninstallbtn, true);
  166. }
  167. if (install && update)
  168. installbtn.Text = "Install and update mod";
  169. else if (update)
  170. installbtn.Text = "Update mod";
  171. else
  172. installbtn.Text = "Install mod";
  173. break;
  174. }
  175. if (unpatched.Checked)
  176. { //Don't allow (un)installing mods if mods are disabled
  177. UpdateButton(installbtn, false);
  178. UpdateButton(uninstallbtn, false);
  179. modlist.Enabled = false;
  180. }
  181. else
  182. modlist.Enabled = true;
  183. }
  184. private async void installbtn_Click(object sender, EventArgs e)
  185. {
  186. if (installbtn.ForeColor == Color.Green) return; //Disabled
  187. if (!BeginWork()) return;
  188. foreach (ListViewItem item in modlist.SelectedItems)
  189. {
  190. var mod = mods[item.Name];
  191. if (item.Group.Name == "installed" && (mod.DownloadURL == null || mod.LatestVersion <= mod.Version)) continue;
  192. await InstallMod(mod);
  193. }
  194. EndWork();
  195. }
  196. private void uninstallbtn_Click(object sender, EventArgs e)
  197. {
  198. if (uninstallbtn.ForeColor == Color.Green) return; //Disabled
  199. foreach (ListViewItem item in modlist.SelectedItems)
  200. {
  201. if (item.Group.Name != "installed") continue;
  202. UninstallMod(mods[item.Name]);
  203. }
  204. EndWork(); //Update button states
  205. }
  206. private void findlog_Click(object sender, EventArgs e)
  207. {
  208. if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  209. {
  210. if (CheckNoExe(out string exe))
  211. return;
  212. Process.Start("explorer.exe", $@"/select,{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}Low\Freejam\{exe.Replace(".exe", "")}\Player.log");
  213. }
  214. }
  215. private void unpatched_CheckedChanged(object sender, EventArgs e)
  216. { //Not using the patcher's revert option because sometimes it restores the wrong files - the game can be patched without mods
  217. if (CheckNoExe())
  218. return;
  219. CheckIfPatched();
  220. modlist_SelectedIndexChanged(modlist, null);
  221. string plugins = GamePath("\\Plugins");
  222. string disabled = GamePath("\\Plugins_Disabled");
  223. DeleteEmptyPluginsDir(out bool pexists, out bool dexists);
  224. if (unpatched.Checked)
  225. {
  226. if (pexists)
  227. {
  228. if (dexists)
  229. Directory.Delete(disabled, true); //Resolving conflicts would be complicated so delete the other mods - this shouldn't happen normally
  230. Directory.Move(plugins, disabled);
  231. }
  232. }
  233. else
  234. {
  235. if (dexists)
  236. {
  237. if (pexists)
  238. Directory.Delete(plugins, true);
  239. Directory.Move(disabled, plugins);
  240. }
  241. }
  242. }
  243. private void DeleteEmptyPluginsDir(out bool pexists, out bool dexists)
  244. {
  245. string plugins = GamePath("\\Plugins");
  246. string disabled = GamePath("\\Plugins_Disabled");
  247. pexists = Directory.Exists(plugins);
  248. dexists = Directory.Exists(disabled);
  249. if (pexists && !Directory.EnumerateFiles(plugins).Any())
  250. {
  251. Directory.Delete(plugins);
  252. pexists = false;
  253. }
  254. if (dexists && !Directory.EnumerateFiles(disabled).Any())
  255. {
  256. Directory.Delete(disabled);
  257. dexists = false;
  258. }
  259. }
  260. private async void refreshbtn_Click(object sender, EventArgs e)
  261. {
  262. await RefreshEverything(true);
  263. }
  264. private async Task RefreshEverything(bool evenMods)
  265. {
  266. CheckIfPatched(); //Set from placeholder
  267. lastGameUpdateTime = await GetLastGameUpdateTime();
  268. var mods = GetInstalledMods();
  269. if (evenMods)
  270. await GetAvailableMods();
  271. CheckUninstalledMods(mods);
  272. CheckIfPatched(); //Check after getting the available mods to show GCIPA updates
  273. }
  274. private void validatebtn_Click(object sender, EventArgs e)
  275. {
  276. if (CheckNoExe())
  277. return;
  278. if (MessageBox.Show("Validating the game's files is useful if the game doesn't start even without mods." +
  279. " Make sure to click Refresh once Steam finished verifying the game." +
  280. " The Steam window that shows the progress might open in the background." +
  281. " Note that if auto-patching isn't enabled then you will need to patch the game again using the Play button in order to use mods." +
  282. "\n\nContinue?", "Verify game files", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
  283. return;
  284. string exe = GetExe();
  285. File.Delete(GamePath($@"\{exe.Replace(".exe", "")}_Data\Managed\IllusionInjector.dll")); //Used to check if game is patched
  286. #if USING_STEAM
  287. if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  288. Process.Start("steam://validate/1078000/");
  289. else
  290. Process.Start("xdg-open", "steam://validate/1078000/");
  291. #endif
  292. }
  293. private void MainForm_Shown(object sender, EventArgs e)
  294. {
  295. Focus();
  296. }
  297. }
  298. }