Installing works, buttons are disabled while working
This commit is contained in:
parent
61d4930682
commit
b03b63294e
10 changed files with 226 additions and 24 deletions
|
@ -17,6 +17,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -35,6 +36,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="modlist.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
|
|
13
GCMM/MainForm.Designer.cs
generated
13
GCMM/MainForm.Designer.cs
generated
|
@ -38,6 +38,7 @@
|
|||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
|
||||
this.modlist = new System.Windows.Forms.ListView();
|
||||
this.modName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.modAuthor = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.modVersion = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.modTimestamp = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.status = new System.Windows.Forms.Label();
|
||||
|
@ -46,7 +47,6 @@
|
|||
this.modinfobox = new System.Windows.Forms.TextBox();
|
||||
this.playbtn = new System.Windows.Forms.Button();
|
||||
this.settingsbtn = new System.Windows.Forms.Button();
|
||||
this.modAuthor = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// modlist
|
||||
|
@ -90,6 +90,11 @@
|
|||
this.modName.Text = "Name";
|
||||
this.modName.Width = 148;
|
||||
//
|
||||
// modAuthor
|
||||
//
|
||||
this.modAuthor.Text = "Author";
|
||||
this.modAuthor.Width = 142;
|
||||
//
|
||||
// modVersion
|
||||
//
|
||||
this.modVersion.Text = "Version";
|
||||
|
@ -126,6 +131,7 @@
|
|||
this.installbtn.TabIndex = 2;
|
||||
this.installbtn.Text = "Install mod";
|
||||
this.installbtn.UseVisualStyleBackColor = true;
|
||||
this.installbtn.Click += new System.EventHandler(this.installbtn_Click);
|
||||
//
|
||||
// uninstallbtn
|
||||
//
|
||||
|
@ -186,11 +192,6 @@
|
|||
this.settingsbtn.UseVisualStyleBackColor = true;
|
||||
this.settingsbtn.Click += new System.EventHandler(this.settingsbtn_Click);
|
||||
//
|
||||
// modAuthor
|
||||
//
|
||||
this.modAuthor.Text = "Author";
|
||||
this.modAuthor.Width = 142;
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
|
|
|
@ -51,7 +51,9 @@ namespace GCMM
|
|||
private void playbtn_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (playbtn.ForeColor == Color.Green) return; //Disabled
|
||||
if (!BeginWork()) return;
|
||||
PatchGame();
|
||||
EndWork();
|
||||
}
|
||||
|
||||
private void settingsbtn_Click(object sender, EventArgs e)
|
||||
|
@ -63,7 +65,51 @@ namespace GCMM
|
|||
|
||||
private void modlist_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (working) return;
|
||||
switch (modlist.SelectedItems.Count)
|
||||
{
|
||||
case 0:
|
||||
modinfobox.Text = "";
|
||||
UpdateButton(installbtn, false);
|
||||
UpdateButton(uninstallbtn, false);
|
||||
break;
|
||||
case 1:
|
||||
default:
|
||||
installbtn.Text = "Install mod";
|
||||
UpdateButton(installbtn, false);
|
||||
UpdateButton(uninstallbtn, false);
|
||||
foreach (ListViewItem item in modlist.SelectedItems)
|
||||
{
|
||||
var mod = mods[item.Name];
|
||||
if (modlist.SelectedItems.Count == 1)
|
||||
modinfobox.Text = 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)
|
||||
installbtn.Text = "Update mod";
|
||||
else
|
||||
installbtn.Text = "Install mod";
|
||||
}
|
||||
if (mod.Version != null)
|
||||
UpdateButton(uninstallbtn, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (item.Group.Name == "installed") continue;
|
||||
await InstallMod(mods[item.Name]);
|
||||
}
|
||||
EndWork();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
58
GCMM/MainModInstaller.cs
Normal file
58
GCMM/MainModInstaller.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using GCMM.Properties;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace GCMM
|
||||
{
|
||||
partial class MainForm
|
||||
{
|
||||
public async Task InstallMod(ModInfo mod)
|
||||
{
|
||||
if (mod.DownloadURL == null) return;
|
||||
if (!File.Exists(Settings.Default.GamePath + @"\Gamecraft.exe"))
|
||||
{
|
||||
MessageBox.Show("Gamecraft not found. Set the correct path in Settings.");
|
||||
return;
|
||||
}
|
||||
var tmp = Directory.CreateDirectory("temp");
|
||||
var plugins = Directory.CreateDirectory(Settings.Default.GamePath + @"\Plugins");
|
||||
string tmppath = tmp.FullName + "\\" + mod.Name;
|
||||
using (var client = GetClient())
|
||||
{
|
||||
await client.DownloadFileTaskAsync(mod.DownloadURL, tmppath);
|
||||
string disposition = client.ResponseHeaders["Content-Disposition"];
|
||||
string filename = disposition.Substring(disposition.IndexOf("filename=") + 10).Replace("\"", "");
|
||||
if (filename.EndsWith(".dll"))
|
||||
File.Move(tmppath, plugins.FullName + "\\" + filename);
|
||||
else if (filename.EndsWith(".zip"))
|
||||
{
|
||||
bool pluginOnly = true;
|
||||
using (var archive = ZipFile.OpenRead(tmppath))
|
||||
{
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
if (entry.FullName == "Plugins/")
|
||||
{
|
||||
pluginOnly = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
archive.ExtractToDirectory(pluginOnly ? plugins.FullName : Settings.Default.GamePath, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Don't know how to install file: " + filename);
|
||||
return;
|
||||
}
|
||||
GetInstalledMods(); //Update list
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,9 @@ using System.Collections.Generic;
|
|||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
@ -23,7 +25,7 @@ namespace GCMM
|
|||
{
|
||||
var an = AssemblyName.GetAssemblyName(modPath);
|
||||
if (an.Name == "0Harmony") continue;
|
||||
AddUpdateModInList(new ModInfo { Name = an.Name, Version = an.Version.ToString(), LastUpdated = File.GetLastWriteTime(modPath) });
|
||||
AddUpdateModInList(new ModInfo { Name = an.Name, Version = an.Version, LastUpdated = File.GetLastWriteTime(modPath) });
|
||||
}
|
||||
catch (BadImageFormatException)
|
||||
{ //Not a .NET assembly
|
||||
|
@ -33,7 +35,7 @@ namespace GCMM
|
|||
|
||||
public async void GetAvailableMods()
|
||||
{
|
||||
string reposURL = "/api/v1/repos/search?sort=updated&order=desc";
|
||||
/*string reposURL = "/api/v1/repos/search?sort=updated&order=desc";
|
||||
using (var client = GetClient())
|
||||
{
|
||||
var obj = JObject.Parse(await client.DownloadStringTaskAsync(reposURL));
|
||||
|
@ -49,6 +51,17 @@ namespace GCMM
|
|||
if (await FetchModInfo(mod)) //If it's actually a mod
|
||||
AddUpdateModInList(mod);
|
||||
}
|
||||
}*/
|
||||
foreach (string line in File.ReadLines("modlist.txt"))
|
||||
{
|
||||
var sp = line.Split('\t');
|
||||
var mod = new ModInfo
|
||||
{
|
||||
Author = sp[0],
|
||||
Name = sp[1]
|
||||
};
|
||||
if (await FetchModInfo(mod)) //If it's actually a mod
|
||||
AddUpdateModInList(mod);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,17 +70,25 @@ namespace GCMM
|
|||
string repoURL = "/api/v1/repos/" + mod.Author + "/" + mod.Name + "/releases";
|
||||
using (var client = GetClient())
|
||||
{
|
||||
var obj = JArray.Parse(await client.DownloadStringTaskAsync(repoURL));
|
||||
var release = obj.FirstOrDefault(rel => !(bool)rel["prerelease"] && !(bool)rel["draft"]);
|
||||
var arr = JArray.Parse(await client.DownloadStringTaskAsync(repoURL));
|
||||
var release = arr.FirstOrDefault(rel => !(bool)rel["prerelease"] && !(bool)rel["draft"]);
|
||||
if (release == null)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
if (release["assets"].Count() == 1)
|
||||
mod.DownloadURL = release["assets"].First["browser_download_url"].ToString();
|
||||
mod.LastUpdated = (DateTime)release["published_at"];
|
||||
mod.LatestVersion = release["tag_name"].ToString().Replace("v", "");
|
||||
return true;
|
||||
mod.LastUpdated = (DateTime)release["published_at"];
|
||||
var ver = release["tag_name"].ToString().Replace("v", "").Split('.').Select(str => int.Parse(str)).ToArray();
|
||||
int getver(byte i) => ver.Length > i ? ver[i] : 0; //By default it sets values not present to -1, but we need them to be 0
|
||||
mod.LatestVersion = new Version(getver(0), getver(1), getver(2), getver(3));
|
||||
try
|
||||
{
|
||||
var obj = JObject.Parse(await client.DownloadStringTaskAsync("/api/v1/repos/" + mod.Author + "/" + mod.Name + "/contents/README.md"));
|
||||
mod.Description = Encoding.UTF8.GetString(Convert.FromBase64String(obj["content"].ToString()));
|
||||
}
|
||||
catch(WebException)
|
||||
{ //It returns a HTTP 500 if it doesn't exist...
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,8 +105,10 @@ namespace GCMM
|
|||
omod.Version = mod.Version ?? omod.Version;
|
||||
omod.LatestVersion = mod.LatestVersion ?? omod.LatestVersion;
|
||||
omod.LastUpdated = mod.LastUpdated == default ? omod.LastUpdated : mod.LastUpdated;
|
||||
omod.Description = mod.Description ?? omod.Description;
|
||||
omod.DownloadURL = mod.DownloadURL ?? omod.DownloadURL;
|
||||
items[1].Text = omod.Author ?? "";
|
||||
items[2].Text = omod.Version ?? omod.LatestVersion;
|
||||
items[2].Text = (omod.Version ?? omod.LatestVersion)?.ToString();
|
||||
items[3].Text = omod.LastUpdated.ToString();
|
||||
item.Group = omod.Installed ? modlist.Groups["installed"] : modlist.Groups["available"];
|
||||
if (mod.Version != mod.LatestVersion)
|
||||
|
@ -94,7 +117,7 @@ namespace GCMM
|
|||
else
|
||||
{
|
||||
mods.Add(mod.Name, mod);
|
||||
var item = new ListViewItem(new[] { mod.Name, mod.Author ?? "", mod.Version ?? mod.LatestVersion ?? "", mod.LastUpdated.ToString() }, modlist.Groups[mod.Installed ? "installed" : "available"]);
|
||||
var item = new ListViewItem(new[] { mod.Name, mod.Author ?? "", (mod.Version ?? mod.LatestVersion)?.ToString() ?? "", mod.LastUpdated.ToString() }, modlist.Groups[mod.Installed ? "installed" : "available"]);
|
||||
item.Name = mod.Name;
|
||||
modlist.Items.Add(item);
|
||||
}
|
|
@ -15,7 +15,6 @@ namespace GCMM
|
|||
{
|
||||
partial class MainForm
|
||||
{
|
||||
|
||||
public bool? CheckIfPatched()
|
||||
{
|
||||
if (!File.Exists(Settings.Default.GamePath + @"\IPA.exe"))
|
||||
|
@ -48,10 +47,6 @@ namespace GCMM
|
|||
|
||||
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)
|
||||
|
|
|
@ -93,5 +93,29 @@ namespace GCMM
|
|||
client.BaseAddress = "https://git.exmods.org";
|
||||
return client;
|
||||
}
|
||||
|
||||
private bool working = false;
|
||||
/// <summary>
|
||||
/// Some simple "locking", only allow one operation at a time
|
||||
/// </summary>
|
||||
/// <returns>Whether the work can begin</returns>
|
||||
public bool BeginWork()
|
||||
{
|
||||
if (working) return false;
|
||||
working = true;
|
||||
UpdateButton(playbtn, false);
|
||||
UpdateButton(installbtn, false);
|
||||
UpdateButton(uninstallbtn, false);
|
||||
UpdateButton(settingsbtn, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void EndWork()
|
||||
{
|
||||
working = false;
|
||||
UpdateButton(playbtn, true);
|
||||
UpdateButton(settingsbtn, true);
|
||||
modlist_SelectedIndexChanged(modlist, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ namespace GCMM
|
|||
/// <summary>
|
||||
/// Can be null.
|
||||
/// </summary>
|
||||
public string Version { get; set; }
|
||||
public string LatestVersion { get; set; }
|
||||
public Version Version { get; set; }
|
||||
public Version LatestVersion { get; set; }
|
||||
/// <summary>
|
||||
/// Can be null.
|
||||
/// </summary>
|
||||
|
|
42
GCMM/ZipArchiveExtensions.cs
Normal file
42
GCMM/ZipArchiveExtensions.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GCMM
|
||||
{
|
||||
public static class ZipArchiveExtensions
|
||||
{ //https://stackoverflow.com/a/14795752/2703239
|
||||
public static void ExtractToDirectory(this ZipArchive archive, string destinationDirectoryName, bool overwrite)
|
||||
{
|
||||
if (!overwrite)
|
||||
{
|
||||
archive.ExtractToDirectory(destinationDirectoryName);
|
||||
return;
|
||||
}
|
||||
|
||||
DirectoryInfo di = Directory.CreateDirectory(destinationDirectoryName);
|
||||
string destinationDirectoryFullPath = di.FullName;
|
||||
|
||||
foreach (ZipArchiveEntry file in archive.Entries)
|
||||
{
|
||||
string completeFileName = Path.GetFullPath(Path.Combine(destinationDirectoryFullPath, file.FullName));
|
||||
|
||||
if (!completeFileName.StartsWith(destinationDirectoryFullPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new IOException("Trying to extract file outside of destination directory. See this link for more info: https://snyk.io/research/zip-slip-vulnerability");
|
||||
}
|
||||
|
||||
if (file.Name == "")
|
||||
{// Assuming Empty for Directory
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(completeFileName));
|
||||
continue;
|
||||
}
|
||||
file.ExtractToFile(completeFileName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
GCMM/modlist.txt
Normal file
9
GCMM/modlist.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
NGnius GamecraftRichPresenceMod
|
||||
SnakesOnAGame GamecraftScripting
|
||||
NGnius Pixi
|
||||
modtainers GamecraftModdingAPI
|
||||
ExtraCommands extracommands
|
||||
NorbiPeti GCDC
|
||||
NorbiPeti GCMC
|
||||
NorbiPeti BlockMod
|
||||
NGnius leadercraft
|
Loading…
Reference in a new issue