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.

227 lines
8.5KB

  1. using GCMM.Properties;
  2. using Newtonsoft.Json.Linq;
  3. using System;
  4. using System.Diagnostics;
  5. using System.Drawing;
  6. using System.IO;
  7. using System.IO.Compression;
  8. using System.Linq;
  9. using System.Net;
  10. using System.Reflection;
  11. using System.Text.RegularExpressions;
  12. using System.Threading.Tasks;
  13. using System.Windows.Forms;
  14. namespace GCMM
  15. {
  16. public partial class MainForm : Form
  17. {
  18. public MainForm()
  19. {
  20. InitializeComponent();
  21. }
  22. private void Form1_Load(object sender, EventArgs e)
  23. {
  24. modlist.Items.Clear();
  25. UpdateButton(installbtn, false);
  26. modinfobox.Text = "";
  27. if (string.IsNullOrWhiteSpace(Settings.Default.GamePath))
  28. {
  29. Settings.Default.GamePath = GetGameFolder();
  30. if (string.IsNullOrWhiteSpace(Settings.Default.GamePath))
  31. Settings.Default.GamePath = SelectGameFolder();
  32. else
  33. MessageBox.Show("Found game at " + Settings.Default.GamePath);
  34. Settings.Default.Save();
  35. }
  36. if(string.IsNullOrWhiteSpace(Settings.Default.GamePath))
  37. {
  38. status.Text = "Status: Game not found";
  39. return;
  40. }
  41. CheckIfPatched();
  42. GetInstalledMods();
  43. }
  44. public void UpdateButton(Button button, bool enabled)
  45. {
  46. if (enabled)
  47. {
  48. button.ForeColor = Color.Lime;
  49. button.FlatAppearance.MouseOverBackColor = Color.FromArgb(0, 40, 0);
  50. button.FlatAppearance.MouseDownBackColor = Color.Green;
  51. }
  52. else
  53. {
  54. button.ForeColor = Color.Green;
  55. button.FlatAppearance.MouseOverBackColor = Color.Black;
  56. button.FlatAppearance.MouseDownBackColor = Color.Black;
  57. }
  58. }
  59. public string GetGameFolder()
  60. {
  61. string libs;
  62. if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  63. libs = @"C:\Program Files (x86)\Steam\steamapps\libraryfolders.vdf";
  64. else
  65. return null;
  66. foreach (var line in File.ReadAllLines(libs).Concat(new[] { @"C:\Program Files (x86)\Steam\" }))
  67. {
  68. var regex = new Regex("\\t\"\\d+\"\\t\\t\"(.+)\"");
  69. var match = regex.Match(line);
  70. if (!match.Success)
  71. continue;
  72. string library = match.Groups[1].Value.Replace("\\\\", "\\");
  73. library += @"\steamapps\common\";
  74. if (File.Exists(library + @"Gamecraft\Gamecraft.exe"))
  75. return library + "Gamecraft";
  76. if (File.Exists(library + @"RobocraftX\Gamecraft.exe"))
  77. return library + "RobocraftX";
  78. }
  79. return libs;
  80. }
  81. public string SelectGameFolder()
  82. {
  83. var ofd = new OpenFileDialog();
  84. ofd.Filter = "Gamecraft executable|Gamecraft.exe";
  85. ofd.Title = "Game location";
  86. ofd.InitialDirectory = @"C:\Program Files (x86)\Steam\steamapps\common\";
  87. ofd.CheckFileExists = true;
  88. ofd.ShowDialog();
  89. if (string.IsNullOrWhiteSpace(ofd.FileName))
  90. return null;
  91. return Directory.GetParent(ofd.FileName).FullName;
  92. }
  93. public void GetInstalledMods()
  94. {
  95. foreach (var mod in Directory.GetFiles(Settings.Default.GamePath + @"\Plugins", "*.dll"))
  96. {
  97. try
  98. {
  99. var an = AssemblyName.GetAssemblyName(mod);
  100. if (an.Name == "0Harmony") continue;
  101. modlist.Items.Add(new ListViewItem(new[] { an.Name, an.Version.ToString(), File.GetLastWriteTime(mod).ToString(), "" }, modlist.Groups["installed"]));
  102. }
  103. catch (BadImageFormatException)
  104. { //Not a .NET assembly
  105. }
  106. }
  107. }
  108. public bool? CheckIfPatched()
  109. {
  110. if (!File.Exists(Settings.Default.GamePath + @"\IPA.exe"))
  111. {
  112. status.Text = "Status: Patcher missing\nInstalling a mod downloads it";
  113. return null;
  114. }
  115. string nopatch = "Status: Unpatched\nClick play";
  116. if (!Directory.Exists(Settings.Default.GamePath + @"\IPA\Backups\Gamecraft"))
  117. {
  118. status.Text = nopatch;
  119. return false;
  120. }
  121. string backup = Directory.EnumerateDirectories(Settings.Default.GamePath + @"\IPA\Backups\Gamecraft").OrderByDescending(s => s).FirstOrDefault();
  122. if (backup == null)
  123. {
  124. status.Text = nopatch;
  125. return false;
  126. }
  127. if (File.GetLastWriteTime(Settings.Default.GamePath + @"\Gamecraft_Data\Managed\Assembly-CSharp.dll")
  128. > //If the file was updated at least a minute after patching
  129. Directory.GetLastWriteTime(backup).AddMinutes(1))
  130. {
  131. status.Text = nopatch;
  132. return false;
  133. }
  134. status.Text = "Status: Patched";
  135. return true;
  136. }
  137. public async void PatchGame()
  138. {
  139. UpdateButton(playbtn, false);
  140. UpdateButton(installbtn, false);
  141. UpdateButton(uninstallbtn, false);
  142. UpdateButton(settingsbtn, false);
  143. if (!CheckIfPatched().HasValue)
  144. {
  145. if (MessageBox.Show("The patcher (GCIPA) is not found. It will be downloaded from https://git.exmods.org/modtainers/GCIPA/releases and ran to patch the game.", "Patcher download needed", MessageBoxButtons.OKCancel)
  146. == DialogResult.Cancel)
  147. return;
  148. string releases = "https://git.exmods.org/api/v1/repos/modtainers/GCIPA/releases";
  149. string url;
  150. this.status.Text = "Status: Patching...";
  151. await Task.Run(() =>
  152. {
  153. using (WebClient client = new WebClient())
  154. {
  155. url = JArray.Parse(client.DownloadString(releases)).First["assets"].First["browser_download_url"].ToString();
  156. client.DownloadFile(url, "IPA.zip");
  157. ZipFile.ExtractToDirectory("IPA.zip", Settings.Default.GamePath);
  158. }
  159. });
  160. }
  161. bool? status = CheckIfPatched();
  162. if (!status.HasValue) //Make sure it actually worked
  163. return;
  164. if (!status.Value)
  165. {
  166. var psi = new ProcessStartInfo(Settings.Default.GamePath + @"\IPA.exe", "Gamecraft.exe --nowait");
  167. psi.UseShellExecute = false;
  168. psi.RedirectStandardError = true;
  169. psi.RedirectStandardOutput = true;
  170. psi.WorkingDirectory = Settings.Default.GamePath;
  171. psi.CreateNoWindow = true;
  172. var process = Process.Start(psi);
  173. process.BeginErrorReadLine();
  174. process.BeginOutputReadLine();
  175. process.EnableRaisingEvents = true;
  176. modinfobox.Text = "";
  177. DataReceivedEventHandler onoutput = (sender, e) =>
  178. {
  179. Invoke((Action)(() => modinfobox.Text += e.Data + Environment.NewLine));
  180. };
  181. process.OutputDataReceived += onoutput;
  182. process.ErrorDataReceived += onoutput;
  183. process.Exited += CheckStartGame;
  184. }
  185. else
  186. CheckStartGame(null, null);
  187. }
  188. private void CheckStartGame(object sender, EventArgs e)
  189. {
  190. Action act = () =>
  191. {
  192. if ((sender as Process).ExitCode != 0)
  193. {
  194. status.Text = "Status: Patching failed";
  195. return;
  196. }
  197. if (CheckIfPatched() ?? false)
  198. Process.Start("steam://run/1078000/");
  199. };
  200. if (InvokeRequired)
  201. Invoke(act);
  202. }
  203. private void playbtn_Click(object sender, EventArgs e)
  204. {
  205. if (playbtn.ForeColor == Color.Green) return; //Disabled
  206. PatchGame();
  207. }
  208. private void settingsbtn_Click(object sender, EventArgs e)
  209. {
  210. if (settingsbtn.ForeColor == Color.Green) return; //Disabled
  211. var sf = new SettingsForm();
  212. sf.ShowDialog(this);
  213. }
  214. }
  215. }