using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Threading.Tasks; using System.Windows.Forms; namespace TBMM { public partial class MainForm : Form { public MainForm() { InitializeComponent(); resources = new ResourceManager("TBMM.Localization", Assembly.GetExecutingAssembly()); Configuration = Configuration.Load(); } public Configuration Configuration { get; } private readonly ResourceManager resources; private readonly Dictionary mods = new Dictionary(); private readonly ModInfo gcipa = new ModInfo { Author = "modtainers", Name = "GCIPA" }; private readonly ModInfo tbmm = new ModInfo { Author = "NorbiPeti", Name = "TBMM" }; private DateTime lastGameUpdateTime; private const string defaultInfo = @" Techblox Mod Manager If you click on a mod it will show some info about it. The install instructions there are usually for manual installs. To get started, click on a mod and select Install mod. Most mods need TechbloxModdingAPI as well so it'll be installed. Then launch Techblox by clicking on the Play button below. Mods are only loaded if you start the game from here. This will first download and run the patcher (GCIPA) if needed. If all goes well, after some time a modded Techblox should launch. 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. Until updated versions are released, use the ""Disable mods"" checkbox at the bottom to launch the game without mods. If you launch the game through the launcher after an update and encounter an error, either repair the game or launch it through the mod manager. Disclaimer: 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. 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. You may also want to verify the game's files in the launcher. "; private async void Form1_Load(object sender, EventArgs e) { if (mods.Values.All(mod => mod.LatestVersion == null)) //Not (fully) loaded yet await LoadEverything(true); } public async Task LoadEverything(bool evenMods) { modlist.Items.Clear(); mods.Clear(); //This method may get called twice when ran from the command line UpdateButton(installbtn, false); modinfobox.Text = defaultInfo; if (string.IsNullOrWhiteSpace(Configuration.GamePath) || GetExe() == null) { Configuration.GamePath = GetGameFolder(); if (string.IsNullOrWhiteSpace(Configuration.GamePath)) { DialogUtils.ShowWarning(resources.GetString("Game_not_found"), ""); Configuration.GamePath = SelectGameFolder(); } else DialogUtils.ShowInfo(string.Format(resources.GetString("Found_game_at"), Configuration.GamePath), ""); Configuration.Save(); } if(string.IsNullOrWhiteSpace(Configuration.GamePath)) { status.Text = resources.GetString("Status_Game_not_found"); return; } DeleteEmptyPluginsDir(out bool pexists, out bool dexists); if (!pexists && dexists) unpatched.Checked = true; //It will call the event but that won't do anything await RefreshEverything(evenMods); } private async void playbtn_Click(object sender, EventArgs e) { if (playbtn.ForeColor == Color.Green) return; //Disabled await UpdateAPI(); await PatchStartGame(); //It will call EndWork(); } private void settingsbtn_Click(object sender, EventArgs e) { if (settingsbtn.ForeColor == Color.Green) return; //Disabled var sf = new SettingsForm(); sf.ShowDialog(this); } private void modlist_SelectedIndexChanged(object sender, EventArgs e) { if (working) return; modinfobox.Clear(); switch (modlist.SelectedItems.Count) { case 0: modinfobox.Text = defaultInfo; UpdateButton(installbtn, false); UpdateButton(uninstallbtn, false); break; case 1: default: installbtn.Text = "Install mod"; UpdateButton(installbtn, false); UpdateButton(uninstallbtn, false); bool install = false, update = false; Action addText = (txt, color) => { int start = modinfobox.Text.Length; modinfobox.AppendText(txt + Environment.NewLine + Environment.NewLine); modinfobox.Select(start, txt.Length); modinfobox.SelectionColor = color; modinfobox.DeselectAll(); modinfobox.SelectionColor = modinfobox.ForeColor; }; foreach (ListViewItem item in modlist.SelectedItems) { var mod = mods[item.Name]; if (modlist.SelectedItems.Count == 1) { if (mod.Updatable) addText("New version available! " + mod.UpdateDetails, Color.Aqua); if (mod.LastUpdated != default && mod.LastUpdated < lastGameUpdateTime) addText("Outdated mod! It may not work properly on the current version of the game.", Color.DarkOrange); if (mod.Description != null) modinfobox.AppendText(mod.Description.Replace("\n", Environment.NewLine)); } else modinfobox.Text = modlist.SelectedItems.Count + " mods selected"; if (mod.DownloadURL != null && !(mod.LatestVersion <= mod.Version)) { UpdateButton(installbtn, true); if (mod.Version != null) update = true; else install = true; } if (mod.Version != null) UpdateButton(uninstallbtn, true); } if (install && update) installbtn.Text = "Install and update mod"; else if (update) installbtn.Text = "Update mod"; else installbtn.Text = "Install mod"; break; } if (unpatched.Checked) { //Don't allow (un)installing mods if mods are disabled UpdateButton(installbtn, false); UpdateButton(uninstallbtn, false); modlist.Enabled = false; } else modlist.Enabled = true; } private async void installbtn_Click(object sender, EventArgs e) { if (installbtn.ForeColor == Color.Green) return; //Disabled if (!BeginWork()) return; foreach (ListViewItem item in modlist.SelectedItems) { var mod = mods[item.Name]; if (item.Group.Name == "installed" && (mod.DownloadURL == null || mod.LatestVersion <= mod.Version)) continue; await InstallMod(mod); } EndWork(); } private void uninstallbtn_Click(object sender, EventArgs e) { if (uninstallbtn.ForeColor == Color.Green) return; //Disabled foreach (ListViewItem item in modlist.SelectedItems) { if (item.Group.Name != "installed") continue; UninstallMod(mods[item.Name]); } EndWork(); //Update button states } private void findlog_Click(object sender, EventArgs e) { if (Environment.OSVersion.Platform == PlatformID.Win32NT) { if (CheckNoExe(out string exe)) return; Process.Start("explorer.exe", $@"/select,{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}Low\Freejam\{exe.Replace(".exe", "")}\Player.log"); } } private void unpatched_CheckedChanged(object sender, EventArgs e) { //Not using the patcher's revert option because sometimes it restores the wrong files - the game can be patched without mods if (CheckNoExe()) return; CheckIfPatched(); modlist_SelectedIndexChanged(modlist, null); string plugins = GamePath("\\Plugins"); string disabled = GamePath("\\Plugins_Disabled"); DeleteEmptyPluginsDir(out bool pexists, out bool dexists); if (unpatched.Checked) { if (pexists) { if (dexists) Directory.Delete(disabled, true); //Resolving conflicts would be complicated so delete the other mods - this shouldn't happen normally Directory.Move(plugins, disabled); } } else { if (dexists) { if (pexists) Directory.Delete(plugins, true); Directory.Move(disabled, plugins); } } } private void DeleteEmptyPluginsDir(out bool pexists, out bool dexists) { string plugins = GamePath("\\Plugins"); string disabled = GamePath("\\Plugins_Disabled"); pexists = Directory.Exists(plugins); dexists = Directory.Exists(disabled); if (pexists && !Directory.EnumerateFiles(plugins).Any()) { Directory.Delete(plugins); pexists = false; } if (dexists && !Directory.EnumerateFiles(disabled).Any()) { Directory.Delete(disabled); dexists = false; } } private async void refreshbtn_Click(object sender, EventArgs e) { await RefreshEverything(true); } private async Task RefreshEverything(bool evenMods) { CheckIfPatched(); //Set from placeholder lastGameUpdateTime = GetGameVersionAsDate(); var mods = GetInstalledMods(); if (evenMods) await GetAvailableMods(); CheckUninstalledMods(mods); CheckIfPatched(); //Check after getting the available mods to show GCIPA updates } private void MainForm_Shown(object sender, EventArgs e) { Focus(); } } }