2019-10-30 12:29:06 +00:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
2019-12-04 01:26:26 +00:00
using System.Threading.Tasks ;
2019-10-30 12:29:06 +00:00
using Alphaleonis.Win32.Filesystem ;
using Compression.BSA ;
2019-10-31 02:24:42 +00:00
using Newtonsoft.Json ;
2019-10-30 12:29:06 +00:00
using Wabbajack.Common ;
2019-12-04 04:12:08 +00:00
using Wabbajack.Common.StatusFeed.Errors ;
2020-02-05 05:17:12 +00:00
using Wabbajack.VirtualFileSystem ;
2019-10-30 12:29:06 +00:00
namespace Wabbajack.Lib.CompilationSteps
{
2019-10-31 02:24:42 +00:00
public class DeconstructBSAs : ACompilationStep
2019-10-30 12:29:06 +00:00
{
2020-04-27 21:32:19 +00:00
private readonly IEnumerable < RelativePath > _includeDirectly ;
2020-02-05 05:17:12 +00:00
private readonly Func < VirtualFile , List < ICompilationStep > > _microstack ;
private readonly Func < VirtualFile , List < ICompilationStep > > _microstackWithInclude ;
2019-11-18 00:17:06 +00:00
private readonly MO2Compiler _mo2Compiler ;
2019-10-30 12:29:06 +00:00
2019-11-03 16:45:49 +00:00
public DeconstructBSAs ( ACompiler compiler ) : base ( compiler )
2019-10-30 12:29:06 +00:00
{
2019-11-18 00:17:06 +00:00
_mo2Compiler = ( MO2Compiler ) compiler ;
2019-11-21 15:51:57 +00:00
_includeDirectly = _mo2Compiler . ModInis . Where ( kv = >
2019-10-30 12:29:06 +00:00
{
var general = kv . Value . General ;
2020-04-27 21:32:19 +00:00
if ( general . notes ! = null & & ( general . notes . Contains ( Consts . WABBAJACK_INCLUDE ) | | general . notes . Contains ( Consts . WABBAJACK_NOMATCH_INCLUDE ) ) ) return true ;
if ( general . comments ! = null & & ( general . notes . Contains ( Consts . WABBAJACK_INCLUDE ) | | general . notes . Contains ( Consts . WABBAJACK_NOMATCH_INCLUDE ) ) ) return true ;
2019-10-30 12:29:06 +00:00
return false ;
} )
2020-04-27 21:32:19 +00:00
. Select ( kv = > kv . Key . RelativeTo ( _mo2Compiler . MO2Folder ) )
2019-10-30 12:29:06 +00:00
. ToList ( ) ;
2020-02-05 05:17:12 +00:00
_microstack = bsa = > new List < ICompilationStep >
2019-10-30 12:29:06 +00:00
{
2019-11-11 20:03:27 +00:00
new DirectMatch ( _mo2Compiler ) ,
2020-02-05 05:17:12 +00:00
new IncludePatches ( _mo2Compiler , bsa ) ,
2019-11-11 20:03:27 +00:00
new DropAll ( _mo2Compiler )
2019-10-30 12:29:06 +00:00
} ;
2020-02-05 05:17:12 +00:00
_microstackWithInclude = bsa = > new List < ICompilationStep >
2019-10-30 12:29:06 +00:00
{
2019-11-11 20:03:27 +00:00
new DirectMatch ( _mo2Compiler ) ,
2020-02-05 05:17:12 +00:00
new IncludePatches ( _mo2Compiler , bsa ) ,
2019-11-11 20:03:27 +00:00
new IncludeAll ( _mo2Compiler )
2019-10-30 12:29:06 +00:00
} ;
2019-10-31 02:24:42 +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 ( ) ;
2019-10-30 12:29:06 +00:00
}
2020-04-09 18:14:05 +00:00
public override async ValueTask < Directive ? > Run ( RawSourceFile source )
2019-10-30 12:29:06 +00:00
{
2020-03-25 22:30:43 +00:00
if ( ! Consts . SupportedBSAs . Contains ( source . Path . Extension ) ) return null ;
2019-10-30 12:29:06 +00:00
var defaultInclude = false ;
2020-03-25 22:30:43 +00:00
if ( source . Path . RelativeTo ( _mo2Compiler . MO2Folder ) . InFolder ( _mo2Compiler . MO2Folder . Combine ( Consts . MO2ModFolderName ) ) )
2019-11-21 15:51:57 +00:00
if ( _includeDirectly . Any ( path = > source . Path . StartsWith ( path ) ) )
2019-10-30 12:29:06 +00:00
defaultInclude = true ;
2020-04-27 12:30:15 +00:00
if ( source . AbsolutePath . Size > = ( long ) 2 < < 31 )
2020-04-20 21:36:33 +00:00
{
await using var bsa = BSADispatch . OpenRead ( source . AbsolutePath ) ;
if ( bsa . State is BSAStateObject )
{
Utils . Error (
$"BSA {source.AbsolutePath.FileName} is over 2GB in size, very few programs (Including Wabbajack) can create BSA files this large without causing CTD issues." +
$"Please re-compress this BSA into a more manageable size." ) ;
}
}
2019-11-21 15:51:57 +00:00
var sourceFiles = source . File . Children ;
2019-10-30 12:29:06 +00:00
2020-02-05 05:17:12 +00:00
var stack = defaultInclude ? _microstackWithInclude ( source . File ) : _microstack ( source . File ) ;
2019-10-30 12:29:06 +00:00
var id = Guid . NewGuid ( ) . ToString ( ) ;
2020-04-27 21:32:19 +00:00
Func < Task > ? _cleanup = null ;
if ( defaultInclude )
{
_cleanup = await source . File . Context . Stage ( source . File . Children ) ;
}
2020-04-15 11:53:49 +00:00
var matches = await sourceFiles . PMap ( _mo2Compiler . Queue , e = > _mo2Compiler . RunStack ( stack , new RawSourceFile ( e , Consts . BSACreationDir . Combine ( ( RelativePath ) id , ( RelativePath ) e . Name ) ) ) ) ;
2019-10-30 12:29:06 +00:00
foreach ( var match in matches )
{
2019-10-31 02:24:42 +00:00
if ( match is IgnoredDirectly )
2019-12-05 04:58:02 +00:00
Utils . ErrorThrow ( new UnconvertedError ( $"File required for BSA {source.Path} creation doesn't exist: {match.To}" ) ) ;
2019-11-11 20:03:27 +00:00
_mo2Compiler . ExtraFiles . Add ( match ) ;
2019-10-30 12:29:06 +00:00
}
2019-10-31 02:24:42 +00:00
2019-10-30 12:29:06 +00:00
CreateBSA directive ;
2020-04-17 03:52:19 +00:00
await using ( var bsa = BSADispatch . OpenRead ( source . AbsolutePath ) )
2019-10-30 12:29:06 +00:00
{
2020-04-10 01:29:53 +00:00
directive = new CreateBSA (
state : bsa . State ,
items : bsa . Files . Select ( f = > f . State ) . ToList ( ) )
2019-10-30 12:29:06 +00:00
{
To = source . Path ,
2020-03-25 22:30:43 +00:00
TempID = ( RelativePath ) id ,
2019-10-30 12:29:06 +00:00
} ;
}
2020-04-27 21:32:19 +00:00
if ( _cleanup ! = null )
await _cleanup ( ) ;
2019-10-30 12:29:06 +00:00
return directive ;
}
2019-10-31 02:24:42 +00:00
[JsonObject("DeconstructBSAs")]
public class State : IState
{
2019-11-03 16:45:49 +00:00
public ICompilationStep CreateStep ( ACompiler compiler )
2019-10-31 02:24:42 +00:00
{
return new DeconstructBSAs ( compiler ) ;
}
}
2019-10-30 12:29:06 +00:00
}
2019-12-04 04:12:08 +00:00
}