NorbiPeti
c3c9ee0a16
Sorting mods when (un)installing Added Apply to all checkbox to a custom message box that replaces the one that shows up when the mod files already exist without the mod manager knowing about it Removing IPA.zip after unzipping Fix deleting files when their folder doesn't exist
214 lines
9.2 KiB
C#
214 lines
9.2 KiB
C#
using GCMM.Properties;
|
|
using Newtonsoft.Json.Linq;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
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;
|
|
|
|
namespace GCMM
|
|
{
|
|
partial class MainForm
|
|
{
|
|
|
|
public HashSet<string> GetInstalledMods()
|
|
{
|
|
bool disabled = false;
|
|
if (!Directory.Exists(GamePath("\\Plugins")))
|
|
if (Directory.Exists(GamePath("\\Plugins_Disabled")))
|
|
disabled = true;
|
|
else return new HashSet<string>();
|
|
var installed = new HashSet<string>();
|
|
foreach (var modPath in Directory.GetFiles(GamePath(disabled ? @"\Plugins_Disabled" : @"\Plugins"), "*.dll"))
|
|
{
|
|
try
|
|
{
|
|
var an = AssemblyName.GetAssemblyName(modPath);
|
|
if (an.Name == "0Harmony") continue;
|
|
//Use filename to avoid differences between repository & assembly name casing
|
|
var mod = new ModInfo { Name = Path.GetFileNameWithoutExtension(modPath), Version = an.Version, LastUpdated = File.GetLastWriteTime(modPath) };
|
|
AddUpdateModInList(mod);
|
|
installed.Add(mod.Name);
|
|
}
|
|
catch (BadImageFormatException)
|
|
{ //Not a .NET assembly
|
|
}
|
|
}
|
|
try
|
|
{
|
|
string ipath = GamePath("\\IPA.exe"); //Heh
|
|
if (File.Exists(ipath))
|
|
{
|
|
var an = AssemblyName.GetAssemblyName(ipath);
|
|
gcipa.Version = an.Version;
|
|
gcipa.LastUpdated = File.GetLastWriteTime(ipath);
|
|
}
|
|
}
|
|
catch (BadImageFormatException)
|
|
{ //Not a .NET assembly
|
|
}
|
|
try
|
|
{
|
|
string mmpath = "GCMM.exe";
|
|
if (File.Exists(mmpath))
|
|
{
|
|
var an = AssemblyName.GetAssemblyName(mmpath);
|
|
gcmm.Version = an.Version;
|
|
gcmm.LastUpdated = File.GetLastWriteTime(mmpath);
|
|
}
|
|
}
|
|
catch (BadImageFormatException)
|
|
{ //Not a .NET assembly
|
|
}
|
|
return installed;
|
|
}
|
|
|
|
public async Task GetAvailableMods()
|
|
{
|
|
bool preview = GetExe()?.Contains("Preview") ?? false;
|
|
using (var client = GetClient())
|
|
{
|
|
string str = await client.DownloadStringTaskAsync("https://exmods.org/mods/modlist.tsv");
|
|
foreach (string line in str.Trim().Split('\n'))
|
|
{
|
|
var sp = line.Split('\t');
|
|
if (sp.Length < 2) continue;
|
|
var mod = new ModInfo
|
|
{
|
|
Author = sp[0].Trim(),
|
|
Name = sp[1].Trim()
|
|
};
|
|
if (await FetchModInfo(mod, preview, true)) //If it's actually a mod
|
|
AddUpdateModInList(mod);
|
|
}
|
|
}
|
|
if (gcmm.LatestVersion == null) //Only check once
|
|
{
|
|
await FetchModInfo(gcipa, preview, false);
|
|
await FetchModInfo(gcmm, preview, false);
|
|
if (gcmm.Updatable)
|
|
if (MessageBox.Show("There is a GCMM update available! Do you want to download it now? If yes, extract it over this installation.\n\n" + gcmm.UpdateDetails, "Mod Manager update", MessageBoxButtons.YesNo)
|
|
== DialogResult.Yes)
|
|
Process.Start(gcmm.DownloadURL);
|
|
}
|
|
}
|
|
|
|
public async Task<bool> FetchModInfo(ModInfo mod, bool preview, bool desc)
|
|
{
|
|
string repoURL = "/api/v1/repos/" + mod.Author + "/" + mod.Name + "/releases";
|
|
using (var client = GetClient())
|
|
{
|
|
var arr = JArray.Parse(await client.DownloadStringTaskAsync(repoURL));
|
|
var release = arr.FirstOrDefault(rel =>
|
|
{
|
|
if ((bool) rel["prerelease"] || (bool) rel["draft"])
|
|
return false;
|
|
var vs = rel["tag_name"].ToString();
|
|
int ind = vs.IndexOf('-');
|
|
if (ind != -1)
|
|
{
|
|
if (vs.Substring(ind + 1).Equals("preview", StringComparison.InvariantCultureIgnoreCase)
|
|
&& !preview)
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
if (release == null)
|
|
return false;
|
|
var verstr = release["tag_name"].ToString().Replace("v", "");
|
|
int index = verstr.IndexOf('-');
|
|
if (index != -1)
|
|
verstr = verstr.Substring(0, index);
|
|
|
|
JToken asset;
|
|
if (release["assets"].Count() == 1)
|
|
asset = release["assets"].First;
|
|
else
|
|
asset = release["assets"].FirstOrDefault(token =>
|
|
{
|
|
string name = token["name"].ToString();
|
|
return name == mod.Name + ".dll" || name == mod.Name + ".zip";
|
|
});
|
|
|
|
mod.DownloadURL = asset?["browser_download_url"]?.ToString();
|
|
mod.LastUpdated = (DateTime)release["published_at"];
|
|
|
|
var ver = verstr.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));
|
|
mod.UpdateDetails = release["name"] + "\n\n" + release["body"].ToString();
|
|
if (desc)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
public void AddUpdateModInList(ModInfo mod)
|
|
{
|
|
if (mods.ContainsKey(mod.Name) ^ modlist.Items.ContainsKey(mod.Name)) //The ListView's keys aren't case sensitive
|
|
throw new InvalidOperationException("The mod isn't present in one of the two places: " + mod.Name);
|
|
ListViewItem item;
|
|
if (modlist.Items.ContainsKey(mod.Name))
|
|
{
|
|
var omod = mods[mod.Name];
|
|
item = modlist.Items[mod.Name];
|
|
var items = item.SubItems;
|
|
omod.Author = mod.Author ?? omod.Author;
|
|
omod.Version = mod.Version ?? omod.Version; //If the object comes from the dictionary then it's directly modified (uninstall)
|
|
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;
|
|
omod.UpdateDetails = mod.UpdateDetails ?? omod.UpdateDetails;
|
|
items[1].Text = omod.Author ?? "";
|
|
items[2].Text = (omod.Version ?? omod.LatestVersion)?.ToString();
|
|
items[3].Text = omod.LastUpdated.ToString();
|
|
item.Group = omod.Installed ? modlist.Groups["installed"] : modlist.Groups["available"];
|
|
modlist.Sort();
|
|
mod = omod;
|
|
}
|
|
else
|
|
{
|
|
mods.Add(mod.Name, mod);
|
|
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);
|
|
}
|
|
if (mod.LatestVersion != null && mod.Version != null && mod.Version < mod.LatestVersion)
|
|
item.ForeColor = Color.Blue;
|
|
else
|
|
item.ForeColor = modlist.ForeColor;
|
|
}
|
|
|
|
public void CheckUninstalledMods(HashSet<string> installed)
|
|
{
|
|
List<string> delete = new List<string>();
|
|
foreach (string name in mods.Keys.Except(installed))
|
|
{
|
|
var mod = mods[name];
|
|
mod.Version = null;
|
|
if (mod.Author != null)
|
|
AddUpdateModInList(mod);
|
|
else
|
|
delete.Add(name);
|
|
}
|
|
delete.ForEach(name => { mods.Remove(name); modlist.Items[name].Remove(); });
|
|
}
|
|
}
|
|
}
|