2020-06-16 17:22:29 +00:00
using GCMM.Properties ;
using Newtonsoft.Json.Linq ;
using System ;
using System.Collections.Generic ;
2020-10-03 13:41:31 +00:00
using System.Diagnostics ;
2020-06-16 21:33:10 +00:00
using System.Drawing ;
2020-06-16 17:22:29 +00:00
using System.IO ;
using System.Linq ;
2020-06-17 13:08:22 +00:00
using System.Net ;
2020-06-16 17:22:29 +00:00
using System.Reflection ;
2020-06-17 13:08:22 +00:00
using System.Runtime.CompilerServices ;
2020-06-16 17:22:29 +00:00
using System.Text ;
using System.Threading.Tasks ;
using System.Windows.Forms ;
namespace GCMM
{
partial class MainForm
{
2020-08-18 18:56:19 +00:00
public HashSet < string > GetInstalledMods ( )
2020-06-16 17:22:29 +00:00
{
2020-08-18 14:02:57 +00:00
bool disabled = false ;
if ( ! Directory . Exists ( GamePath ( "\\Plugins" ) ) )
if ( Directory . Exists ( GamePath ( "\\Plugins_Disabled" ) ) )
disabled = true ;
2020-08-18 18:56:19 +00:00
else return new HashSet < string > ( ) ;
var installed = new HashSet < string > ( ) ;
2020-08-18 14:02:57 +00:00
foreach ( var modPath in Directory . GetFiles ( GamePath ( disabled ? @"\Plugins_Disabled" : @"\Plugins" ) , "*.dll" ) )
2020-06-16 17:22:29 +00:00
{
try
{
2020-06-16 21:33:10 +00:00
var an = AssemblyName . GetAssemblyName ( modPath ) ;
2020-06-16 17:22:29 +00:00
if ( an . Name = = "0Harmony" ) continue ;
2020-06-17 16:31:20 +00:00
//Use filename to avoid differences between repository & assembly name casing
2020-12-31 17:05:38 +00:00
var mod = new ModInfo { Name = Path . GetFileNameWithoutExtension ( modPath ) , Version = an . Version } ;
2020-08-18 18:56:19 +00:00
AddUpdateModInList ( mod ) ;
installed . Add ( mod . Name ) ;
2020-06-16 17:22:29 +00:00
}
catch ( BadImageFormatException )
{ //Not a .NET assembly
}
}
2020-08-23 21:38:10 +00:00
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
}
2020-10-03 13:41:31 +00:00
try
{
string mmpath = "GCMM.exe" ;
if ( File . Exists ( mmpath ) )
{
var an = AssemblyName . GetAssemblyName ( mmpath ) ;
gcmm . Version = an . Version ;
}
}
catch ( BadImageFormatException )
{ //Not a .NET assembly
}
2020-08-18 18:56:19 +00:00
return installed ;
2020-06-16 17:22:29 +00:00
}
2020-08-23 21:38:10 +00:00
public async Task GetAvailableMods ( )
2020-06-16 17:22:29 +00:00
{
2020-07-17 18:57:46 +00:00
bool preview = GetExe ( ) ? . Contains ( "Preview" ) ? ? false ;
2020-06-17 21:56:18 +00:00
using ( var client = GetClient ( ) )
2020-06-17 13:08:22 +00:00
{
2020-06-17 21:56:18 +00:00
string str = await client . DownloadStringTaskAsync ( "https://exmods.org/mods/modlist.tsv" ) ;
2020-06-18 16:28:07 +00:00
foreach ( string line in str . Trim ( ) . Split ( '\n' ) )
2020-06-17 13:08:22 +00:00
{
2020-06-17 21:56:18 +00:00
var sp = line . Split ( '\t' ) ;
2020-06-18 16:28:07 +00:00
if ( sp . Length < 2 ) continue ;
2020-06-17 21:56:18 +00:00
var mod = new ModInfo
{
Author = sp [ 0 ] . Trim ( ) ,
Name = sp [ 1 ] . Trim ( )
} ;
2020-08-23 21:38:10 +00:00
if ( await FetchModInfo ( mod , preview , true ) ) //If it's actually a mod
2020-06-17 21:56:18 +00:00
AddUpdateModInList ( mod ) ;
}
2020-06-16 21:33:10 +00:00
}
2020-10-03 13:41:31 +00:00
if ( gcmm . LatestVersion = = null ) //Only check once
{
await FetchModInfo ( gcipa , preview , false ) ;
await FetchModInfo ( gcmm , preview , false ) ;
if ( gcmm . Updatable )
2020-10-03 23:08:19 +00:00
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 )
2020-10-03 13:41:31 +00:00
= = DialogResult . Yes )
Process . Start ( gcmm . DownloadURL ) ;
}
2020-06-16 21:33:10 +00:00
}
2020-08-23 21:38:10 +00:00
public async Task < bool > FetchModInfo ( ModInfo mod , bool preview , bool desc )
2020-06-16 21:33:10 +00:00
{
string repoURL = "/api/v1/repos/" + mod . Author + "/" + mod . Name + "/releases" ;
using ( var client = GetClient ( ) )
{
2020-06-17 13:08:22 +00:00
var arr = JArray . Parse ( await client . DownloadStringTaskAsync ( repoURL ) ) ;
2020-07-17 18:57:46 +00:00
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 ;
} ) ;
2020-06-16 21:33:10 +00:00
if ( release = = null )
return false ;
2020-07-17 18:57:46 +00:00
var verstr = release [ "tag_name" ] . ToString ( ) . Replace ( "v" , "" ) ;
int index = verstr . IndexOf ( '-' ) ;
if ( index ! = - 1 )
verstr = verstr . Substring ( 0 , index ) ;
2020-06-18 16:28:07 +00:00
JToken asset ;
2020-06-17 13:08:22 +00:00
if ( release [ "assets" ] . Count ( ) = = 1 )
2020-06-18 16:28:07 +00:00
asset = release [ "assets" ] . First ;
else
asset = release [ "assets" ] . FirstOrDefault ( token = >
{
string name = token [ "name" ] . ToString ( ) ;
return name = = mod . Name + ".dll" | | name = = mod . Name + ".zip" ;
} ) ;
2020-07-17 18:57:46 +00:00
2020-06-18 16:28:07 +00:00
mod . DownloadURL = asset ? [ "browser_download_url" ] ? . ToString ( ) ;
2020-06-17 13:08:22 +00:00
mod . LastUpdated = ( DateTime ) release [ "published_at" ] ;
2020-07-17 18:57:46 +00:00
var ver = verstr . Split ( '.' ) . Select ( str = > int . Parse ( str ) ) . ToArray ( ) ;
2020-06-17 13:08:22 +00:00
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 ) ) ;
2020-06-17 21:56:18 +00:00
mod . UpdateDetails = release [ "name" ] + "\n\n" + release [ "body" ] . ToString ( ) ;
2020-08-23 21:38:10 +00:00
if ( desc )
2020-06-17 13:08:22 +00:00
{
2020-08-23 21:38:10 +00:00
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...
}
2020-06-16 17:22:29 +00:00
}
2020-06-17 13:08:22 +00:00
return true ;
2020-06-16 17:22:29 +00:00
}
}
2020-06-16 21:33:10 +00:00
public void AddUpdateModInList ( ModInfo mod )
{
2020-06-17 16:31:20 +00:00
if ( mods . ContainsKey ( mod . Name ) ^ modlist . Items . ContainsKey ( mod . Name ) ) //The ListView's keys aren't case sensitive
2020-06-16 21:33:10 +00:00
throw new InvalidOperationException ( "The mod isn't present in one of the two places: " + mod . Name ) ;
2020-06-17 21:56:18 +00:00
ListViewItem item ;
2020-06-16 21:33:10 +00:00
if ( modlist . Items . ContainsKey ( mod . Name ) )
{
var omod = mods [ mod . Name ] ;
2020-06-17 21:56:18 +00:00
item = modlist . Items [ mod . Name ] ;
2020-06-16 21:33:10 +00:00
var items = item . SubItems ;
omod . Author = mod . Author ? ? omod . Author ;
2020-08-18 18:56:19 +00:00
omod . Version = mod . Version ? ? omod . Version ; //If the object comes from the dictionary then it's directly modified (uninstall)
2020-06-16 21:33:10 +00:00
omod . LatestVersion = mod . LatestVersion ? ? omod . LatestVersion ;
omod . LastUpdated = mod . LastUpdated = = default ? omod . LastUpdated : mod . LastUpdated ;
2020-06-17 13:08:22 +00:00
omod . Description = mod . Description ? ? omod . Description ;
omod . DownloadURL = mod . DownloadURL ? ? omod . DownloadURL ;
2020-06-17 21:56:18 +00:00
omod . UpdateDetails = mod . UpdateDetails ? ? omod . UpdateDetails ;
2020-06-16 21:33:10 +00:00
items [ 1 ] . Text = omod . Author ? ? "" ;
2020-06-17 13:08:22 +00:00
items [ 2 ] . Text = ( omod . Version ? ? omod . LatestVersion ) ? . ToString ( ) ;
2020-12-31 17:05:38 +00:00
items [ 3 ] . Text = omod . LatestVersion ! = null ? omod . LastUpdated . ToString ( ) : "" ;
2020-06-16 21:33:10 +00:00
item . Group = omod . Installed ? modlist . Groups [ "installed" ] : modlist . Groups [ "available" ] ;
2020-10-03 23:08:19 +00:00
modlist . Sort ( ) ;
2020-06-17 21:56:18 +00:00
mod = omod ;
2020-06-16 21:33:10 +00:00
}
else
{
mods . Add ( mod . Name , mod ) ;
2020-12-31 17:05:38 +00:00
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" ] ) ;
2020-06-16 21:33:10 +00:00
item . Name = mod . Name ;
modlist . Items . Add ( item ) ;
}
2020-06-17 21:56:18 +00:00
if ( mod . LatestVersion ! = null & & mod . Version ! = null & & mod . Version < mod . LatestVersion )
item . ForeColor = Color . Blue ;
2021-01-19 23:49:11 +00:00
else if ( mod . LastUpdated ! = default & & mod . LastUpdated < lastGameUpdateTime )
2020-12-31 17:05:38 +00:00
item . ForeColor = Color . DarkOrange ;
2020-07-03 00:00:08 +00:00
else
item . ForeColor = modlist . ForeColor ;
2020-06-16 21:33:10 +00:00
}
2020-08-18 18:56:19 +00:00
public void CheckUninstalledMods ( HashSet < string > installed )
2020-06-16 21:33:10 +00:00
{
2020-10-03 11:32:53 +00:00
List < string > delete = new List < string > ( ) ;
2020-08-18 18:56:19 +00:00
foreach ( string name in mods . Keys . Except ( installed ) )
{
var mod = mods [ name ] ;
mod . Version = null ;
2020-10-03 11:32:53 +00:00
if ( mod . Author ! = null )
AddUpdateModInList ( mod ) ;
else
delete . Add ( name ) ;
2020-08-18 18:56:19 +00:00
}
2020-10-03 11:32:53 +00:00
delete . ForEach ( name = > { mods . Remove ( name ) ; modlist . Items [ name ] . Remove ( ) ; } ) ;
2020-06-16 21:33:10 +00:00
}
2020-06-16 17:22:29 +00:00
}
}