2019-12-26 16:47:10 +00:00
using System ;
2020-04-17 03:52:19 +00:00
using System.Collections.Generic ;
2019-08-25 23:52:03 +00:00
using System.Diagnostics ;
2019-09-24 11:27:43 +00:00
using System.Linq ;
2020-04-10 12:58:55 +00:00
using System.Reactive.Linq ;
2019-12-04 01:26:26 +00:00
using System.Threading.Tasks ;
2019-08-28 03:22:57 +00:00
using Alphaleonis.Win32.Filesystem ;
2019-09-14 04:35:42 +00:00
using Compression.BSA ;
2020-02-14 18:19:51 +00:00
using OMODFramework ;
2019-12-04 04:12:08 +00:00
using Wabbajack.Common.StatusFeed ;
using Wabbajack.Common.StatusFeed.Errors ;
2020-03-05 00:02:16 +00:00
using Wabbajack.Common ;
using Utils = Wabbajack . Common . Utils ;
2019-08-09 04:07:23 +00:00
2020-03-05 00:02:16 +00:00
namespace Wabbajack.VirtualFileSystem
2019-08-09 04:07:23 +00:00
{
public class FileExtractor
{
2020-04-17 03:52:19 +00:00
public static async Task < ExtractedFiles > ExtractAll ( WorkQueue queue , AbsolutePath source , IEnumerable < RelativePath > OnlyFiles = null )
2019-08-09 04:07:23 +00:00
{
2019-09-08 22:44:15 +00:00
try
{
2020-05-25 16:30:47 +00:00
if ( await BSADispatch . MightBeBSA ( source ) )
2020-04-17 03:52:19 +00:00
return await ExtractAllWithBSA ( queue , source ) ;
2020-03-23 12:57:18 +00:00
else if ( source . Extension = = Consts . OMOD )
2020-04-28 03:17:06 +00:00
return await ExtractAllWithOMOD ( source ) ;
2020-03-23 12:57:18 +00:00
else if ( source . Extension = = Consts . EXE )
2020-04-17 03:52:19 +00:00
return await ExtractAllExe ( source ) ;
2019-09-08 22:44:15 +00:00
else
2020-04-17 03:52:19 +00:00
return await ExtractAllWith7Zip ( source , OnlyFiles ) ;
2019-08-09 04:07:23 +00:00
}
2019-09-08 22:44:15 +00:00
catch ( Exception ex )
2019-08-09 04:07:23 +00:00
{
2019-12-05 05:26:15 +00:00
Utils . ErrorThrow ( ex , $"Error while extracting {source}" ) ;
2020-04-17 03:52:19 +00:00
throw new Exception ( ) ;
2019-08-09 04:07:23 +00:00
}
}
2020-04-17 03:52:19 +00:00
private static async Task < ExtractedFiles > ExtractAllExe ( AbsolutePath source )
2019-12-01 13:57:28 +00:00
{
2020-04-10 12:58:55 +00:00
var isArchive = await TestWith7z ( source ) ;
2020-04-09 10:48:08 +00:00
if ( isArchive )
{
2020-04-17 03:52:19 +00:00
return await ExtractAllWith7Zip ( source , null ) ;
2020-04-09 10:48:08 +00:00
}
2020-04-28 03:17:06 +00:00
var dest = await TempFolder . Create ( ) ;
2020-03-23 12:57:18 +00:00
Utils . Log ( $"Extracting {(string)source.FileName}" ) ;
2019-12-01 13:57:28 +00:00
2020-04-10 12:58:55 +00:00
var process = new ProcessHelper
2019-12-01 13:57:28 +00:00
{
2020-04-10 12:58:55 +00:00
Path = @"Extractors\innounp.exe" . RelativeTo ( AbsolutePath . EntryPoint ) ,
2020-04-17 03:52:19 +00:00
Arguments = new object [ ] { "-x" , "-y" , "-b" , $"-d\" { dest . Dir } \ "" , source }
2019-12-01 13:57:28 +00:00
} ;
2020-04-10 12:58:55 +00:00
var result = process . Output . Where ( d = > d . Type = = ProcessHelper . StreamType . Output )
. ForEachAsync ( p = >
2019-12-01 13:57:28 +00:00
{
2020-04-10 12:58:55 +00:00
var ( _ , line ) = p ;
2019-12-01 13:57:28 +00:00
if ( line = = null )
2020-04-10 12:58:55 +00:00
return ;
2019-12-01 13:57:28 +00:00
if ( line . Length < = 4 | | line [ 3 ] ! = '%' )
2020-04-10 12:58:55 +00:00
return ;
2019-12-01 13:57:28 +00:00
2020-02-08 04:35:08 +00:00
int . TryParse ( line . Substring ( 0 , 3 ) , out var percentInt ) ;
2020-04-10 12:58:55 +00:00
Utils . Status ( $"Extracting {source.FileName} - {line.Trim()}" , Percent . FactoryPutInRange ( percentInt / 100d ) ) ;
} ) ;
await process . Start ( ) ;
2020-04-17 03:52:19 +00:00
return new ExtractedFiles ( dest ) ;
}
2019-12-01 13:57:28 +00:00
2020-02-14 18:19:51 +00:00
private class OMODProgress : ICodeProgress
{
private long _total ;
public void SetProgress ( long inSize , long outSize )
{
Utils . Status ( "Extracting OMOD" , Percent . FactoryPutInRange ( inSize , _total ) ) ;
}
public void Init ( long totalSize , bool compressing )
{
_total = totalSize ;
}
public void Dispose ( )
{
//
}
}
2020-04-28 03:17:06 +00:00
private static async Task < ExtractedFiles > ExtractAllWithOMOD ( AbsolutePath source )
2019-09-24 16:03:50 +00:00
{
2020-04-28 03:17:06 +00:00
var dest = await TempFolder . Create ( ) ;
2020-03-23 12:57:18 +00:00
Utils . Log ( $"Extracting {(string)source.FileName}" ) ;
2020-02-14 18:19:51 +00:00
2020-04-17 03:52:19 +00:00
Framework . Settings . TempPath = ( string ) dest . Dir ;
2020-02-14 18:19:51 +00:00
Framework . Settings . CodeProgress = new OMODProgress ( ) ;
2020-03-23 12:57:18 +00:00
var omod = new OMOD ( ( string ) source ) ;
2020-02-14 18:19:51 +00:00
omod . GetDataFiles ( ) ;
omod . GetPlugins ( ) ;
2020-04-17 03:52:19 +00:00
return new ExtractedFiles ( dest ) ;
2019-09-24 16:03:50 +00:00
}
2019-12-26 16:47:10 +00:00
2020-04-17 03:52:19 +00:00
private static async Task < ExtractedFiles > ExtractAllWithBSA ( WorkQueue queue , AbsolutePath source )
2019-08-09 04:07:23 +00:00
{
2019-09-12 23:44:35 +00:00
try
2019-08-09 04:07:23 +00:00
{
2020-05-25 16:30:47 +00:00
await using var arch = await BSADispatch . OpenRead ( source ) ;
2020-04-17 03:52:19 +00:00
var files = arch . Files . ToDictionary ( f = > f . Path , f = > ( IExtractedFile ) new ExtractedBSAFile ( f ) ) ;
return new ExtractedFiles ( files , arch ) ;
2019-09-12 23:44:35 +00:00
}
catch ( Exception ex )
{
2019-12-05 05:26:15 +00:00
Utils . ErrorThrow ( ex , $"While Extracting {source}" ) ;
2020-04-17 03:52:19 +00:00
throw new Exception ( ) ;
2019-08-09 04:07:23 +00:00
}
}
2020-04-17 03:52:19 +00:00
private static async Task < ExtractedFiles > ExtractAllWith7Zip ( AbsolutePath source , IEnumerable < RelativePath > onlyFiles )
2019-08-09 04:07:23 +00:00
{
2020-04-17 03:52:19 +00:00
TempFile tmpFile = null ;
2020-04-28 03:17:06 +00:00
var dest = await TempFolder . Create ( ) ;
2020-03-23 12:57:18 +00:00
Utils . Log ( new GenericInfo ( $"Extracting {(string)source.FileName}" , $"The contents of {(string)source.FileName} are being extracted to {(string)source.FileName} using 7zip.exe" ) ) ;
2019-08-09 04:07:23 +00:00
2020-04-10 12:58:55 +00:00
var process = new ProcessHelper
2019-08-09 04:07:23 +00:00
{
2020-04-10 12:58:55 +00:00
Path = @"Extractors\7z.exe" . RelativeTo ( AbsolutePath . EntryPoint ) ,
2020-04-17 03:52:19 +00:00
2019-08-25 23:52:03 +00:00
} ;
2020-04-10 12:58:55 +00:00
2020-04-17 03:52:19 +00:00
if ( onlyFiles ! = null )
{
2020-04-17 13:06:28 +00:00
//It's stupid that we have to do this, but 7zip's file pattern matching isn't very fuzzy
IEnumerable < string > AllVariants ( string input )
{
2020-04-27 11:06:57 +00:00
yield return $"\" { input } \ "" ;
yield return $"\" \ \ { input } \ "" ;
2020-04-17 13:06:28 +00:00
}
2020-04-17 03:52:19 +00:00
tmpFile = new TempFile ( ) ;
2020-04-17 13:06:28 +00:00
await tmpFile . Path . WriteAllLinesAsync ( onlyFiles . SelectMany ( f = > AllVariants ( ( string ) f ) ) . ToArray ( ) ) ;
2020-04-17 03:52:19 +00:00
process . Arguments = new object [ ]
{
"x" , "-bsp1" , "-y" , $"-o\" { dest . Dir } \ "" , source , $"@\" { tmpFile . Path } \ "" , "-mmt=off"
} ;
}
else
{
process . Arguments = new object [ ] { "x" , "-bsp1" , "-y" , $"-o\" { dest . Dir } \ "" , source , "-mmt=off" } ;
}
2019-08-09 04:07:23 +00:00
2020-04-10 12:58:55 +00:00
var result = process . Output . Where ( d = > d . Type = = ProcessHelper . StreamType . Output )
. ForEachAsync ( p = >
2019-08-29 22:49:48 +00:00
{
2020-04-10 12:58:55 +00:00
var ( _ , line ) = p ;
2019-09-24 11:27:43 +00:00
if ( line = = null )
2020-04-10 12:58:55 +00:00
return ;
2019-12-01 13:57:28 +00:00
2020-04-10 12:58:55 +00:00
if ( line . Length < = 4 | | line [ 3 ] ! = '%' ) return ;
2019-12-01 13:57:28 +00:00
2020-02-08 04:35:08 +00:00
int . TryParse ( line . Substring ( 0 , 3 ) , out var percentInt ) ;
2020-04-10 12:58:55 +00:00
Utils . Status ( $"Extracting {(string)source.FileName} - {line.Trim()}" , Percent . FactoryPutInRange ( percentInt / 100d ) ) ;
} ) ;
2019-08-29 22:49:48 +00:00
2020-04-10 12:58:55 +00:00
var exitCode = await process . Start ( ) ;
2019-12-22 06:27:57 +00:00
2020-04-10 12:58:55 +00:00
if ( exitCode ! = 0 )
2019-08-25 23:52:03 +00:00
{
2020-04-17 03:52:19 +00:00
Utils . Error ( new _7zipReturnError ( exitCode , source , dest . Dir , "" ) ) ;
2020-04-10 12:58:55 +00:00
}
else
{
Utils . Status ( $"Extracting {source.FileName} - done" , Percent . One , alsoLog : true ) ;
2019-08-09 04:07:23 +00:00
}
2020-04-17 03:52:19 +00:00
2020-05-28 02:43:57 +00:00
if ( tmpFile ! = null )
{
await tmpFile . DisposeAsync ( ) ;
}
2020-04-17 03:52:19 +00:00
return new ExtractedFiles ( dest ) ;
2019-08-09 04:07:23 +00:00
}
2019-08-15 04:30:37 +00:00
/// <summary>
2019-09-14 04:35:42 +00:00
/// Returns true if the given extension type can be extracted
2019-08-15 04:30:37 +00:00
/// </summary>
/// <param name="v"></param>
/// <returns></returns>
2020-04-10 12:58:55 +00:00
public static async Task < bool > CanExtract ( AbsolutePath v )
2019-08-15 04:30:37 +00:00
{
2020-03-23 12:57:18 +00:00
var ext = v . Extension ;
if ( ext ! = _exeExtension & & ! Consts . TestArchivesBeforeExtraction . Contains ( ext ) )
2019-12-01 13:57:28 +00:00
return Consts . SupportedArchives . Contains ( ext ) | | Consts . SupportedBSAs . Contains ( ext ) ;
2020-04-10 12:58:55 +00:00
var isArchive = await TestWith7z ( v ) ;
2019-12-15 11:58:03 +00:00
2020-04-09 10:48:08 +00:00
if ( isArchive )
return true ;
2019-12-15 11:58:03 +00:00
2020-04-10 12:58:55 +00:00
var process = new ProcessHelper
2020-04-09 10:48:08 +00:00
{
2020-04-10 12:58:55 +00:00
Path = @"Extractors\innounp.exe" . RelativeTo ( AbsolutePath . EntryPoint ) ,
Arguments = new object [ ] { "-t" , v } ,
2020-04-09 10:48:08 +00:00
} ;
2019-12-15 11:58:03 +00:00
2020-04-10 12:58:55 +00:00
return await process . Start ( ) = = 0 ;
2020-04-09 10:48:08 +00:00
}
2020-01-10 04:47:06 +00:00
2020-04-10 12:58:55 +00:00
public static async Task < bool > TestWith7z ( AbsolutePath file )
2020-04-09 10:48:08 +00:00
{
2020-04-10 12:58:55 +00:00
var process = new ProcessHelper ( )
2019-12-15 11:58:03 +00:00
{
2020-04-10 12:58:55 +00:00
Path = @"Extractors\7z.exe" . RelativeTo ( AbsolutePath . EntryPoint ) ,
Arguments = new object [ ] { "t" , file } ,
2019-12-01 13:57:28 +00:00
} ;
2020-04-10 12:58:55 +00:00
return await process . Start ( ) = = 0 ;
2019-08-15 04:30:37 +00:00
}
2020-01-10 04:47:06 +00:00
2020-03-23 12:57:18 +00:00
private static Extension _exeExtension = new Extension ( ".exe" ) ;
2020-04-17 03:52:19 +00:00
public static bool MightBeArchive ( Extension ext )
2020-01-10 04:47:06 +00:00
{
2020-03-23 12:57:18 +00:00
return ext = = _exeExtension | | Consts . SupportedArchives . Contains ( ext ) | | Consts . SupportedBSAs . Contains ( ext ) ;
2020-01-10 04:47:06 +00:00
}
2019-08-09 04:07:23 +00:00
}
2019-12-01 13:57:28 +00:00
}