2019-10-16 21:36:14 +00:00
using System ;
2020-03-09 21:07:57 +00:00
using System.ComponentModel.DataAnnotations ;
2020-01-04 10:25:53 +00:00
using System.Globalization ;
2019-07-23 04:17:52 +00:00
using System.IO ;
using System.Linq ;
using System.Text ;
2019-12-04 01:26:26 +00:00
using System.Threading.Tasks ;
2019-12-03 21:56:18 +00:00
using System.Threading ;
2019-08-26 23:02:49 +00:00
using System.Windows ;
2019-12-06 04:58:18 +00:00
using Alphaleonis.Win32.Filesystem ;
using IniParser ;
2020-01-03 15:01:17 +00:00
using IniParser.Model ;
2019-12-16 06:33:44 +00:00
using IniParser.Model.Configuration ;
2019-12-06 04:58:18 +00:00
using IniParser.Parser ;
2019-07-23 04:17:52 +00:00
using Wabbajack.Common ;
2019-12-04 04:12:08 +00:00
using Wabbajack.Lib.CompilationSteps.CompilationErrors ;
2019-10-16 03:10:34 +00:00
using Wabbajack.Lib.Downloaders ;
using Wabbajack.Lib.NexusApi ;
using Wabbajack.Lib.Validation ;
2019-09-18 21:33:23 +00:00
using Directory = Alphaleonis . Win32 . Filesystem . Directory ;
using File = Alphaleonis . Win32 . Filesystem . File ;
using Path = Alphaleonis . Win32 . Filesystem . Path ;
2020-01-18 14:48:55 +00:00
using SectionData = Wabbajack . Common . SectionData ;
2019-07-23 04:17:52 +00:00
2019-10-16 03:10:34 +00:00
namespace Wabbajack.Lib
2019-07-23 04:17:52 +00:00
{
2019-11-18 00:17:06 +00:00
public class MO2Installer : AInstaller
2019-07-23 04:17:52 +00:00
{
2019-11-18 05:21:24 +00:00
public bool WarnOnOverwrite { get ; set ; } = true ;
2019-12-01 20:22:33 +00:00
public override ModManager ModManager = > ModManager . MO2 ;
2019-07-23 04:17:52 +00:00
2019-09-24 04:20:24 +00:00
public string GameFolder { get ; set ; }
2019-07-23 04:17:52 +00:00
2020-01-07 13:50:11 +00:00
public MO2Installer ( string archive , ModList modList , string outputFolder , string downloadFolder , SystemParameters parameters )
2019-12-01 20:22:33 +00:00
: base (
archive : archive ,
modList : modList ,
outputFolder : outputFolder ,
2020-01-07 13:50:11 +00:00
downloadFolder : downloadFolder ,
parameters : parameters )
2019-12-01 20:22:33 +00:00
{
}
2019-12-04 01:26:26 +00:00
protected override async Task < bool > _Begin ( CancellationToken cancel )
2019-07-23 04:17:52 +00:00
{
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2020-01-18 19:28:24 +00:00
var metric = Metrics . Send ( Metrics . BeginInstall , ModList . Name ) ;
2019-12-15 04:33:48 +00:00
2020-01-18 14:48:55 +00:00
ConfigureProcessor ( 20 , ConstructDynamicNumThreads ( await RecommendQueueSize ( ) ) ) ;
2019-12-09 22:38:26 +00:00
var game = ModList . GameType . MetaData ( ) ;
2019-11-02 23:20:41 +00:00
2019-12-21 20:09:21 +00:00
if ( GameFolder = = null )
GameFolder = game . GameLocation ( ) ;
2019-11-02 23:20:41 +00:00
if ( GameFolder = = null )
{
2020-03-19 02:10:55 +00:00
var otherGame = game . CommonlyConfusedWith . Where ( g = > g . MetaData ( ) . IsInstalled ) . Select ( g = > g . MetaData ( ) ) . FirstOrDefault ( ) ;
if ( otherGame ! = null )
{
await Utils . Log ( new CriticalFailureIntervention (
$"In order to do a proper install Wabbajack needs to know where your {game.HumanFriendlyGameName} folder resides. However this game doesn't seem to be installed, we did however find a installed" +
$"copy of {otherGame.HumanFriendlyGameName}, did you install the wrong game?" ,
$"Could not locate {game.HumanFriendlyGameName}" ) )
. Task ;
}
else
{
await Utils . Log ( new CriticalFailureIntervention (
$"In order to do a proper install Wabbajack needs to know where your {game.HumanFriendlyGameName} folder resides. However this game doesn't seem to be installed" ,
$"Could not locate {game.HumanFriendlyGameName}" ) )
. Task ;
}
2019-11-02 23:20:41 +00:00
Utils . Log ( "Exiting because we couldn't find the game folder." ) ;
2019-11-17 23:48:32 +00:00
return false ;
2019-11-02 23:20:41 +00:00
}
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Validating Game ESMs" ) ;
2019-11-02 21:08:37 +00:00
ValidateGameESMs ( ) ;
2019-11-24 23:03:36 +00:00
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Validating Modlist" ) ;
2019-12-04 01:26:26 +00:00
await ValidateModlist . RunValidation ( Queue , ModList ) ;
2019-09-29 22:21:18 +00:00
2019-11-16 13:22:40 +00:00
Directory . CreateDirectory ( OutputFolder ) ;
2019-07-23 04:17:52 +00:00
Directory . CreateDirectory ( DownloadFolder ) ;
2020-02-05 05:17:12 +00:00
if ( Directory . Exists ( Path . Combine ( OutputFolder , Consts . MO2ModFolderName ) ) & & WarnOnOverwrite )
2019-09-04 21:19:37 +00:00
{
2019-12-07 02:54:27 +00:00
if ( ( await Utils . Log ( new ConfirmUpdateOfExistingInstall { ModListName = ModList . Name , OutputFolder = OutputFolder } ) . Task ) = = ConfirmUpdateOfExistingInstall . Choice . Abort )
2019-09-18 21:33:23 +00:00
{
2019-12-21 19:37:48 +00:00
Utils . Log ( "Exiting installation at the request of the user, existing mods folder found." ) ;
2019-11-17 23:48:32 +00:00
return false ;
2019-09-18 21:33:23 +00:00
}
2019-09-04 21:19:37 +00:00
}
2019-09-14 04:35:42 +00:00
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2020-01-13 21:11:07 +00:00
UpdateTracker . NextStep ( "Optimizing ModList" ) ;
2019-12-04 01:26:26 +00:00
await OptimizeModlist ( ) ;
2019-08-24 23:20:54 +00:00
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Hashing Archives" ) ;
2019-12-04 01:26:26 +00:00
await HashArchives ( ) ;
2019-11-24 23:03:36 +00:00
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Downloading Missing Archives" ) ;
2019-12-04 01:26:26 +00:00
await DownloadArchives ( ) ;
2019-11-24 23:03:36 +00:00
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Hashing Remaining Archives" ) ;
2019-12-04 01:26:26 +00:00
await HashArchives ( ) ;
2019-07-23 04:17:52 +00:00
var missing = ModList . Archives . Where ( a = > ! HashedArchives . ContainsKey ( a . Hash ) ) . ToList ( ) ;
if ( missing . Count > 0 )
{
foreach ( var a in missing )
2019-09-26 22:32:15 +00:00
Info ( $"Unable to download {a.Name}" ) ;
2019-08-07 23:06:38 +00:00
if ( IgnoreMissingFiles )
Info ( "Missing some archives, but continuing anyways at the request of the user" ) ;
else
Error ( "Cannot continue, was unable to download one or more archives" ) ;
2019-07-23 04:17:52 +00:00
}
2019-08-20 04:57:08 +00:00
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Priming VFS" ) ;
2019-12-07 02:54:27 +00:00
await PrimeVFS ( ) ;
2019-08-20 04:57:08 +00:00
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Building Folder Structure" ) ;
2019-07-23 04:17:52 +00:00
BuildFolderStructure ( ) ;
2019-11-24 23:03:36 +00:00
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Installing Archives" ) ;
2019-12-04 01:26:26 +00:00
await InstallArchives ( ) ;
2019-11-24 23:03:36 +00:00
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Installing Included files" ) ;
2019-12-04 01:26:26 +00:00
await InstallIncludedFiles ( ) ;
2019-11-24 23:03:36 +00:00
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Installing Archive Metas" ) ;
2019-12-04 01:26:26 +00:00
await InstallIncludedDownloadMetas ( ) ;
2019-11-24 23:03:36 +00:00
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Building BSAs" ) ;
2019-12-04 01:26:26 +00:00
await BuildBSAs ( ) ;
2019-07-23 04:17:52 +00:00
2019-12-03 21:56:18 +00:00
if ( cancel . IsCancellationRequested ) return false ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Generating Merges" ) ;
2019-12-04 01:26:26 +00:00
await zEditIntegration . GenerateMerges ( this ) ;
2019-11-02 15:38:03 +00:00
2020-01-06 15:20:18 +00:00
UpdateTracker . NextStep ( "Set MO2 into portable" ) ;
ForcePortable ( ) ;
2020-01-18 14:48:55 +00:00
UpdateTracker . NextStep ( "Create Empty Output Mods" ) ;
CreateOutputMods ( ) ;
2019-12-06 04:58:18 +00:00
UpdateTracker . NextStep ( "Updating System-specific ini settings" ) ;
SetScreenSizeInPrefs ( ) ;
2019-11-24 23:03:36 +00:00
UpdateTracker . NextStep ( "Installation complete! You may exit the program." ) ;
2020-01-18 19:28:24 +00:00
var metric2 = Metrics . Send ( Metrics . FinishInstall , ModList . Name ) ;
2019-12-15 04:33:48 +00:00
2019-11-17 23:48:32 +00:00
return true ;
2019-08-26 23:02:49 +00:00
}
2020-01-18 14:48:55 +00:00
private void CreateOutputMods ( )
{
Directory . EnumerateFiles ( Path . Combine ( OutputFolder , "profiles" ) , "settings.ini" , DirectoryEnumerationOptions . Recursive ) . Do ( f = >
{
var ini = f . LoadIniFile ( ) ;
if ( ini = = null )
{
Utils . Log ( $"settings.ini is null for {f}, skipping" ) ;
return ;
}
var overwrites = ini . custom_overwrites ;
if ( overwrites = = null )
{
Utils . Log ( "No custom overwrites found, skipping" ) ;
return ;
}
if ( overwrites is SectionData data )
{
data . Coll . Do ( keyData = >
{
var v = keyData . Value ;
2020-02-05 05:17:12 +00:00
var mod = Path . Combine ( OutputFolder , Consts . MO2ModFolderName , v ) ;
2020-01-18 14:48:55 +00:00
if ( ! Directory . Exists ( mod ) )
Directory . CreateDirectory ( mod ) ;
} ) ;
}
} ) ;
}
2020-01-06 15:20:18 +00:00
private void ForcePortable ( )
{
var path = Path . Combine ( OutputFolder , "portable.txt" ) ;
if ( File . Exists ( path ) ) return ;
try
{
File . WriteAllText ( path , "Created by Wabbajack" ) ;
}
catch ( Exception e )
{
Utils . Error ( e , $"Could not create portable.txt in {OutputFolder}" ) ;
}
}
2019-11-18 05:21:24 +00:00
2019-12-04 01:26:26 +00:00
private async Task InstallIncludedDownloadMetas ( )
2019-11-04 04:36:25 +00:00
{
2019-12-04 01:26:26 +00:00
await ModList . Directives
2019-11-04 04:36:25 +00:00
. OfType < ArchiveMeta > ( )
2019-11-17 04:16:42 +00:00
. PMap ( Queue , directive = >
2019-11-04 04:36:25 +00:00
{
Status ( $"Writing included .meta file {directive.To}" ) ;
2019-11-21 15:51:57 +00:00
var outPath = Path . Combine ( DownloadFolder , directive . To ) ;
if ( File . Exists ( outPath ) ) File . Delete ( outPath ) ;
File . WriteAllBytes ( outPath , LoadBytesFromPath ( directive . SourceDataID ) ) ;
2019-11-04 04:36:25 +00:00
} ) ;
}
2019-11-02 21:08:37 +00:00
private void ValidateGameESMs ( )
{
foreach ( var esm in ModList . Directives . OfType < CleanedESM > ( ) . ToList ( ) )
{
var filename = Path . GetFileName ( esm . To ) ;
2019-11-21 15:51:57 +00:00
var gameFile = Path . Combine ( GameFolder , "Data" , filename ) ;
2019-11-02 21:08:37 +00:00
Utils . Log ( $"Validating {filename}" ) ;
2019-11-21 15:51:57 +00:00
var hash = gameFile . FileHash ( ) ;
2019-11-02 21:08:37 +00:00
if ( hash ! = esm . SourceESMHash )
{
2019-12-05 04:58:02 +00:00
Utils . ErrorThrow ( new InvalidGameESMError ( esm , hash , gameFile ) ) ;
2019-11-02 21:08:37 +00:00
}
}
}
2019-12-04 01:26:26 +00:00
private async Task BuildBSAs ( )
2019-07-28 23:04:23 +00:00
{
var bsas = ModList . Directives . OfType < CreateBSA > ( ) . ToList ( ) ;
2019-07-30 03:32:52 +00:00
Info ( $"Building {bsas.Count} bsa files" ) ;
2019-07-28 23:04:23 +00:00
2019-12-04 01:26:26 +00:00
foreach ( var bsa in bsas )
2019-07-28 23:04:23 +00:00
{
Status ( $"Building {bsa.To}" ) ;
2019-11-21 15:51:57 +00:00
var sourceDir = Path . Combine ( OutputFolder , Consts . BSACreationDir , bsa . TempID ) ;
2019-07-28 23:04:23 +00:00
2020-03-09 21:07:57 +00:00
var bsaSize = bsa . FileStates . Select ( state = > File . GetSize ( Path . Combine ( sourceDir , state . Path ) ) ) . Sum ( ) ;
using ( var a = bsa . State . MakeBuilder ( bsaSize ) )
2019-10-11 23:31:36 +00:00
{
2020-03-05 05:27:15 +00:00
var streams = await bsa . FileStates . PMap ( Queue , state = >
2019-07-28 23:04:23 +00:00
{
2019-10-11 23:31:36 +00:00
Status ( $"Adding {state.Path} to BSA" ) ;
2020-03-05 05:27:15 +00:00
var fs = File . OpenRead ( Path . Combine ( sourceDir , state . Path ) ) ;
a . AddFile ( state , fs ) ;
return fs ;
2019-10-11 23:31:36 +00:00
} ) ;
Info ( $"Writing {bsa.To}" ) ;
2019-11-16 13:22:40 +00:00
a . Build ( Path . Combine ( OutputFolder , bsa . To ) ) ;
2020-03-05 05:27:15 +00:00
streams . Do ( s = > s . Dispose ( ) ) ;
2019-10-11 23:31:36 +00:00
}
2019-12-04 01:26:26 +00:00
}
2019-09-18 21:33:23 +00:00
2019-11-21 15:51:57 +00:00
var bsaDir = Path . Combine ( OutputFolder , Consts . BSACreationDir ) ;
if ( Directory . Exists ( bsaDir ) )
2019-08-20 22:37:55 +00:00
{
Info ( $"Removing temp folder {Consts.BSACreationDir}" ) ;
2019-11-23 17:37:24 +00:00
Utils . DeleteDirectory ( bsaDir ) ;
2019-08-20 22:37:55 +00:00
}
2019-07-28 23:04:23 +00:00
}
2019-12-04 01:26:26 +00:00
private async Task InstallIncludedFiles ( )
2019-07-23 04:27:26 +00:00
{
Info ( "Writing inline files" ) ;
2019-12-04 01:26:26 +00:00
await ModList . Directives
2019-09-14 04:35:42 +00:00
. OfType < InlineFile > ( )
2019-11-17 04:16:42 +00:00
. PMap ( Queue , directive = >
2019-09-14 04:35:42 +00:00
{
2019-09-26 22:32:15 +00:00
Status ( $"Writing included file {directive.To}" ) ;
2019-11-21 15:51:57 +00:00
var outPath = Path . Combine ( OutputFolder , directive . To ) ;
if ( File . Exists ( outPath ) ) File . Delete ( outPath ) ;
2019-09-14 04:35:42 +00:00
if ( directive is RemappedInlineFile )
2019-10-07 17:33:34 +00:00
WriteRemappedFile ( ( RemappedInlineFile ) directive ) ;
2019-09-14 04:35:42 +00:00
else if ( directive is CleanedESM )
2019-10-07 17:33:34 +00:00
GenerateCleanedESM ( ( CleanedESM ) directive ) ;
2019-09-14 04:35:42 +00:00
else
2019-11-21 15:51:57 +00:00
File . WriteAllBytes ( outPath , LoadBytesFromPath ( directive . SourceDataID ) ) ;
2019-09-14 04:35:42 +00:00
} ) ;
2019-07-23 04:27:26 +00:00
}
2019-08-25 03:46:32 +00:00
private void GenerateCleanedESM ( CleanedESM directive )
{
var filename = Path . GetFileName ( directive . To ) ;
2019-11-21 15:51:57 +00:00
var gameFile = Path . Combine ( GameFolder , "Data" , filename ) ;
2019-08-25 03:46:32 +00:00
Info ( $"Generating cleaned ESM for {filename}" ) ;
2019-11-21 15:51:57 +00:00
if ( ! File . Exists ( gameFile ) ) throw new InvalidDataException ( $"Missing {filename} at {gameFile}" ) ;
2019-08-25 03:46:32 +00:00
Status ( $"Hashing game version of {filename}" ) ;
2019-11-21 15:51:57 +00:00
var sha = gameFile . FileHash ( ) ;
2019-08-25 03:46:32 +00:00
if ( sha ! = directive . SourceESMHash )
2019-09-14 04:35:42 +00:00
throw new InvalidDataException (
2019-12-21 20:09:21 +00:00
$"Cannot patch {filename} from the game folder because the hashes do not match. Have you already cleaned the file?" ) ;
2019-08-25 03:46:32 +00:00
2019-11-21 15:51:57 +00:00
var patchData = LoadBytesFromPath ( directive . SourceDataID ) ;
var toFile = Path . Combine ( OutputFolder , directive . To ) ;
2019-08-25 03:46:32 +00:00
Status ( $"Patching {filename}" ) ;
2020-01-18 20:52:09 +00:00
using ( var output = File . Open ( toFile , FileMode . Create ) )
2019-11-21 15:51:57 +00:00
using ( var input = File . OpenRead ( gameFile ) )
2019-09-14 04:35:42 +00:00
{
2020-02-24 23:18:29 +00:00
Utils . ApplyPatch ( input , ( ) = > new MemoryStream ( patchData ) , output ) ;
2019-08-25 03:46:32 +00:00
}
}
2019-12-06 04:58:18 +00:00
private void SetScreenSizeInPrefs ( )
{
2019-12-17 23:17:44 +00:00
var config = new IniParserConfiguration { AllowDuplicateKeys = true , AllowDuplicateSections = true } ;
2019-12-06 04:58:18 +00:00
foreach ( var file in Directory . EnumerateFiles ( Path . Combine ( OutputFolder , "profiles" ) , "*refs.ini" ,
DirectoryEnumerationOptions . Recursive ) )
{
2020-01-03 15:01:17 +00:00
try
{
2020-01-04 02:52:17 +00:00
var parser = new FileIniDataParser ( new IniDataParser ( config ) ) ;
2020-01-04 10:25:53 +00:00
var data = parser . ReadFile ( file ) ;
2020-03-10 04:11:11 +00:00
bool modified = false ;
if ( data . Sections [ "Display" ] ! = null )
{
if ( data . Sections [ "Display" ] [ "iSize W" ] ! = null & & data . Sections [ "Display" ] [ "iSize H" ] ! = null )
{
data . Sections [ "Display" ] [ "iSize W" ] =
SystemParameters . ScreenWidth . ToString ( CultureInfo . CurrentCulture ) ;
data . Sections [ "Display" ] [ "iSize H" ] =
SystemParameters . ScreenHeight . ToString ( CultureInfo . CurrentCulture ) ;
modified = true ;
}
2020-01-04 10:25:53 +00:00
2020-03-10 04:11:11 +00:00
}
if ( data . Sections [ "MEMORY" ] ! = null )
2020-01-04 02:52:17 +00:00
{
2020-03-10 04:11:11 +00:00
if ( data . Sections [ "MEMORY" ] [ "VideoMemorySizeMb" ] ! = null )
{
data . Sections [ "MEMORY" ] [ "VideoMemorySizeMb" ] =
SystemParameters . EnbLEVRAMSize . ToString ( CultureInfo . CurrentCulture ) ;
modified = true ;
}
2020-01-04 02:52:17 +00:00
}
2020-03-10 04:11:11 +00:00
if ( modified )
parser . WriteFile ( file , data ) ;
2020-01-03 15:01:17 +00:00
}
catch ( Exception ex )
{
Utils . Log ( $"Skipping screen size remap for {file} due to parse error." ) ;
continue ;
}
2019-12-06 04:58:18 +00:00
}
}
2019-08-24 23:20:54 +00:00
private void WriteRemappedFile ( RemappedInlineFile directive )
{
2019-10-01 22:39:25 +00:00
var data = Encoding . UTF8 . GetString ( LoadBytesFromPath ( directive . SourceDataID ) ) ;
2019-08-24 23:20:54 +00:00
data = data . Replace ( Consts . GAME_PATH_MAGIC_BACK , GameFolder ) ;
data = data . Replace ( Consts . GAME_PATH_MAGIC_DOUBLE_BACK , GameFolder . Replace ( "\\" , "\\\\" ) ) ;
data = data . Replace ( Consts . GAME_PATH_MAGIC_FORWARD , GameFolder . Replace ( "\\" , "/" ) ) ;
2019-11-16 13:22:40 +00:00
data = data . Replace ( Consts . MO2_PATH_MAGIC_BACK , OutputFolder ) ;
data = data . Replace ( Consts . MO2_PATH_MAGIC_DOUBLE_BACK , OutputFolder . Replace ( "\\" , "\\\\" ) ) ;
data = data . Replace ( Consts . MO2_PATH_MAGIC_FORWARD , OutputFolder . Replace ( "\\" , "/" ) ) ;
2019-08-24 23:20:54 +00:00
2019-09-03 22:12:39 +00:00
data = data . Replace ( Consts . DOWNLOAD_PATH_MAGIC_BACK , DownloadFolder ) ;
data = data . Replace ( Consts . DOWNLOAD_PATH_MAGIC_DOUBLE_BACK , DownloadFolder . Replace ( "\\" , "\\\\" ) ) ;
data = data . Replace ( Consts . DOWNLOAD_PATH_MAGIC_FORWARD , DownloadFolder . Replace ( "\\" , "/" ) ) ;
2019-11-16 13:22:40 +00:00
File . WriteAllText ( Path . Combine ( OutputFolder , directive . To ) , data ) ;
2019-07-23 04:17:52 +00:00
}
2019-12-20 22:06:20 +00:00
2019-12-22 01:30:01 +00:00
public static IErrorResponse CheckValidInstallPath ( string path , string downloadFolder )
2019-12-20 22:06:20 +00:00
{
var ret = Utils . IsDirectoryPathValid ( path ) ;
if ( ! ret . Succeeded ) return ret ;
2019-12-20 22:31:35 +00:00
if ( ! Directory . Exists ( path ) ) return ErrorResponse . Success ;
2020-01-13 21:11:07 +00:00
// Check folder does not have a Wabbajack ModList
2019-12-24 06:28:39 +00:00
foreach ( var file in Directory . EnumerateFiles ( path ) )
2019-12-20 22:06:20 +00:00
{
if ( ! File . Exists ( file ) ) continue ;
2020-01-20 01:49:12 +00:00
if ( System . IO . Path . GetExtension ( file ) . Equals ( Consts . ModListExtension ) )
2019-12-20 22:06:20 +00:00
{
2020-01-13 21:11:07 +00:00
return ErrorResponse . Fail ( $"Cannot install into a folder with a Wabbajack ModList inside of it" ) ;
2019-12-20 22:06:20 +00:00
}
}
2019-12-20 22:31:35 +00:00
// Check folder is either empty, or a likely valid previous install
if ( ! Directory . IsEmpty ( path ) )
{
2019-12-22 01:30:01 +00:00
// If we have a MO2 install, assume good to go
if ( Directory . EnumerateFiles ( path ) . Any ( file = >
2019-12-20 22:31:35 +00:00
{
var fileName = Path . GetFileName ( file ) ;
if ( fileName . Equals ( "ModOrganizer.exe" , StringComparison . OrdinalIgnoreCase ) ) return true ;
if ( fileName . Equals ( "ModOrganizer.ini" , StringComparison . OrdinalIgnoreCase ) ) return true ;
return false ;
} ) )
{
2019-12-22 01:30:01 +00:00
return ErrorResponse . Success ;
}
// If we don't have a MO2 install, and there's any file that's not in the downloads folder, mark failure
if ( Directory . EnumerateFiles ( path ) . Any ( file = >
{
var fileName = Path . GetFileName ( file ) ;
if ( string . IsNullOrWhiteSpace ( downloadFolder ) ) return true ;
return ! Utils . IsUnderneathDirectory ( file , downloadFolder ) ;
} ) )
{
return ErrorResponse . Fail ( $"Cannot install into a non-empty folder that does not look like a previous WJ installation.\n" +
$"To override, delete all installed files from your target installation folder. Any files in your download folder are okay to keep." ) ;
2019-12-20 22:31:35 +00:00
}
}
2019-12-20 22:06:20 +00:00
return ErrorResponse . Success ;
}
2019-07-23 04:17:52 +00:00
}
2019-09-24 15:26:44 +00:00
}