using GCMM.Properties; using Newtonsoft.Json.Linq; using System; using System.Diagnostics; using System.Drawing; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Reflection; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; namespace GCMM { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { modlist.Items.Clear(); UpdateButton(installbtn, false); modinfobox.Text = ""; if (string.IsNullOrWhiteSpace(Settings.Default.GamePath)) { Settings.Default.GamePath = GetGameFolder(); if (string.IsNullOrWhiteSpace(Settings.Default.GamePath)) Settings.Default.GamePath = SelectGameFolder(); else MessageBox.Show("Found game at " + Settings.Default.GamePath); Settings.Default.Save(); } if(string.IsNullOrWhiteSpace(Settings.Default.GamePath)) { status.Text = "Status: Game not found"; return; } CheckIfPatched(); GetInstalledMods(); GetAvailableMods(); } public void UpdateButton(Button button, bool enabled) { if (enabled) { button.ForeColor = Color.Lime; button.FlatAppearance.MouseOverBackColor = Color.FromArgb(0, 40, 0); button.FlatAppearance.MouseDownBackColor = Color.Green; } else { button.ForeColor = Color.Green; button.FlatAppearance.MouseOverBackColor = Color.Black; button.FlatAppearance.MouseDownBackColor = Color.Black; } } public string GetGameFolder() { string libs; if (Environment.OSVersion.Platform == PlatformID.Win32NT) libs = @"C:\Program Files (x86)\Steam\steamapps\libraryfolders.vdf"; else return null; foreach (var line in File.ReadAllLines(libs).Concat(new[] { @"C:\Program Files (x86)\Steam\" })) { var regex = new Regex("\\t\"\\d+\"\\t\\t\"(.+)\""); var match = regex.Match(line); if (!match.Success) continue; string library = match.Groups[1].Value.Replace("\\\\", "\\"); library += @"\steamapps\common\"; if (File.Exists(library + @"Gamecraft\Gamecraft.exe")) return library + "Gamecraft"; if (File.Exists(library + @"RobocraftX\Gamecraft.exe")) return library + "RobocraftX"; } return libs; } public string SelectGameFolder() { var ofd = new OpenFileDialog(); ofd.Filter = "Gamecraft executable|Gamecraft.exe"; ofd.Title = "Game location"; ofd.InitialDirectory = @"C:\Program Files (x86)\Steam\steamapps\common\"; ofd.CheckFileExists = true; ofd.ShowDialog(); if (string.IsNullOrWhiteSpace(ofd.FileName)) return null; return Directory.GetParent(ofd.FileName).FullName; } public void GetInstalledMods() { foreach (var mod in Directory.GetFiles(Settings.Default.GamePath + @"\Plugins", "*.dll")) { try { var an = AssemblyName.GetAssemblyName(mod); if (an.Name == "0Harmony") continue; var item = new ListViewItem(new[] { an.Name, "", an.Version.ToString(), File.GetLastWriteTime(mod).ToString() }, modlist.Groups["installed"]); item.Name = an.Name; modlist.Items.Add(item); } catch (BadImageFormatException) { //Not a .NET assembly } } } public bool? CheckIfPatched() { if (!File.Exists(Settings.Default.GamePath + @"\IPA.exe")) { status.Text = "Status: Patcher missing\nClicking Play will install it"; return null; } string nopatch = "Status: Unpatched\nClicking Play patches it"; if (!Directory.Exists(Settings.Default.GamePath + @"\IPA\Backups\Gamecraft")) { status.Text = nopatch; return false; } string backup = Directory.EnumerateDirectories(Settings.Default.GamePath + @"\IPA\Backups\Gamecraft").OrderByDescending(s => s).FirstOrDefault(); if (backup == null) { status.Text = nopatch; return false; } if (File.GetLastWriteTime(Settings.Default.GamePath + @"\Gamecraft_Data\Managed\Assembly-CSharp.dll") > //If the file was updated at least 2 minutes after patching Directory.GetLastWriteTime(backup).AddMinutes(2)) { status.Text = nopatch; return false; } status.Text = "Status: Patched"; return true; } public async void PatchGame() { UpdateButton(playbtn, false); UpdateButton(installbtn, false); UpdateButton(uninstallbtn, false); UpdateButton(settingsbtn, false); if (!CheckIfPatched().HasValue) { 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) == DialogResult.Cancel) return; string releases = "/api/v1/repos/modtainers/GCIPA/releases"; string url; this.status.Text = "Status: Patching..."; using (WebClient client = await GetClient()) { url = JArray.Parse(await client.DownloadStringTaskAsync(releases)).First["assets"].First["browser_download_url"].ToString(); await client.DownloadFileTaskAsync(url, "IPA.zip"); ZipFile.ExtractToDirectory("IPA.zip", Settings.Default.GamePath); } } bool? status = CheckIfPatched(); if (!status.HasValue) //Make sure it actually worked return; if (!status.Value) { var psi = new ProcessStartInfo(Settings.Default.GamePath + @"\IPA.exe", "Gamecraft.exe --nowait"); psi.UseShellExecute = false; psi.RedirectStandardError = true; psi.RedirectStandardOutput = true; psi.WorkingDirectory = Settings.Default.GamePath; psi.CreateNoWindow = true; var process = Process.Start(psi); process.BeginErrorReadLine(); process.BeginOutputReadLine(); process.EnableRaisingEvents = true; modinfobox.Text = ""; DataReceivedEventHandler onoutput = (sender, e) => { Invoke((Action)(() => modinfobox.Text += e.Data + Environment.NewLine)); }; process.OutputDataReceived += onoutput; process.ErrorDataReceived += onoutput; process.Exited += CheckStartGame; } else CheckStartGame(null, null); } private void CheckStartGame(object sender, EventArgs e) { Action act = () => { if ((sender as Process).ExitCode != 0) { status.Text = "Status: Patching failed"; return; } if (CheckIfPatched() ?? false) Process.Start("steam://run/1078000/"); }; if (InvokeRequired) Invoke(act); } public async void GetAvailableMods() { string reposURL = "/api/v1/repos/search?sort=updated&order=desc"; using (var client = await GetClient()) { var obj = JObject.Parse(await client.DownloadStringTaskAsync(reposURL)); if (!(bool)obj["ok"]) return; foreach (var repo in obj["data"]) { string name = repo["name"].ToString(); if (modlist.Items.ContainsKey(name)) { var item = modlist.Items[name]; var si = item.SubItems; si[1] = new ListViewItem.ListViewSubItem(item, repo["owner"]["username"].ToString()); } else { var item = new ListViewItem(new[] { name, repo["owner"]["username"].ToString(), "", ((DateTime)repo["updated_at"]).ToString() }, modlist.Groups["available"]); item.Name = name; modlist.Items.Add(item); } } } } public async Task GetClient() { var client = new WebClient(); if (!Settings.Default.UseProxy) client.Proxy = null; client.Headers.Clear(); client.Headers[HttpRequestHeader.Accept] = "application/json"; /*client.Headers[HttpRequestHeader.Host] = "git.exmods.org"; var addr = (await Dns.GetHostAddressesAsync("git.exmods.org")).Where(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).First(); client.BaseAddress = "https://" + addr;*/ client.BaseAddress = "https://git.exmods.org"; return client; } private void playbtn_Click(object sender, EventArgs e) { if (playbtn.ForeColor == Color.Green) return; //Disabled PatchGame(); } 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) { } } }