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 (
2019-11-11 20:05:23 +00:00
m = > Path . Combine ( _mo2Compiler . MO2Folder , "mods" , 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
{
2019-11-02 20:32:40 +00:00
var orig_path = Path . Combine ( f . dataFolder , f . filename ) ;
var paths = new [ ]
{
orig_path ,
orig_path + ".mohidden" ,
Path . Combine ( Path . GetDirectoryName ( orig_path ) , "optional" , Path . GetFileName ( orig_path ) )
} ;
var abs_path = paths . FirstOrDefault ( p = > File . Exists ( p ) ) ;
if ( abs_path = = null )
2019-10-30 12:29:06 +00:00
throw new InvalidDataException (
2019-11-02 20:32:40 +00:00
$"File {abs_path} is required to build {merge.filename} but it doesn't exist searched in: \n" + String . Join ( "\n" , paths ) ) ;
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
{
2019-11-11 20:05:23 +00:00
RelativePath = abs_path . RelativeTo ( _mo2Compiler . MO2Folder ) ,
2019-11-15 13:06:34 +00:00
Hash = _compiler . VFS . Index . ByFullPath [ abs_path ] . 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
2019-10-30 12:29:06 +00:00
using ( var ms = new MemoryStream ( ) )
{
Utils . CreatePatch ( src_data , dst_data , ms ) ;
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 ) ;
2019-11-16 13:23:41 +00:00
using ( var fs = File . OpenWrite ( Path . Combine ( installer . OutputFolder , m . To ) ) )
2019-11-02 15:38:03 +00:00
BSDiff . Apply ( new MemoryStream ( src_data ) , ( ) = > new MemoryStream ( patch_data ) , fs ) ;
} ) ;
}
2019-09-23 21:37:10 +00:00
}
}