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 };
|
|
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;
|
|
}
|
|
}
|
|
catch (BadImageFormatException)
|
|
{ //Not a .NET assembly
|
|
}
|
|
try
|
|
{
|
|
string mmpath = "GCMM.exe";
|
|
if (File.Exists(mmpath))
|
|
{
|
|
var an = AssemblyName.GetAssemblyName(mmpath);
|
|
gcmm.Version = an.Version;
|
|
}
|
|
}
|
|
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.LatestVersion != null ? 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.LatestVersion != null ? 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 if (mod.LastUpdated < lastGameUpdateTime)
|
|
item.ForeColor = Color.DarkOrange;
|
|
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(); });
|
|
}
|
|
}
|
|
}
|