2019-10-07 17:33:34 +00:00
using Alphaleonis.Win32.Filesystem ;
using System ;
2019-09-23 21:37:10 +00:00
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
2019-10-31 02:24:42 +00:00
using Newtonsoft.Json ;
2019-09-23 21:37:10 +00:00
using Wabbajack.Common ;
2019-10-30 12:29:06 +00:00
using Wabbajack.Lib.CompilationSteps ;
2019-09-23 21:37:10 +00:00
using Directory = Alphaleonis . Win32 . Filesystem . Directory ;
using File = Alphaleonis . Win32 . Filesystem . File ;
using Path = Alphaleonis . Win32 . Filesystem . Path ;
2019-12-04 01:26:26 +00:00
using System.Threading.Tasks ;
2019-09-23 21:37:10 +00:00
2019-10-16 03:10:34 +00:00
namespace Wabbajack.Lib
2019-09-23 21:37:10 +00:00
{
public class zEditIntegration
{
2019-11-18 00:17:06 +00:00
private static MO2Compiler _mo2Compiler ;
2019-11-11 20:05:23 +00:00
2019-11-03 16:46:26 +00:00
public static string FindzEditPath ( ACompiler compiler )
2019-09-23 21:37:10 +00:00
{
2019-11-18 00:17:06 +00:00
_mo2Compiler = ( MO2Compiler ) compiler ;
2019-11-11 20:05:23 +00:00
var executables = _mo2Compiler . MO2Ini . customExecutables ;
2019-09-24 04:20:24 +00:00
if ( executables . size = = null ) return null ;
2019-09-23 21:37:10 +00:00
foreach ( var idx in Enumerable . Range ( 1 , int . Parse ( executables . size ) ) )
{
var path = ( string ) executables [ $"{idx}\\binary" ] ;
if ( path = = null ) continue ;
if ( path . EndsWith ( "zEdit.exe" ) )
return Path . GetDirectoryName ( path ) ;
}
return null ;
}
2019-10-30 12:29:06 +00:00
public class IncludeZEditPatches : ACompilationStep
2019-09-23 21:37:10 +00:00
{
2019-10-30 12:29:06 +00:00
private Dictionary < string , zEditMerge > _mergesIndexed ;
2019-09-23 21:37:10 +00:00
2019-11-03 16:46:26 +00:00
public IncludeZEditPatches ( ACompiler compiler ) : base ( compiler )
2019-10-30 12:29:06 +00:00
{
var zEditPath = FindzEditPath ( compiler ) ;
var havezEdit = zEditPath ! = null ;
2019-09-23 21:37:10 +00:00
2019-10-30 12:29:06 +00:00
Utils . Log ( havezEdit ? $"Found zEdit at {zEditPath}" : $"zEdit not detected, disabling zEdit routines" ) ;
2019-09-23 21:37:10 +00:00
2019-10-30 12:29:06 +00:00
if ( ! havezEdit )
2019-10-07 17:33:34 +00:00
{
2019-10-30 12:29:06 +00:00
_mergesIndexed = new Dictionary < string , zEditMerge > ( ) ;
return ;
}
2019-09-23 21:37:10 +00:00
2019-10-30 12:29:06 +00:00
var merges = Directory . EnumerateFiles ( Path . Combine ( zEditPath , "profiles" ) ,
DirectoryEnumerationOptions . Files | DirectoryEnumerationOptions . Recursive )
. Where ( f = > f . EndsWith ( "\\merges.json" ) )
. SelectMany ( f = > f . FromJSON < List < zEditMerge > > ( ) )
. GroupBy ( f = > ( f . name , f . filename ) ) ;
2019-09-23 21:37:10 +00:00
2019-10-30 12:29:06 +00:00
merges . Where ( m = > m . Count ( ) > 1 )
. Do ( m = >
{
2019-12-04 04:12:08 +00:00
Utils . Log (
2019-10-30 12:29:06 +00:00
$"WARNING, you have two patches named {m.Key.name}\\{m.Key.filename} in your zEdit profiles. We'll pick one at random, this probably isn't what you want." ) ;
} ) ;
_mergesIndexed =
merges . ToDictionary (
2020-02-05 05:17:12 +00:00
m = > Path . Combine ( _mo2Compiler . MO2Folder , Consts . MO2ModFolderName , m . Key . name , m . Key . filename ) ,
2019-10-30 12:29:06 +00:00
m = > m . First ( ) ) ;
}
2019-09-23 21:37:10 +00:00
2019-12-04 01:26:26 +00:00
public override async ValueTask < Directive > Run ( RawSourceFile source )
2019-09-23 21:37:10 +00:00
{
2019-10-30 12:29:06 +00:00
if ( ! _mergesIndexed . TryGetValue ( source . AbsolutePath , out var merge ) ) return null ;
var result = source . EvolveTo < MergedPatch > ( ) ;
result . Sources = merge . plugins . Select ( f = >
2019-09-23 21:37:10 +00:00
{
2020-04-03 12:53:22 +00:00
var origPath = Path . Combine ( f . dataFolder , f . filename ) ;
2019-11-02 20:32:40 +00:00
var paths = new [ ]
{
2020-04-03 12:53:22 +00:00
origPath ,
origPath + ".mohidden" ,
Path . Combine ( Path . GetDirectoryName ( origPath ) , "optional" , Path . GetFileName ( origPath ) )
2019-11-02 20:32:40 +00:00
} ;
2020-04-03 12:53:22 +00:00
var absPath = paths . FirstOrDefault ( File . Exists ) ;
2019-11-02 20:32:40 +00:00
2020-04-03 12:53:22 +00:00
if ( absPath = = null )
2019-10-30 12:29:06 +00:00
throw new InvalidDataException (
2020-04-03 12:53:22 +00:00
$"File {origPath} is required to build {merge.filename} but it doesn't exist searched in: \n" + string . Join ( "\n" , paths ) ) ;
string hash = "" ;
try
{
hash = _compiler . VFS . Index . ByFullPath [ absPath ] . Hash ;
} catch ( KeyNotFoundException e )
{
Utils . ErrorThrow ( e , $"Could not find the key {absPath} in the VFS Index dictionary!" ) ;
}
2019-09-23 21:37:10 +00:00
2019-10-30 12:29:06 +00:00
return new SourcePatch
2019-09-23 21:37:10 +00:00
{
2020-04-03 12:53:22 +00:00
RelativePath = absPath . RelativeTo ( _mo2Compiler . MO2Folder ) ,
Hash = hash
2019-10-30 12:29:06 +00:00
} ;
} ) . ToList ( ) ;
2019-11-11 20:05:23 +00:00
var src_data = result . Sources . Select ( f = > File . ReadAllBytes ( Path . Combine ( _mo2Compiler . MO2Folder , f . RelativePath ) ) )
2019-10-30 12:29:06 +00:00
. ConcatArrays ( ) ;
2019-09-23 21:37:10 +00:00
2019-10-30 12:29:06 +00:00
var dst_data = File . ReadAllBytes ( source . AbsolutePath ) ;
2019-09-23 21:37:10 +00:00
2020-02-05 05:17:12 +00:00
await using ( var ms = new MemoryStream ( ) )
2019-10-30 12:29:06 +00:00
{
2020-02-05 05:17:12 +00:00
await Utils . CreatePatch ( src_data , dst_data , ms ) ;
2019-10-30 12:29:06 +00:00
result . PatchID = _compiler . IncludeFile ( ms . ToArray ( ) ) ;
2019-09-23 21:37:10 +00:00
}
2019-10-30 12:29:06 +00:00
return result ;
2019-09-23 21:37:10 +00:00
2019-10-30 12:29:06 +00:00
}
2019-10-31 02:24:42 +00:00
public override IState GetState ( )
{
return new State ( ) ;
}
[JsonObject("IncludeZEditPatches")]
public class State : IState
{
2019-11-03 16:46:26 +00:00
public ICompilationStep CreateStep ( ACompiler compiler )
2019-10-31 02:24:42 +00:00
{
return new IncludeZEditPatches ( compiler ) ;
}
}
2019-09-23 21:37:10 +00:00
}
2019-11-02 15:38:03 +00:00
public class zEditMerge
2019-09-23 21:37:10 +00:00
{
public string name ;
public string filename ;
public List < zEditMergePlugin > plugins ;
}
2019-11-02 15:38:03 +00:00
public class zEditMergePlugin
2019-09-23 21:37:10 +00:00
{
public string filename ;
public string dataFolder ;
}
2019-11-18 00:17:06 +00:00
public static void VerifyMerges ( MO2Compiler compiler )
2019-09-23 21:37:10 +00:00
{
var by_name = compiler . InstallDirectives . ToDictionary ( f = > f . To ) ;
foreach ( var directive in compiler . InstallDirectives . OfType < MergedPatch > ( ) )
{
foreach ( var source in directive . Sources )
{
if ( by_name . TryGetValue ( source . RelativePath , out var result ) )
{
if ( result . Hash ! = source . Hash )
throw new InvalidDataException ( $"Hashes for {result.To} needed for zEdit merge sources don't match, this shouldn't happen" ) ;
continue ;
}
throw new InvalidDataException ( $"{source.RelativePath} is needed for merged patch {directive.To} but is not included in the install." ) ;
}
}
}
2019-11-02 15:38:03 +00:00
2019-12-04 01:26:26 +00:00
public static async Task GenerateMerges ( MO2Installer installer )
2019-11-02 15:38:03 +00:00
{
2019-12-04 01:26:26 +00:00
await installer . ModList
2019-11-02 15:38:03 +00:00
. Directives
. OfType < MergedPatch > ( )
2019-11-19 13:04:17 +00:00
. PMap ( installer . Queue , m = >
2019-11-02 15:38:03 +00:00
{
Utils . LogStatus ( $"Generating zEdit merge: {m.To}" ) ;
2019-11-16 13:23:41 +00:00
var src_data = m . Sources . Select ( s = > File . ReadAllBytes ( Path . Combine ( installer . OutputFolder , s . RelativePath ) ) )
2019-11-02 15:38:03 +00:00
. ConcatArrays ( ) ;
var patch_data = installer . LoadBytesFromPath ( m . PatchID ) ;
2020-01-18 20:52:09 +00:00
using ( var fs = File . Open ( Path . Combine ( installer . OutputFolder , m . To ) , FileMode . Create ) )
2020-02-24 23:18:29 +00:00
Utils . ApplyPatch ( new MemoryStream ( src_data ) , ( ) = > new MemoryStream ( patch_data ) , fs ) ;
2019-11-02 15:38:03 +00:00
} ) ;
}
2019-09-23 21:37:10 +00:00
}
}