using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Wabbajack.Common; using Wabbajack.Common.FileSignatures; using Wabbajack.VirtualFileSystem.SevenZipExtractor; namespace Wabbajack.VirtualFileSystem { public class GatheringExtractor : IArchiveExtractCallback { private ArchiveFile _archive; private Predicate _shouldExtract; private Func> _mapFn; private Dictionary _results; private Dictionary _indexes; private Stream _stream; private Definitions.FileType _sig; public GatheringExtractor(Stream stream, Definitions.FileType sig, Predicate shouldExtract, Func> mapfn) { _shouldExtract = shouldExtract; _mapFn = mapfn; _results = new Dictionary(); _stream = stream; _sig = sig; } public async Task> Extract() { var source = new TaskCompletionSource(); var th = new Thread(() => { try { _archive = ArchiveFile.Open(_stream, _sig).Result; _indexes = _archive.Entries .Where(f => !f.IsFolder) .Select((entry, idx) => ((RelativePath)entry.FileName, (uint)idx)) .Where(t => _shouldExtract(t.Item1)) .ToDictionary(t => t.Item2, t => t.Item1); _archive._archive.Extract(null, 0xFFFFFFFF, 0, this); _archive.Dispose(); source.SetResult(true); } catch (Exception ex) { source.SetException(ex); } }) {Priority = ThreadPriority.BelowNormal, Name = "7Zip Extraction Worker Thread"}; th.Start(); await source.Task; return _results; } public void SetTotal(ulong total) { } public void SetCompleted(ref ulong completeValue) { } public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode) { if (_indexes.ContainsKey(index)) { outStream = new GatheringExtractorStream(this, index); return 0; } outStream = null; return 0; } public void PrepareOperation(AskMode askExtractMode) { } public void SetOperationResult(OperationResult resultEOperationResult) { } private class GatheringExtractorStream : ISequentialOutStream, IOutStream { private GatheringExtractor _extractor; private uint _index; public GatheringExtractorStream(GatheringExtractor extractor, uint index) { _extractor = extractor; _index = index; } public int Write(IntPtr data, uint size, IntPtr processedSize) { unsafe { var result = _extractor._mapFn(_extractor._indexes[_index], new UnmanagedStreamFactory((byte*)data, size)).AsTask().Result; _extractor._results[_extractor._indexes[_index]] = result; if (processedSize != IntPtr.Zero) { Marshal.WriteInt32(processedSize, (int) size); } return 0; } } public void Seek(long offset, uint seekOrigin, IntPtr newPosition) { } public int SetSize(long newSize) { return 0; } } } }