@@ -1,6 +1,7 @@ | |||
using GCMM.Properties; | |||
using Newtonsoft.Json.Linq; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.Drawing; | |||
using System.IO; | |||
@@ -21,6 +22,8 @@ namespace GCMM | |||
InitializeComponent(); | |||
} | |||
private Dictionary<string, ModInfo> mods = new Dictionary<string, ModInfo>(); | |||
private void Form1_Load(object sender, EventArgs e) | |||
{ | |||
modlist.Items.Clear(); | |||
@@ -45,211 +48,6 @@ namespace GCMM | |||
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<WebClient> 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 | |||
@@ -0,0 +1,61 @@ | |||
using GCMM.Properties; | |||
using Newtonsoft.Json.Linq; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Reflection; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using System.Windows.Forms; | |||
namespace GCMM | |||
{ | |||
partial class MainForm | |||
{ | |||
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 async void GetAvailableMods() | |||
{ | |||
string reposURL = "/api/v1/repos/search?sort=updated&order=desc"; | |||
using (var client = 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); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,98 @@ | |||
using GCMM.Properties; | |||
using Newtonsoft.Json.Linq; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.IO.Compression; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using System.Windows.Forms; | |||
namespace GCMM | |||
{ | |||
partial class MainForm | |||
{ | |||
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 = 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); | |||
} | |||
} | |||
} |
@@ -0,0 +1,97 @@ | |||
using GCMM.Properties; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.Drawing; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Text; | |||
using System.Text.RegularExpressions; | |||
using System.Threading.Tasks; | |||
using System.Windows.Forms; | |||
namespace GCMM | |||
{ | |||
partial class MainForm | |||
{ | |||
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; | |||
} | |||
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 WebClient GetClient() | |||
{ | |||
var client = new WebClient(); | |||
if (!Settings.Default.UseProxy) | |||
client.Proxy = null; | |||
client.Headers.Clear(); | |||
client.Headers[HttpRequestHeader.Accept] = "application/json"; | |||
client.BaseAddress = "https://git.exmods.org"; | |||
return client; | |||
} | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace GCMM | |||
{ | |||
class ModInfo | |||
{ | |||
public string Name { get; set; } | |||
public string Version { get; set; } | |||
public string LatestVersion { get; set; } | |||
public string Description { get; set; } | |||
public string Author { get; set; } | |||
public DateTime LastUpdated { get; set; } | |||
} | |||
} |