2020-06-17 13:08:22 +00:00
using GCMM.Properties ;
2020-06-17 16:31:20 +00:00
using Newtonsoft.Json ;
2020-06-17 13:08:22 +00:00
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" ) )
2020-06-17 16:31:20 +00:00
File . Move ( tmppath , plugins . FullName + "\\" + mod . Name + ".dll" ) ; //Force mod name to make uninstalls & identifying easier
2020-06-17 13:08:22 +00:00
else if ( filename . EndsWith ( ".zip" ) )
{
bool pluginOnly = true ;
using ( var archive = ZipFile . OpenRead ( tmppath ) )
{
2020-06-17 16:31:20 +00:00
bool modFound = false ;
2020-06-17 13:08:22 +00:00
foreach ( var entry in archive . Entries )
{
if ( entry . FullName = = "Plugins/" )
pluginOnly = false ;
2020-06-17 16:31:20 +00:00
if ( entry . FullName = = "Plugins/" + mod . Name + ".dll" )
2020-06-18 16:28:07 +00:00
{
modFound = true ;
pluginOnly = false ; //The directory-only entry may be missing
}
else if ( pluginOnly & & entry . FullName = = mod . Name + ".dll" )
2020-06-17 16:31:20 +00:00
modFound = true ;
if ( ! pluginOnly & & modFound ) break ;
2020-06-17 13:08:22 +00:00
}
2020-06-18 16:28:07 +00:00
if ( ! modFound )
2020-06-17 21:56:18 +00:00
if ( MessageBox . Show ( "The mod was not found in the downloaded archive. It likely means it's using a different name for the dll file. The mod manager will not be able to track this mod if you continue. Do you want to continue?" , "Mod not found in archive" , MessageBoxButtons . YesNo ) = = DialogResult . No )
2020-06-17 16:31:20 +00:00
return ;
ExtractMod ( archive , pluginOnly ? plugins . FullName : Settings . Default . GamePath , mod ) ;
2020-06-17 13:08:22 +00:00
}
2020-06-17 16:31:20 +00:00
File . Delete ( tmppath ) ;
2020-06-17 13:08:22 +00:00
}
else
{
2020-06-18 16:28:07 +00:00
MessageBox . Show ( "Don't know how to install file: " + filename + "\nThe file remains in the temp folder near the mod manager" ) ;
2020-06-17 13:08:22 +00:00
return ;
}
GetInstalledMods ( ) ; //Update list
}
}
2020-06-17 16:31:20 +00:00
public void ExtractMod ( ZipArchive archive , string destinationDirectoryName , ModInfo mod )
{
LoadFileList ( mod ) ;
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" ) ;
}
2020-06-18 16:28:07 +00:00
Directory . CreateDirectory ( Path . GetDirectoryName ( completeFileName ) ) ; //Sometimes there are no directory-only entries
2020-06-17 16:31:20 +00:00
if ( file . Name = = "" )
{ // Assuming Empty for Directory
continue ;
}
2020-06-18 18:05:14 +00:00
if ( ( mod . Version = = null | | mod . ModFiles . Count ! = 0 ) //Negated: The mod is installed and we don't know about any of its files
& & File . Exists ( completeFileName ) // OR it's a new file
& & ! mod . ModFiles . Contains ( completeFileName ) // OR it's known to be part of the mod already
& & file . FullName ! = "Plugins/" + mod . Name + ".dll" ) // OR it's the plugin's DLL (dll->zip release)
{
2020-06-18 16:28:07 +00:00
if ( MessageBox . Show ( "The mod zip contains a file that exists as part of the game. Do you want to skip this file?\n" + file . FullName + "\nOnly choose No if it's part of a previous installation of the mod." , "File is part of the game" , MessageBoxButtons . YesNo ) = = DialogResult . Yes )
continue ;
2020-06-17 16:31:20 +00:00
}
mod . ModFiles . Add ( completeFileName ) ;
file . ExtractToFile ( completeFileName , true ) ;
}
SaveFileList ( mod ) ;
}
public void SaveFileList ( ModInfo mod )
{
if ( mod . ModFiles ! = null )
File . WriteAllText ( mod . Name + ".json" , JsonConvert . SerializeObject ( mod . ModFiles ) ) ;
}
public void LoadFileList ( ModInfo mod )
{
if ( File . Exists ( mod . Name + ".json" ) )
mod . ModFiles = JsonConvert . DeserializeObject < HashSet < string > > ( File . ReadAllText ( mod . Name + ".json" ) ) ;
else
mod . ModFiles = new HashSet < string > ( ) ;
}
public void UninstallMod ( ModInfo mod )
{
LoadFileList ( mod ) ;
if ( mod . ModFiles . Count = = 0 ) //A single DLL
File . Delete ( Settings . Default . GamePath + @"\Plugins\" + mod . Name + ".dll" ) ;
else //A ZIP
{
foreach ( string file in mod . ModFiles )
{
File . Delete ( file ) ;
var parent = Directory . GetParent ( file ) ;
if ( ! parent . EnumerateFiles ( ) . Any ( ) )
parent . Delete ( ) ;
}
}
File . Delete ( mod . Name + ".json" ) ;
mod . Version = null ; //Not installed
AddUpdateModInList ( mod ) ; //Update list
}
2020-06-17 13:08:22 +00:00
}
}