TBMM/GCMM/MainModList.cs
NorbiPeti c3c9ee0a16 Sorting, apply to all, fixes
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
2020-10-04 01:08:19 +02:00

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(); });
}
}
}