2020-09-04 21:00:37 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Threading.Tasks;
|
2020-09-05 14:01:32 +00:00
|
|
|
|
using Compression.BSA;
|
2020-09-06 03:19:05 +00:00
|
|
|
|
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
2020-09-08 22:15:33 +00:00
|
|
|
|
using OMODFramework;
|
2020-09-06 03:19:05 +00:00
|
|
|
|
using SharpCompress.Archives.SevenZip;
|
|
|
|
|
using SharpCompress.Readers;
|
2020-09-04 21:00:37 +00:00
|
|
|
|
using Wabbajack.Common;
|
|
|
|
|
using Wabbajack.Common.FileSignatures;
|
|
|
|
|
using Wabbajack.VirtualFileSystem.SevenZipExtractor;
|
2020-09-08 22:15:33 +00:00
|
|
|
|
using Utils = Wabbajack.Common.Utils;
|
2020-09-04 21:00:37 +00:00
|
|
|
|
|
|
|
|
|
namespace Wabbajack.VirtualFileSystem
|
|
|
|
|
{
|
|
|
|
|
public static class FileExtractor2
|
|
|
|
|
{
|
|
|
|
|
public static readonly SignatureChecker ArchiveSigs = new SignatureChecker(Definitions.FileType.TES3,
|
|
|
|
|
Definitions.FileType.BSA,
|
|
|
|
|
Definitions.FileType.BA2,
|
|
|
|
|
Definitions.FileType.ZIP,
|
2020-09-05 14:01:32 +00:00
|
|
|
|
//Definitions.FileType.EXE,
|
2020-09-06 03:19:05 +00:00
|
|
|
|
Definitions.FileType.RAR_OLD,
|
|
|
|
|
Definitions.FileType.RAR_NEW,
|
2020-09-04 21:00:37 +00:00
|
|
|
|
Definitions.FileType._7Z);
|
2020-09-08 22:15:33 +00:00
|
|
|
|
|
|
|
|
|
private static Extension OMODExtension = new Extension(".omod");
|
2020-09-12 20:23:03 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// When true, will allow 7z to use multiple threads and cache more data in memory, potentially
|
|
|
|
|
/// using many GB of RAM during extraction but vastly reducing extraction times in the process.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static bool FavorPerfOverRAM { get; set; }
|
2020-09-04 21:00:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task<Dictionary<RelativePath, T>> GatheringExtract<T>(IStreamFactory sFn,
|
|
|
|
|
Predicate<RelativePath> shouldExtract, Func<RelativePath, IStreamFactory, ValueTask<T>> mapfn)
|
|
|
|
|
{
|
2020-09-08 02:22:23 +00:00
|
|
|
|
if (sFn is NativeFileStreamFactory)
|
|
|
|
|
{
|
|
|
|
|
Utils.Log($"Extracting {sFn.Name}");
|
|
|
|
|
}
|
2020-09-04 21:00:37 +00:00
|
|
|
|
await using var archive = await sFn.GetStream();
|
|
|
|
|
var sig = await ArchiveSigs.MatchesAsync(archive);
|
|
|
|
|
archive.Position = 0;
|
|
|
|
|
|
|
|
|
|
switch (sig)
|
|
|
|
|
{
|
2020-09-06 03:19:05 +00:00
|
|
|
|
case Definitions.FileType.RAR_OLD:
|
|
|
|
|
case Definitions.FileType.RAR_NEW:
|
2020-09-08 22:15:33 +00:00
|
|
|
|
case Definitions.FileType._7Z:
|
2020-09-04 21:00:37 +00:00
|
|
|
|
case Definitions.FileType.ZIP:
|
2020-09-08 22:15:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (sFn.Name.FileName.Extension == OMODExtension)
|
|
|
|
|
{
|
|
|
|
|
return await GatheringExtractWithOMOD(archive, shouldExtract, mapfn);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return await GatheringExtractWith7Zip<T>(archive, (Definitions.FileType)sig, shouldExtract,
|
|
|
|
|
mapfn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-05 14:01:32 +00:00
|
|
|
|
case Definitions.FileType.TES3:
|
|
|
|
|
case Definitions.FileType.BSA:
|
|
|
|
|
case Definitions.FileType.BA2:
|
|
|
|
|
return await GatheringExtractWithBSA(sFn, (Definitions.FileType)sig, shouldExtract, mapfn);
|
|
|
|
|
|
|
|
|
|
|
2020-09-04 21:00:37 +00:00
|
|
|
|
default:
|
2020-09-05 14:01:32 +00:00
|
|
|
|
throw new Exception($"Invalid file format {sFn.Name}");
|
2020-09-04 21:00:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-08 22:15:33 +00:00
|
|
|
|
private static async Task<Dictionary<RelativePath,T>> GatheringExtractWithOMOD<T>(Stream archive, Predicate<RelativePath> shouldExtract, Func<RelativePath,IStreamFactory,ValueTask<T>> mapfn)
|
|
|
|
|
{
|
|
|
|
|
var tmpFile = new TempFile();
|
|
|
|
|
await tmpFile.Path.WriteAllAsync(archive);
|
|
|
|
|
var dest = await TempFolder.Create();
|
|
|
|
|
Utils.Log($"Extracting {(string)tmpFile.Path}");
|
|
|
|
|
|
|
|
|
|
Framework.Settings.TempPath = (string)dest.Dir;
|
|
|
|
|
Framework.Settings.CodeProgress = new OMODProgress();
|
|
|
|
|
|
|
|
|
|
var omod = new OMOD((string)tmpFile.Path);
|
|
|
|
|
omod.GetDataFiles();
|
|
|
|
|
omod.GetPlugins();
|
|
|
|
|
|
|
|
|
|
var results = new Dictionary<RelativePath, T>();
|
|
|
|
|
foreach (var file in dest.Dir.EnumerateFiles())
|
|
|
|
|
{
|
|
|
|
|
var path = file.RelativeTo(dest.Dir);
|
|
|
|
|
if (!shouldExtract(path)) continue;
|
|
|
|
|
|
|
|
|
|
var result = await mapfn(path, new NativeFileStreamFactory(file, path));
|
|
|
|
|
results.Add(path, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return results;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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-09-05 14:01:32 +00:00
|
|
|
|
private static async Task<Dictionary<RelativePath,T>> GatheringExtractWithBSA<T>(IStreamFactory sFn, Definitions.FileType sig, Predicate<RelativePath> shouldExtract, Func<RelativePath,IStreamFactory,ValueTask<T>> mapfn)
|
|
|
|
|
{
|
|
|
|
|
var archive = await BSADispatch.OpenRead(sFn, sig);
|
|
|
|
|
var results = new Dictionary<RelativePath, T>();
|
|
|
|
|
foreach (var entry in archive.Files)
|
|
|
|
|
{
|
|
|
|
|
if (!shouldExtract(entry.Path))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
var result = await mapfn(entry.Path, await entry.GetStreamFactory());
|
|
|
|
|
results.Add(entry.Path, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return results;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-04 21:00:37 +00:00
|
|
|
|
private static async Task<Dictionary<RelativePath,T>> GatheringExtractWith7Zip<T>(Stream stream, Definitions.FileType sig, Predicate<RelativePath> shouldExtract, Func<RelativePath,IStreamFactory,ValueTask<T>> mapfn)
|
|
|
|
|
{
|
|
|
|
|
return await new GatheringExtractor<T>(stream, sig, shouldExtract, mapfn).Extract();
|
2020-09-06 03:19:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static async Task ExtractAll(AbsolutePath src, AbsolutePath dest)
|
|
|
|
|
{
|
|
|
|
|
await GatheringExtract(new NativeFileStreamFactory(src), _ => true, async (path, factory) =>
|
|
|
|
|
{
|
|
|
|
|
var abs = path.RelativeTo(dest);
|
|
|
|
|
abs.Parent.CreateDirectory();
|
|
|
|
|
await using var stream = await factory.GetStream();
|
|
|
|
|
await abs.WriteAllAsync(stream);
|
|
|
|
|
return 0;
|
|
|
|
|
});
|
2020-09-04 21:00:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|