2019-12-26 16:47:10 +00:00
using System ;
2019-08-25 23:52:03 +00:00
using System.Diagnostics ;
2019-09-24 11:27:43 +00:00
using System.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-03-23 12:57:18 +00:00
public static async Task ExtractAll ( WorkQueue queue , AbsolutePath source , AbsolutePath dest )
2019-08-09 04:07:23 +00:00
{
2019-09-08 22:44:15 +00:00
try
{
2020-03-23 12:57:18 +00:00
if ( Consts . SupportedBSAs . Contains ( source . Extension ) )
2019-12-04 01:26:26 +00:00
await ExtractAllWithBSA ( queue , source , dest ) ;
2020-03-23 12:57:18 +00:00
else if ( source . Extension = = Consts . OMOD )
2019-11-16 00:01:37 +00:00
ExtractAllWithOMOD ( source , dest ) ;
2020-03-23 12:57:18 +00:00
else if ( source . Extension = = Consts . EXE )
2020-04-09 10:48:08 +00:00
ExtractAllEXE ( source , dest ) ;
2019-09-08 22:44:15 +00:00
else
2019-12-05 05:26:15 +00:00
ExtractAllWith7Zip ( source , dest ) ;
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}" ) ;
2019-08-09 04:07:23 +00:00
}
}
2020-04-09 22:36:07 +00:00
private static void ExtractAllEXE ( AbsolutePath source , AbsolutePath dest )
2019-12-01 13:57:28 +00:00
{
2020-04-09 10:48:08 +00:00
var isArchive = TestWith7z ( source ) ;
if ( isArchive )
{
ExtractAllWith7Zip ( source , dest ) ;
return ;
}
2020-03-23 12:57:18 +00:00
Utils . Log ( $"Extracting {(string)source.FileName}" ) ;
2019-12-01 13:57:28 +00:00
var info = new ProcessStartInfo
{
2020-01-29 12:20:37 +00:00
FileName = @"Extractors\innounp.exe" ,
2020-03-23 12:57:18 +00:00
Arguments = $"-x -y -b -d\" { ( string ) dest } \ " \"{(string)source}\"" ,
2019-12-01 13:57:28 +00:00
RedirectStandardError = true ,
RedirectStandardInput = true ,
RedirectStandardOutput = true ,
UseShellExecute = false ,
CreateNoWindow = true
} ;
var p = new Process { StartInfo = info } ;
p . Start ( ) ;
ChildProcessTracker . AddProcess ( p ) ;
try
{
p . PriorityClass = ProcessPriorityClass . BelowNormal ;
}
catch ( Exception e )
{
2019-12-05 05:07:44 +00:00
Utils . Error ( e , "Error while setting process priority level for innounp.exe" ) ;
2019-12-01 13:57:28 +00:00
}
2020-03-23 12:57:18 +00:00
var name = source . FileName ;
2019-12-01 13:57:28 +00:00
try
{
while ( ! p . HasExited )
{
var line = p . StandardOutput . ReadLine ( ) ;
if ( line = = null )
break ;
if ( line . Length < = 4 | | line [ 3 ] ! = '%' )
continue ;
2020-02-08 04:35:08 +00:00
int . TryParse ( line . Substring ( 0 , 3 ) , out var percentInt ) ;
2020-03-23 12:57:18 +00:00
Utils . Status ( $"Extracting {(string)name} - {line.Trim()}" , Percent . FactoryPutInRange ( percentInt / 100d ) ) ;
2019-12-01 13:57:28 +00:00
}
}
catch ( Exception e )
{
2019-12-05 05:07:44 +00:00
Utils . Error ( e , "Error while reading StandardOutput for innounp.exe" ) ;
2019-12-01 13:57:28 +00:00
}
2020-03-23 12:57:18 +00:00
p . WaitForExitAndWarn ( TimeSpan . FromSeconds ( 30 ) , $"Extracting {(string)name}" ) ;
2019-12-01 13:57:28 +00:00
if ( p . ExitCode = = 0 )
return ;
Utils . Log ( p . StandardOutput . ReadToEnd ( ) ) ;
Utils . Log ( $"Extraction error extracting {source}" ) ;
}
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-03-23 12:57:18 +00:00
private static void ExtractAllWithOMOD ( AbsolutePath source , AbsolutePath dest )
2019-09-24 16:03:50 +00:00
{
2020-03-23 12:57:18 +00:00
Utils . Log ( $"Extracting {(string)source.FileName}" ) ;
2020-02-14 18:19:51 +00:00
2020-03-23 12:57:18 +00:00
Framework . Settings . TempPath = ( string ) dest ;
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 ( ) ;
2019-09-24 16:03:50 +00:00
}
2019-12-26 16:47:10 +00:00
2020-03-23 12:57:18 +00:00
private static async Task ExtractAllWithBSA ( WorkQueue queue , AbsolutePath source , AbsolutePath dest )
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-03-23 12:57:18 +00:00
using var arch = BSADispatch . OpenRead ( source ) ;
await arch . Files
. PMap ( queue , f = >
{
Utils . Status ( $"Extracting {(string)f.Path}" ) ;
var outPath = f . Path . RelativeTo ( dest ) ;
var parent = outPath . Parent ;
if ( ! parent . IsDirectory )
parent . CreateDirectory ( ) ;
using var fs = outPath . Create ( ) ;
f . CopyDataTo ( fs ) ;
} ) ;
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}" ) ;
2019-08-09 04:07:23 +00:00
}
}
2020-03-23 12:57:18 +00:00
private static void ExtractAllWith7Zip ( AbsolutePath source , AbsolutePath dest )
2019-08-09 04:07:23 +00:00
{
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
2019-08-25 23:52:03 +00:00
var info = new ProcessStartInfo
2019-08-09 04:07:23 +00:00
{
2020-01-29 12:20:37 +00:00
FileName = @"Extractors\7z.exe" ,
2020-03-23 12:57:18 +00:00
Arguments = $"x -bsp1 -y -o\" { ( string ) dest } \ " \"{(string)source}\" -mmt=off" ,
2019-08-25 23:52:03 +00:00
RedirectStandardError = true ,
RedirectStandardInput = true ,
RedirectStandardOutput = true ,
UseShellExecute = false ,
2019-09-14 04:35:42 +00:00
CreateNoWindow = true
2019-08-25 23:52:03 +00:00
} ;
2019-08-09 04:07:23 +00:00
2019-12-01 13:57:28 +00:00
var p = new Process { StartInfo = info } ;
2019-08-09 04:07:23 +00:00
2019-08-25 23:52:03 +00:00
p . Start ( ) ;
2019-08-26 23:02:49 +00:00
ChildProcessTracker . AddProcess ( p ) ;
2019-08-28 03:22:57 +00:00
try
{
p . PriorityClass = ProcessPriorityClass . BelowNormal ;
}
2019-09-14 04:35:42 +00:00
catch ( Exception )
{
}
2019-08-09 04:07:23 +00:00
2020-03-23 12:57:18 +00:00
var name = source . FileName ;
2019-08-29 22:49:48 +00:00
try
{
while ( ! p . HasExited )
{
2019-11-16 00:01:37 +00:00
var line = p . StandardOutput . ReadLine ( ) ;
2019-09-24 11:27:43 +00:00
if ( line = = null )
break ;
2019-12-01 13:57:28 +00:00
2019-11-12 05:33:32 +00:00
if ( line . Length < = 4 | | line [ 3 ] ! = '%' ) continue ;
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-03-23 12:57:18 +00:00
Utils . Status ( $"Extracting {(string)name} - {line.Trim()}" , Percent . FactoryPutInRange ( percentInt / 100d ) ) ;
2019-08-29 22:49:48 +00:00
}
}
2019-10-16 21:36:14 +00:00
catch ( Exception )
2019-08-29 22:49:48 +00:00
{
}
2019-12-22 06:50:11 +00:00
p . WaitForExitAndWarn ( TimeSpan . FromSeconds ( 30 ) , $"Extracting {name}" ) ;
2019-12-22 06:27:57 +00:00
2019-12-04 04:12:08 +00:00
if ( p . ExitCode = = 0 )
2019-08-25 23:52:03 +00:00
{
2020-02-08 04:35:08 +00:00
Utils . Status ( $"Extracting {name} - 100%" , Percent . One , alsoLog : true ) ;
2019-12-04 04:12:08 +00:00
return ;
2019-08-09 04:07:23 +00:00
}
2020-01-23 23:02:49 +00:00
Utils . Error ( new _7zipReturnError ( p . ExitCode , source , dest , p . StandardOutput . ReadToEnd ( ) ) ) ;
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-03-23 12:57:18 +00:00
public static 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-09 10:48:08 +00:00
var isArchive = 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-09 10:48:08 +00:00
var info = new ProcessStartInfo
{
FileName = @"Extractors\innounp.exe" ,
Arguments = $"-t \" { v } \ " " ,
RedirectStandardError = true ,
RedirectStandardInput = true ,
RedirectStandardOutput = true ,
UseShellExecute = false ,
CreateNoWindow = true
} ;
2019-12-15 11:58:03 +00:00
2020-04-09 10:48:08 +00:00
var p = new Process { StartInfo = info } ;
2019-12-15 11:58:03 +00:00
2020-04-09 10:48:08 +00:00
p . Start ( ) ;
ChildProcessTracker . AddProcess ( p ) ;
2019-12-15 11:58:03 +00:00
2020-04-09 22:36:07 +00:00
var name = v . FileName ;
2020-04-09 10:48:08 +00:00
while ( ! p . HasExited )
{
var line = p . StandardOutput . ReadLine ( ) ;
if ( line = = null )
break ;
2019-12-15 11:58:03 +00:00
2020-04-09 10:48:08 +00:00
if ( line [ 0 ] ! = '#' )
continue ;
2020-04-09 22:36:07 +00:00
Utils . Status ( $"Testing {(string)name} - {line.Trim()}" ) ;
2019-12-15 11:58:03 +00:00
}
2020-04-09 10:48:08 +00:00
p . WaitForExitAndWarn ( TimeSpan . FromSeconds ( 30 ) , $"Testing {name}" ) ;
return p . ExitCode = = 0 ;
}
2020-01-10 04:47:06 +00:00
2020-04-09 22:36:07 +00:00
public static bool TestWith7z ( AbsolutePath file )
2020-04-09 10:48:08 +00:00
{
2019-12-15 11:58:03 +00:00
var testInfo = new ProcessStartInfo
{
2020-01-30 22:44:38 +00:00
FileName = @"Extractors\7z.exe" ,
2020-04-09 10:48:08 +00:00
Arguments = $"t \" { file } \ "" ,
2019-12-01 13:57:28 +00:00
RedirectStandardError = true ,
RedirectStandardInput = true ,
RedirectStandardOutput = true ,
UseShellExecute = false ,
CreateNoWindow = true
} ;
2019-12-15 11:58:03 +00:00
var testP = new Process { StartInfo = testInfo } ;
2019-12-01 13:57:28 +00:00
2019-12-15 11:58:03 +00:00
testP . Start ( ) ;
ChildProcessTracker . AddProcess ( testP ) ;
try
{
testP . PriorityClass = ProcessPriorityClass . BelowNormal ;
}
catch ( Exception )
2019-12-01 13:57:28 +00:00
{
2020-04-09 10:48:08 +00:00
return false ;
2019-12-01 13:57:28 +00:00
}
2019-12-15 11:58:03 +00:00
try
{
while ( ! testP . HasExited )
{
var line = testP . StandardOutput . ReadLine ( ) ;
if ( line = = null )
break ;
}
2020-04-09 10:48:08 +00:00
}
catch ( Exception )
{
return false ;
}
2019-12-15 11:58:03 +00:00
2020-04-09 10:48:08 +00:00
testP . WaitForExitAndWarn ( TimeSpan . FromSeconds ( 30 ) , $"Can Extract Check {file}" ) ;
2019-12-15 11:58:03 +00:00
return testP . ExitCode = = 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" ) ;
public static bool MightBeArchive ( AbsolutePath path )
2020-01-10 04:47:06 +00:00
{
2020-03-23 12:57:18 +00:00
var ext = path . Extension ;
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
}