mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge pull request #719 from wabbajack-tools/extraction-improvements
Extraction improvements
This commit is contained in:
commit
a82b2d5094
@ -81,7 +81,8 @@ namespace Compression.BSA.Test
|
||||
var folder = _bsaFolder.Combine(game.ToString(), modid.ToString());
|
||||
await folder.DeleteDirectory();
|
||||
folder.CreateDirectory();
|
||||
await FileExtractor.ExtractAll(Queue, filename, folder);
|
||||
await using var files = await FileExtractor.ExtractAll(Queue, filename);
|
||||
await files.MoveAllTo(folder);
|
||||
|
||||
foreach (var bsa in folder.EnumerateFiles().Where(f => Consts.SupportedBSAs.Contains(f.Extension)))
|
||||
{
|
||||
@ -94,7 +95,7 @@ namespace Compression.BSA.Test
|
||||
var tempFile = ((RelativePath)"tmp.bsa").RelativeToEntryPoint();
|
||||
var size = bsa.Size;
|
||||
|
||||
using var a = BSADispatch.OpenRead(bsa);
|
||||
await using var a = BSADispatch.OpenRead(bsa);
|
||||
await a.Files.PMap(Queue, file =>
|
||||
{
|
||||
var absName = _tempDir.Combine(file.Path);
|
||||
@ -111,7 +112,7 @@ namespace Compression.BSA.Test
|
||||
|
||||
TestContext.WriteLine($"Building {bsa}");
|
||||
|
||||
using (var w = ViaJson(a.State).MakeBuilder(size))
|
||||
await using (var w = ViaJson(a.State).MakeBuilder(size))
|
||||
{
|
||||
var streams = await a.Files.PMap(Queue, file =>
|
||||
{
|
||||
@ -125,7 +126,7 @@ namespace Compression.BSA.Test
|
||||
}
|
||||
|
||||
TestContext.WriteLine($"Verifying {bsa}");
|
||||
using var b = BSADispatch.OpenRead(tempFile);
|
||||
await using var b = BSADispatch.OpenRead(tempFile);
|
||||
TestContext.WriteLine($"Performing A/B tests on {bsa}");
|
||||
Assert.Equal(a.State.ToJson(), b.State.ToJson());
|
||||
|
||||
|
@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||
using Wabbajack.Common;
|
||||
@ -34,7 +35,7 @@ namespace Compression.BSA
|
||||
_slab = new DiskSlabAllocator(size);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
_slab.Dispose();
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Common.Serialization.Json;
|
||||
@ -99,7 +100,7 @@ namespace Compression.BSA
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
_stream?.Dispose();
|
||||
_rdr?.Dispose();
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||
using K4os.Compression.LZ4;
|
||||
using K4os.Compression.LZ4.Streams;
|
||||
@ -75,7 +76,7 @@ namespace Compression.BSA
|
||||
|
||||
public bool HasNameBlobs => (_archiveFlags & 0x100) > 0;
|
||||
|
||||
public void Dispose()
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
_slab.Dispose();
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||
using K4os.Compression.LZ4.Streams;
|
||||
using Wabbajack.Common;
|
||||
@ -49,7 +50,7 @@ namespace Compression.BSA
|
||||
Miscellaneous = 0x100
|
||||
}
|
||||
|
||||
public class BSAReader : IDisposable, IBSAReader
|
||||
public class BSAReader : IAsyncDisposable, IBSAReader
|
||||
{
|
||||
internal uint _archiveFlags;
|
||||
internal uint _fileCount;
|
||||
@ -113,7 +114,7 @@ namespace Compression.BSA
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
_stream.Close();
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ using Wabbajack.Common;
|
||||
|
||||
namespace Compression.BSA
|
||||
{
|
||||
public interface IBSAReader : IDisposable
|
||||
public interface IBSAReader : IAsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The files defined by the archive
|
||||
@ -15,7 +15,7 @@ namespace Compression.BSA
|
||||
ArchiveStateObject State { get; }
|
||||
}
|
||||
|
||||
public interface IBSABuilder : IDisposable
|
||||
public interface IBSABuilder : IAsyncDisposable
|
||||
{
|
||||
void AddFile(FileStateObject state, Stream src);
|
||||
void Build(AbsolutePath filename);
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
|
||||
@ -71,7 +72,7 @@ namespace Compression.BSA
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Common.Serialization.Json;
|
||||
|
||||
@ -61,7 +62,7 @@ namespace Compression.BSA
|
||||
_dataOffset = br.BaseStream.Position;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ namespace Wabbajack.BuildServer.Controllers
|
||||
private static AsyncLock _findPatchLock = new AsyncLock();
|
||||
private async Task<(Archive, ArchiveStatus)> TryToFix(SqlService.ValidationData data, Archive archive)
|
||||
{
|
||||
using var _ = await _findPatchLock.Wait();
|
||||
using var _ = await _findPatchLock.WaitAsync();
|
||||
|
||||
var result = await _updater.GetAlternative(archive.Hash.ToHex());
|
||||
return result switch
|
||||
|
@ -63,7 +63,7 @@ namespace Wabbajack.BuildServer.Controllers
|
||||
Utils.Log($"Writing {ms.Length} at position {Offset} in ingest file {Key}");
|
||||
|
||||
long position;
|
||||
using (var _ = await _writeLocks[Key].Wait())
|
||||
using (var _ = await _writeLocks[Key].WaitAsync())
|
||||
{
|
||||
await using var file = _settings.TempPath.Combine(Key).WriteShared();
|
||||
file.Position = Offset;
|
||||
|
@ -32,11 +32,11 @@ namespace Wabbajack.CLI.Verbs
|
||||
.Debounce(TimeSpan.FromSeconds(1))
|
||||
.Subscribe(s => Console.WriteLine($"Downloading {s.ProgressPercent}"));
|
||||
|
||||
new[] {state}
|
||||
await new[] {state}
|
||||
.PMap(queue, async s =>
|
||||
{
|
||||
await s.Download(new Archive(state: null!) {Name = Path.GetFileName(Output)}, (AbsolutePath)Output);
|
||||
}).Wait();
|
||||
});
|
||||
|
||||
File.WriteAllLines(Output + ".meta", state.GetMetaIni());
|
||||
return 0;
|
||||
|
@ -13,7 +13,7 @@ namespace Wabbajack.Common.Test
|
||||
bool firstRun = false;
|
||||
var first = Task.Run(async () =>
|
||||
{
|
||||
using (await asyncLock.Wait())
|
||||
using (await asyncLock.WaitAsync())
|
||||
{
|
||||
await Task.Delay(500);
|
||||
firstRun = true;
|
||||
@ -22,7 +22,7 @@ namespace Wabbajack.Common.Test
|
||||
var second = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(200);
|
||||
using (await asyncLock.Wait())
|
||||
using (await asyncLock.WaitAsync())
|
||||
{
|
||||
Assert.True(firstRun);
|
||||
}
|
||||
@ -41,7 +41,7 @@ namespace Wabbajack.Common.Test
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
using (await asyncLock.Wait())
|
||||
using (await asyncLock.WaitAsync())
|
||||
{
|
||||
await Task.Delay(500);
|
||||
firstRun = true;
|
||||
@ -55,7 +55,7 @@ namespace Wabbajack.Common.Test
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(200);
|
||||
using (await asyncLock.Wait())
|
||||
using (await asyncLock.WaitAsync())
|
||||
{
|
||||
Assert.True(firstRun);
|
||||
secondRun = true;
|
||||
|
@ -145,6 +145,17 @@ namespace Wabbajack.Common
|
||||
var value = xxHashFactory.Instance.Create(config).ComputeHash(f);
|
||||
return Hash.FromULong(BitConverter.ToUInt64(value.Hash));
|
||||
}
|
||||
|
||||
public static Hash xxHash(this Stream stream)
|
||||
{
|
||||
var hash = new xxHashConfig();
|
||||
hash.HashSizeInBits = 64;
|
||||
hash.Seed = 0x42;
|
||||
var config = new xxHashConfig {HashSizeInBits = 64};
|
||||
using var f = new StatusFileStream(stream, $"Hashing memory stream");
|
||||
var value = xxHashFactory.Instance.Create(config).ComputeHash(f);
|
||||
return Hash.FromULong(BitConverter.ToUInt64(value.Hash));
|
||||
}
|
||||
public static Hash FileHashCached(this AbsolutePath file, bool nullOnIoError = false)
|
||||
{
|
||||
if (TryGetHashCache(file, out var foundHash)) return foundHash;
|
||||
|
@ -28,7 +28,7 @@ namespace Wabbajack.Common
|
||||
return sigStream;
|
||||
}
|
||||
|
||||
private static void CreateSignature(FileStream oldData, FileStream sigStream)
|
||||
private static void CreateSignature(Stream oldData, FileStream sigStream)
|
||||
{
|
||||
Utils.Status("Creating Patch Signature");
|
||||
var signatureBuilder = new SignatureBuilder();
|
||||
@ -36,7 +36,7 @@ namespace Wabbajack.Common
|
||||
sigStream.Position = 0;
|
||||
}
|
||||
|
||||
public static void Create(FileStream oldData, FileStream newData, FileStream signature, FileStream output)
|
||||
public static void Create(Stream oldData, FileStream newData, FileStream signature, FileStream output)
|
||||
{
|
||||
CreateSignature(oldData, signature);
|
||||
var db = new DeltaBuilder {ProgressReporter = reporter};
|
||||
|
@ -9,7 +9,7 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using Directory = System.IO.Directory;
|
||||
using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
using FileInfo = Alphaleonis.Win32.Filesystem.FileInfo;
|
||||
using Path = Alphaleonis.Win32.Filesystem.Path;
|
||||
|
@ -12,7 +12,7 @@ namespace Wabbajack.Common
|
||||
{
|
||||
private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
|
||||
|
||||
public async Task<IDisposable> Wait()
|
||||
public async Task<IDisposable> WaitAsync()
|
||||
{
|
||||
await _lock.WaitAsync();
|
||||
return Disposable.Create(() => _lock.Release());
|
||||
|
@ -14,7 +14,7 @@ namespace Wabbajack.Common
|
||||
|
||||
public TempFolder(bool deleteAfter = true)
|
||||
{
|
||||
Dir = new AbsolutePath(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()));
|
||||
Dir = Path.Combine("tmp_files", Guid.NewGuid().ToString()).RelativeTo(AbsolutePath.EntryPoint);
|
||||
if (!Dir.Exists)
|
||||
Dir.CreateDirectory();
|
||||
DeleteAfter = deleteAfter;
|
||||
|
@ -427,6 +427,13 @@ namespace Wabbajack.Common
|
||||
await ins.CopyToAsync(ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
public static async Task<string> ReadAllTextAsync(this Stream ins)
|
||||
{
|
||||
await using var ms = new MemoryStream();
|
||||
await ins.CopyToAsync(ms);
|
||||
return Encoding.UTF8.GetString(ms.ToArray());
|
||||
}
|
||||
|
||||
public static async Task<TR[]> PMap<TI, TR>(this IEnumerable<TI> coll, WorkQueue queue, StatusUpdateTracker updateTracker,
|
||||
Func<TI, TR> f)
|
||||
@ -727,7 +734,7 @@ namespace Wabbajack.Common
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task CreatePatch(FileStream srcStream, Hash srcHash, FileStream destStream, Hash destHash,
|
||||
public static async Task CreatePatch(Stream srcStream, Hash srcHash, FileStream destStream, Hash destHash,
|
||||
FileStream patchStream)
|
||||
{
|
||||
await using var sigFile = new TempStream();
|
||||
@ -967,7 +974,7 @@ namespace Wabbajack.Common
|
||||
Status($"Deleting: {p.Line}");
|
||||
});
|
||||
|
||||
process.Start().Wait();
|
||||
await process.Start();
|
||||
await result;
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ namespace Wabbajack.Common
|
||||
|
||||
private async Task AddNewThreadsIfNeeded(int desired)
|
||||
{
|
||||
using (await _lock.Wait())
|
||||
using (await _lock.WaitAsync())
|
||||
{
|
||||
DesiredNumWorkers = desired;
|
||||
while (DesiredNumWorkers > _tasks.Count)
|
||||
@ -141,7 +141,7 @@ namespace Wabbajack.Common
|
||||
if (DesiredNumWorkers >= _tasks.Count) continue;
|
||||
|
||||
// Noticed that we may need to shut down, lock and check again
|
||||
using (await _lock.Wait())
|
||||
using (await _lock.WaitAsync())
|
||||
{
|
||||
// Check if another thread shut down before this one and got us back to the desired amount already
|
||||
if (DesiredNumWorkers >= _tasks.Count) continue;
|
||||
|
@ -172,7 +172,8 @@ namespace Wabbajack.Lib
|
||||
throw new ArgumentNullException("FromFile was null");
|
||||
}
|
||||
var firstDest = OutputFolder.Combine(group.First().To);
|
||||
await CopyFile(group.Key.StagedPath, firstDest, true);
|
||||
|
||||
await group.Key.StagedFile.MoveTo(firstDest);
|
||||
|
||||
foreach (var copy in group.Skip(1))
|
||||
{
|
||||
|
@ -77,7 +77,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
}
|
||||
|
||||
CreateBSA directive;
|
||||
using (var bsa = BSADispatch.OpenRead(source.AbsolutePath))
|
||||
await using (var bsa = BSADispatch.OpenRead(source.AbsolutePath))
|
||||
{
|
||||
directive = new CreateBSA(
|
||||
state: bsa.State,
|
||||
|
@ -21,7 +21,15 @@ namespace Wabbajack.Lib
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public AbsolutePath AbsolutePath => File.StagedPath;
|
||||
public AbsolutePath AbsolutePath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!File.IsNative)
|
||||
throw new InvalidDataException("Can't get the absolute path of a non-native file");
|
||||
return File.FullPath.Base;
|
||||
}
|
||||
}
|
||||
|
||||
public VirtualFile File { get; }
|
||||
|
||||
|
@ -1,18 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Wabbajack.Lib.Downloaders
|
||||
{
|
||||
public class AFKModsDownloader : AbstractIPS4Downloader<AFKModsDownloader, AFKModsDownloader.State>
|
||||
{
|
||||
#region INeedsDownload
|
||||
public override string SiteName => "AFK Mods";
|
||||
public override Uri SiteURL => new Uri("https://www.afkmods.com/index.php?");
|
||||
public override Uri IconUri => new Uri("https://www.afkmods.com/favicon.ico");
|
||||
#endregion
|
||||
|
||||
public AFKModsDownloader() : base(new Uri("https://www.afkmods.com/index.php?/login/"),
|
||||
"afkmods", "www.afkmods.com"){}
|
||||
|
||||
public class State : State<AFKModsDownloader>{}
|
||||
}
|
||||
}
|
@ -37,7 +37,6 @@ namespace Wabbajack.Lib.Downloaders
|
||||
typeof(SteamWorkshopDownloader.State),
|
||||
typeof(VectorPlexusDownloader.State),
|
||||
typeof(DeadlyStreamDownloader.State),
|
||||
typeof(AFKModsDownloader.State),
|
||||
typeof(TESAllianceDownloader.State),
|
||||
typeof(BethesdaNetDownloader.State),
|
||||
typeof(YouTubeDownloader.State)
|
||||
|
@ -21,6 +21,9 @@ namespace Wabbajack.Lib.Downloaders
|
||||
private readonly string _encryptedKeyName;
|
||||
private readonly string _cookieDomain;
|
||||
private readonly string _cookieName;
|
||||
|
||||
private bool _isPrepared;
|
||||
|
||||
// ToDo
|
||||
// Remove null assignment. Either add nullability to type, or figure way to prepare it safely
|
||||
public Common.Http.Client AuthedClient { get; private set; } = null!;
|
||||
@ -100,7 +103,9 @@ namespace Wabbajack.Lib.Downloaders
|
||||
|
||||
public async Task Prepare()
|
||||
{
|
||||
if (_isPrepared) return;
|
||||
AuthedClient = (await GetAuthedClient()) ?? throw new NotLoggedInError(this);
|
||||
_isPrepared = true;
|
||||
}
|
||||
|
||||
public class NotLoggedInError : Exception
|
||||
|
@ -29,6 +29,7 @@ namespace Wabbajack.Lib.Downloaders
|
||||
{
|
||||
public class BethesdaNetDownloader : IUrlDownloader, INeedsLogin
|
||||
{
|
||||
private bool _isPrepared;
|
||||
public const string DataName = "bethesda-net-data";
|
||||
|
||||
public ReactiveCommand<Unit, Unit> TriggerLogin { get; }
|
||||
@ -70,8 +71,14 @@ namespace Wabbajack.Lib.Downloaders
|
||||
|
||||
public async Task Prepare()
|
||||
{
|
||||
if (Utils.HaveEncryptedJson(DataName)) return;
|
||||
if (_isPrepared) return;
|
||||
if (Utils.HaveEncryptedJson(DataName))
|
||||
{
|
||||
_isPrepared = true;
|
||||
return;
|
||||
}
|
||||
await Utils.Log(new RequestBethesdaNetLogin()).Task;
|
||||
_isPrepared = true;
|
||||
}
|
||||
|
||||
public static async Task<BethesdaNetData?> Login(Game game)
|
||||
|
@ -23,7 +23,6 @@ namespace Wabbajack.Lib.Downloaders
|
||||
new VectorPlexusDownloader(),
|
||||
new DeadlyStreamDownloader(),
|
||||
new BethesdaNetDownloader(),
|
||||
new AFKModsDownloader(),
|
||||
new TESAllianceDownloader(),
|
||||
new YouTubeDownloader(),
|
||||
new HTTPDownloader(),
|
||||
|
@ -100,7 +100,7 @@ namespace Wabbajack.Lib.Downloaders
|
||||
{
|
||||
if (!_prepared)
|
||||
{
|
||||
using var _ = await _lock.Wait();
|
||||
using var _ = await _lock.WaitAsync();
|
||||
// Could have become prepared while we waited for the lock
|
||||
if (!_prepared)
|
||||
{
|
||||
|
@ -459,13 +459,13 @@ namespace Wabbajack.Lib
|
||||
await using var srcStream = srcFile.OpenRead();
|
||||
await using var outputStream = IncludeFile(out var id);
|
||||
entry.PatchID = id;
|
||||
await using var destStream = LoadDataForTo(entry.To, absolutePaths);
|
||||
await using var destStream = await LoadDataForTo(entry.To, absolutePaths);
|
||||
await Utils.CreatePatch(srcStream, srcFile.Hash, destStream, entry.Hash, outputStream);
|
||||
Info($"Patch size {outputStream.Length} for {entry.To}");
|
||||
});
|
||||
}
|
||||
|
||||
private FileStream LoadDataForTo(RelativePath to, Dictionary<RelativePath, AbsolutePath> absolutePaths)
|
||||
private async Task<FileStream> LoadDataForTo(RelativePath to, Dictionary<RelativePath, AbsolutePath> absolutePaths)
|
||||
{
|
||||
if (absolutePaths.TryGetValue(to, out var absolute))
|
||||
return absolute.OpenRead();
|
||||
@ -475,7 +475,7 @@ namespace Wabbajack.Lib
|
||||
var bsaId = (RelativePath)((string)to).Split('\\')[1];
|
||||
var bsa = InstallDirectives.OfType<CreateBSA>().First(b => b.TempID == bsaId);
|
||||
|
||||
using var a = BSADispatch.OpenRead(MO2Folder.Combine(bsa.To));
|
||||
await using var a = BSADispatch.OpenRead(MO2Folder.Combine(bsa.To));
|
||||
var find = (RelativePath)Path.Combine(((string)to).Split('\\').Skip(2).ToArray());
|
||||
var file = a.Files.First(e => e.Path == find);
|
||||
var returnStream = new TempStream();
|
||||
|
@ -259,7 +259,7 @@ namespace Wabbajack.Lib
|
||||
|
||||
var bsaSize = bsa.FileStates.Select(state => sourceDir.Combine(state.Path).Size).Sum();
|
||||
|
||||
using (var a = bsa.State.MakeBuilder(bsaSize))
|
||||
await using (var a = bsa.State.MakeBuilder(bsaSize))
|
||||
{
|
||||
var streams = await bsa.FileStates.PMap(Queue, state =>
|
||||
{
|
||||
|
@ -52,7 +52,7 @@ namespace Wabbajack.Lib.NexusApi
|
||||
private static AsyncLock _getAPIKeyLock = new AsyncLock();
|
||||
private static async Task<string> GetApiKey()
|
||||
{
|
||||
using (await _getAPIKeyLock.Wait())
|
||||
using (await _getAPIKeyLock.WaitAsync())
|
||||
{
|
||||
// Clean up old location
|
||||
if (File.Exists(API_KEY_CACHE_FILE))
|
||||
@ -114,7 +114,7 @@ namespace Wabbajack.Lib.NexusApi
|
||||
key = await browser.EvaluateJavaScript(
|
||||
"document.querySelector(\"input[value=wabbajack]\").parentElement.parentElement.querySelector(\"textarea.application-key\").innerHTML");
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
@ -46,7 +46,8 @@ namespace Wabbajack.Test
|
||||
[Fact]
|
||||
public async Task TestAllPrepares()
|
||||
{
|
||||
await Task.WhenAll(DownloadDispatcher.Downloaders.Select(d => d.Prepare()));
|
||||
foreach (var downloader in DownloadDispatcher.Downloaders)
|
||||
await downloader.Prepare();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -105,8 +105,8 @@ namespace Wabbajack.Test
|
||||
|
||||
await src.CopyToAsync(utils.DownloadsFolder.Combine(filename));
|
||||
|
||||
await FileExtractor.ExtractAll(Queue, src,
|
||||
modName == null ? utils.MO2Folder : utils.ModsFolder.Combine(modName));
|
||||
await using var dest = await FileExtractor.ExtractAll(Queue, src);
|
||||
await dest.MoveAllTo(modName == null ? utils.MO2Folder : utils.ModsFolder.Combine(modName));
|
||||
}
|
||||
|
||||
private async Task<(AbsolutePath Download, AbsolutePath ModFolder)> DownloadAndInstall(Game game, int modId, string modName)
|
||||
@ -140,7 +140,8 @@ namespace Wabbajack.Test
|
||||
await src.CopyToAsync(dest);
|
||||
|
||||
var modFolder = utils.ModsFolder.Combine(modName);
|
||||
await FileExtractor.ExtractAll(Queue, src, modFolder);
|
||||
await using var files = await FileExtractor.ExtractAll(Queue, src);
|
||||
await files.MoveAllTo(modFolder);
|
||||
|
||||
await dest.WithExtension(Consts.MetaFileExtension).WriteAllTextAsync(ini);
|
||||
return (dest, modFolder);
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
using Xunit;
|
||||
@ -141,7 +142,10 @@ namespace Wabbajack.VirtualFileSystem.Test
|
||||
var file = context.Index.ByFullPath[res];
|
||||
|
||||
var cleanup = await context.Stage(new List<VirtualFile> {file});
|
||||
Assert.Equal("This is a test", await file.StagedPath.ReadAllTextAsync());
|
||||
|
||||
await using var stream = file.StagedFile.OpenRead();
|
||||
|
||||
Assert.Equal("This is a test", await stream.ReadAllTextAsync());
|
||||
|
||||
await cleanup();
|
||||
}
|
||||
@ -164,7 +168,11 @@ namespace Wabbajack.VirtualFileSystem.Test
|
||||
var cleanup = await context.Stage(files);
|
||||
|
||||
foreach (var file in files)
|
||||
Assert.Equal("This is a test", await file.StagedPath.ReadAllTextAsync());
|
||||
{
|
||||
await using var stream = file.StagedFile.OpenRead();
|
||||
|
||||
Assert.Equal("This is a test", await stream.ReadAllTextAsync());
|
||||
}
|
||||
|
||||
await cleanup();
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ namespace Wabbajack.VirtualFileSystem
|
||||
return found;
|
||||
}
|
||||
|
||||
return await VirtualFile.Analyze(this, null, f, f, 0);
|
||||
return await VirtualFile.Analyze(this, null, new ExtractedDiskFile(f), f, 0);
|
||||
});
|
||||
|
||||
var newIndex = await IndexRoot.Empty.Integrate(filtered.Concat(allFiles).ToList());
|
||||
@ -90,7 +90,7 @@ namespace Wabbajack.VirtualFileSystem
|
||||
public async Task<IndexRoot> AddRoots(List<AbsolutePath> roots)
|
||||
{
|
||||
await _cleanupTask;
|
||||
var native = Index.AllFiles.Where(file => file.IsNative).ToDictionary(file => file.StagedPath);
|
||||
var native = Index.AllFiles.Where(file => file.IsNative).ToDictionary(file => file.FullPath.Base);
|
||||
|
||||
var filtered = Index.AllFiles.Where(file => ((AbsolutePath)file.Name).Exists).ToList();
|
||||
|
||||
@ -106,7 +106,7 @@ namespace Wabbajack.VirtualFileSystem
|
||||
return found;
|
||||
}
|
||||
|
||||
return await VirtualFile.Analyze(this, null, f, f, 0);
|
||||
return await VirtualFile.Analyze(this, null, new ExtractedDiskFile(f), f, 0);
|
||||
});
|
||||
|
||||
var newIndex = await IndexRoot.Empty.Integrate(filtered.Concat(allFiles).ToList());
|
||||
@ -210,22 +210,22 @@ namespace Wabbajack.VirtualFileSystem
|
||||
.OrderBy(f => f.Key?.NestingFactor ?? 0)
|
||||
.ToList();
|
||||
|
||||
var paths = new List<AbsolutePath>();
|
||||
var paths = new List<IAsyncDisposable>();
|
||||
|
||||
foreach (var group in grouped)
|
||||
{
|
||||
var tmpPath = ((RelativePath)Guid.NewGuid().ToString()).RelativeTo(StagingFolder);
|
||||
await FileExtractor.ExtractAll(Queue, group.Key.StagedPath, tmpPath);
|
||||
paths.Add(tmpPath);
|
||||
var only = group.Select(f => f.RelativeName);
|
||||
var extracted = await group.Key.StagedFile.ExtractAll(Queue, only);
|
||||
paths.Add(extracted);
|
||||
foreach (var file in group)
|
||||
file.StagedPath = file.RelativeName.RelativeTo(tmpPath);
|
||||
file.StagedFile = extracted[file.RelativeName];
|
||||
}
|
||||
|
||||
return async () =>
|
||||
{
|
||||
foreach (var p in paths)
|
||||
{
|
||||
await p.DeleteDirectory();
|
||||
await p.DisposeAsync();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
51
Wabbajack.VirtualFileSystem/ExtractedBSAFile.cs
Normal file
51
Wabbajack.VirtualFileSystem/ExtractedBSAFile.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Compression.BSA;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.VirtualFileSystem
|
||||
{
|
||||
public class ExtractedBSAFile : IExtractedFile
|
||||
{
|
||||
private readonly IFile _file;
|
||||
public ExtractedBSAFile(IFile file)
|
||||
{
|
||||
_file = file;
|
||||
}
|
||||
|
||||
public RelativePath Path => _file.Path;
|
||||
|
||||
public async Task<Hash> HashAsync()
|
||||
{
|
||||
await using var stream = OpenRead();
|
||||
return stream.xxHash();
|
||||
}
|
||||
public DateTime LastModifiedUtc => DateTime.UtcNow;
|
||||
public long Size => _file.Size;
|
||||
public Stream OpenRead()
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
_file.CopyDataTo(ms);
|
||||
ms.Position = 0;
|
||||
return ms;
|
||||
}
|
||||
|
||||
public async Task<bool> CanExtract()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task<ExtractedFiles> ExtractAll(WorkQueue queue, IEnumerable<RelativePath> OnlyFiles)
|
||||
{
|
||||
throw new Exception("BSAs can't contain archives");
|
||||
}
|
||||
|
||||
public async Task MoveTo(AbsolutePath path)
|
||||
{
|
||||
await using var fs = path.Create();
|
||||
_file.CopyDataTo(fs);
|
||||
}
|
||||
}
|
||||
}
|
47
Wabbajack.VirtualFileSystem/ExtractedDiskFile.cs
Normal file
47
Wabbajack.VirtualFileSystem/ExtractedDiskFile.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.VirtualFileSystem
|
||||
{
|
||||
public class ExtractedDiskFile : IExtractedFile
|
||||
{
|
||||
private AbsolutePath _path;
|
||||
|
||||
public ExtractedDiskFile(AbsolutePath path)
|
||||
{
|
||||
if (path == default)
|
||||
throw new InvalidDataException("Path cannot be empty");
|
||||
_path = path;
|
||||
}
|
||||
|
||||
public async Task<Hash> HashAsync()
|
||||
{
|
||||
return await _path.FileHashAsync();
|
||||
}
|
||||
public DateTime LastModifiedUtc => _path.LastModifiedUtc;
|
||||
public long Size => _path.Size;
|
||||
public Stream OpenRead()
|
||||
{
|
||||
return _path.OpenRead();
|
||||
}
|
||||
|
||||
public async Task<bool> CanExtract()
|
||||
{
|
||||
return await FileExtractor.CanExtract(_path);
|
||||
}
|
||||
|
||||
public Task<ExtractedFiles> ExtractAll(WorkQueue queue, IEnumerable<RelativePath> onlyFiles)
|
||||
{
|
||||
return FileExtractor.ExtractAll(queue, _path, onlyFiles);
|
||||
}
|
||||
|
||||
public async Task MoveTo(AbsolutePath path)
|
||||
{
|
||||
await _path.MoveToAsync(path, true);
|
||||
_path = path;
|
||||
}
|
||||
}
|
||||
}
|
64
Wabbajack.VirtualFileSystem/ExtractedFiles.cs
Normal file
64
Wabbajack.VirtualFileSystem/ExtractedFiles.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.VirtualFileSystem
|
||||
{
|
||||
public class ExtractedFiles : IAsyncDisposable, IEnumerable<KeyValuePair<RelativePath, IExtractedFile>>
|
||||
{
|
||||
private Dictionary<RelativePath, IExtractedFile> _files;
|
||||
private IAsyncDisposable _disposable;
|
||||
private AbsolutePath _tempFolder;
|
||||
|
||||
public ExtractedFiles(Dictionary<RelativePath, IExtractedFile> files, IAsyncDisposable disposeOther)
|
||||
{
|
||||
_files = files;
|
||||
_disposable = disposeOther;
|
||||
}
|
||||
|
||||
public ExtractedFiles(TempFolder tempPath)
|
||||
{
|
||||
_files = tempPath.Dir.EnumerateFiles().ToDictionary(f => f.RelativeTo(tempPath.Dir),
|
||||
f => (IExtractedFile)new ExtractedDiskFile(f));
|
||||
_disposable = tempPath;
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposable != null)
|
||||
{
|
||||
await _disposable.DisposeAsync();
|
||||
_disposable = null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsKey(RelativePath key)
|
||||
{
|
||||
return _files.ContainsKey(key);
|
||||
}
|
||||
|
||||
public int Count => _files.Count;
|
||||
|
||||
public IExtractedFile this[RelativePath key] => _files[key];
|
||||
public IEnumerator<KeyValuePair<RelativePath, IExtractedFile>> GetEnumerator()
|
||||
{
|
||||
return _files.GetEnumerator();
|
||||
}
|
||||
|
||||
public async Task MoveAllTo(AbsolutePath folder)
|
||||
{
|
||||
foreach (var (key, value) in this)
|
||||
{
|
||||
await value.MoveTo(key.RelativeTo(folder));
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
@ -16,42 +17,43 @@ namespace Wabbajack.VirtualFileSystem
|
||||
{
|
||||
public class FileExtractor
|
||||
{
|
||||
|
||||
public static async Task ExtractAll(WorkQueue queue, AbsolutePath source, AbsolutePath dest)
|
||||
|
||||
public static async Task<ExtractedFiles> ExtractAll(WorkQueue queue, AbsolutePath source, IEnumerable<RelativePath> OnlyFiles = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Consts.SupportedBSAs.Contains(source.Extension))
|
||||
await ExtractAllWithBSA(queue, source, dest);
|
||||
return await ExtractAllWithBSA(queue, source);
|
||||
else if (source.Extension == Consts.OMOD)
|
||||
ExtractAllWithOMOD(source, dest);
|
||||
return ExtractAllWithOMOD(source);
|
||||
else if (source.Extension == Consts.EXE)
|
||||
await ExtractAllExe(source, dest);
|
||||
return await ExtractAllExe(source);
|
||||
else
|
||||
await ExtractAllWith7Zip(source, dest);
|
||||
return await ExtractAllWith7Zip(source, OnlyFiles);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utils.ErrorThrow(ex, $"Error while extracting {source}");
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ExtractAllExe(AbsolutePath source, AbsolutePath dest)
|
||||
private static async Task<ExtractedFiles> ExtractAllExe(AbsolutePath source)
|
||||
{
|
||||
var isArchive = await TestWith7z(source);
|
||||
|
||||
if (isArchive)
|
||||
{
|
||||
await ExtractAllWith7Zip(source, dest);
|
||||
return;
|
||||
return await ExtractAllWith7Zip(source, null);
|
||||
}
|
||||
|
||||
var dest = new TempFolder();
|
||||
Utils.Log($"Extracting {(string)source.FileName}");
|
||||
|
||||
var process = new ProcessHelper
|
||||
{
|
||||
Path = @"Extractors\innounp.exe".RelativeTo(AbsolutePath.EntryPoint),
|
||||
Arguments = new object[] {"-x", "-y", "-b", $"-d\"{dest}\"", source}
|
||||
Arguments = new object[] {"-x", "-y", "-b", $"-d\"{dest.Dir}\"", source}
|
||||
};
|
||||
|
||||
|
||||
@ -69,7 +71,8 @@ namespace Wabbajack.VirtualFileSystem
|
||||
Utils.Status($"Extracting {source.FileName} - {line.Trim()}", Percent.FactoryPutInRange(percentInt / 100d));
|
||||
});
|
||||
await process.Start();
|
||||
}
|
||||
return new ExtractedFiles(dest);
|
||||
}
|
||||
|
||||
private class OMODProgress : ICodeProgress
|
||||
{
|
||||
@ -91,55 +94,70 @@ namespace Wabbajack.VirtualFileSystem
|
||||
}
|
||||
}
|
||||
|
||||
private static void ExtractAllWithOMOD(AbsolutePath source, AbsolutePath dest)
|
||||
private static ExtractedFiles ExtractAllWithOMOD(AbsolutePath source)
|
||||
{
|
||||
var dest = new TempFolder();
|
||||
Utils.Log($"Extracting {(string)source.FileName}");
|
||||
|
||||
Framework.Settings.TempPath = (string)dest;
|
||||
Framework.Settings.TempPath = (string)dest.Dir;
|
||||
Framework.Settings.CodeProgress = new OMODProgress();
|
||||
|
||||
var omod = new OMOD((string)source);
|
||||
omod.GetDataFiles();
|
||||
omod.GetPlugins();
|
||||
|
||||
return new ExtractedFiles(dest);
|
||||
}
|
||||
|
||||
|
||||
private static async Task ExtractAllWithBSA(WorkQueue queue, AbsolutePath source, AbsolutePath dest)
|
||||
private static async Task<ExtractedFiles> ExtractAllWithBSA(WorkQueue queue, AbsolutePath source)
|
||||
{
|
||||
try
|
||||
{
|
||||
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);
|
||||
});
|
||||
await using var arch = BSADispatch.OpenRead(source);
|
||||
var files = arch.Files.ToDictionary(f => f.Path, f => (IExtractedFile)new ExtractedBSAFile(f));
|
||||
return new ExtractedFiles(files, arch);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utils.ErrorThrow(ex, $"While Extracting {source}");
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ExtractAllWith7Zip(AbsolutePath source, AbsolutePath dest)
|
||||
private static async Task<ExtractedFiles> ExtractAllWith7Zip(AbsolutePath source, IEnumerable<RelativePath> onlyFiles)
|
||||
{
|
||||
TempFile tmpFile = null;
|
||||
var dest = new TempFolder();
|
||||
Utils.Log(new GenericInfo($"Extracting {(string)source.FileName}", $"The contents of {(string)source.FileName} are being extracted to {(string)source.FileName} using 7zip.exe"));
|
||||
|
||||
|
||||
var process = new ProcessHelper
|
||||
{
|
||||
Path = @"Extractors\7z.exe".RelativeTo(AbsolutePath.EntryPoint),
|
||||
Arguments = new object[] {"x", "-bsp1", "-y", $"-o\"{dest}\"", source, "-mmt=off"}
|
||||
|
||||
};
|
||||
|
||||
if (onlyFiles != null)
|
||||
{
|
||||
//It's stupid that we have to do this, but 7zip's file pattern matching isn't very fuzzy
|
||||
IEnumerable<string> AllVariants(string input)
|
||||
{
|
||||
yield return input;
|
||||
yield return "\\" + input;
|
||||
}
|
||||
|
||||
tmpFile = new TempFile();
|
||||
await tmpFile.Path.WriteAllLinesAsync(onlyFiles.SelectMany(f => AllVariants((string)f)).ToArray());
|
||||
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"};
|
||||
}
|
||||
|
||||
|
||||
var result = process.Output.Where(d => d.Type == ProcessHelper.StreamType.Output)
|
||||
.ForEachAsync(p =>
|
||||
@ -159,12 +177,15 @@ namespace Wabbajack.VirtualFileSystem
|
||||
|
||||
if (exitCode != 0)
|
||||
{
|
||||
Utils.Error(new _7zipReturnError(exitCode, source, dest, ""));
|
||||
Utils.Error(new _7zipReturnError(exitCode, source, dest.Dir, ""));
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.Status($"Extracting {source.FileName} - done", Percent.One, alsoLog: true);
|
||||
}
|
||||
|
||||
tmpFile?.Dispose();
|
||||
return new ExtractedFiles(dest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -205,9 +226,8 @@ namespace Wabbajack.VirtualFileSystem
|
||||
|
||||
private static Extension _exeExtension = new Extension(".exe");
|
||||
|
||||
public static bool MightBeArchive(AbsolutePath path)
|
||||
public static bool MightBeArchive(Extension ext)
|
||||
{
|
||||
var ext = path.Extension;
|
||||
return ext == _exeExtension || Consts.SupportedArchives.Contains(ext) || Consts.SupportedBSAs.Contains(ext);
|
||||
}
|
||||
}
|
||||
|
24
Wabbajack.VirtualFileSystem/IExtractedFile.cs
Normal file
24
Wabbajack.VirtualFileSystem/IExtractedFile.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.VirtualFileSystem
|
||||
{
|
||||
public interface IExtractedFile
|
||||
{
|
||||
public Task<Hash> HashAsync();
|
||||
public DateTime LastModifiedUtc { get; }
|
||||
public long Size { get; }
|
||||
|
||||
public Stream OpenRead();
|
||||
|
||||
public Task<bool> CanExtract();
|
||||
|
||||
public Task<ExtractedFiles> ExtractAll(WorkQueue queue, IEnumerable<RelativePath> Only = null);
|
||||
|
||||
public Task MoveTo(AbsolutePath path);
|
||||
|
||||
}
|
||||
}
|
@ -38,22 +38,21 @@ namespace Wabbajack.VirtualFileSystem
|
||||
|
||||
public Context Context { get; set; }
|
||||
|
||||
public AbsolutePath StagedPath
|
||||
private IExtractedFile _stagedFile = null;
|
||||
public IExtractedFile StagedFile
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsNative)
|
||||
return (AbsolutePath)Name;
|
||||
if (_stagedPath == null)
|
||||
throw new UnstagedFileException(FullPath);
|
||||
return _stagedPath;
|
||||
if (IsNative) return new ExtractedDiskFile(AbsoluteName);
|
||||
if (_stagedFile == null)
|
||||
throw new InvalidDataException("File is unstaged");
|
||||
return _stagedFile;
|
||||
}
|
||||
internal set
|
||||
set
|
||||
{
|
||||
if (IsNative)
|
||||
throw new CannotStageNativeFile("Cannot stage a native file");
|
||||
_stagedPath = value;
|
||||
_stagedFile = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -129,18 +128,18 @@ namespace Wabbajack.VirtualFileSystem
|
||||
itm.ThisAndAllChildrenReduced(fn);
|
||||
}
|
||||
|
||||
public static async Task<VirtualFile> Analyze(Context context, VirtualFile parent, AbsolutePath absPath,
|
||||
public static async Task<VirtualFile> Analyze(Context context, VirtualFile parent, IExtractedFile extractedFile,
|
||||
IPath relPath, int depth = 0)
|
||||
{
|
||||
var hash = absPath.FileHash();
|
||||
var hash = await extractedFile.HashAsync();
|
||||
|
||||
if (!context.UseExtendedHashes && FileExtractor.MightBeArchive(absPath))
|
||||
if (!context.UseExtendedHashes && FileExtractor.MightBeArchive(relPath.FileName.Extension))
|
||||
{
|
||||
var result = await TryGetContentsFromServer(hash);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
Utils.Log($"Downloaded VFS data for {(string)absPath}");
|
||||
Utils.Log($"Downloaded VFS data for {relPath.FileName}");
|
||||
|
||||
VirtualFile Convert(IndexedVirtualFile file, IPath path, VirtualFile vparent)
|
||||
{
|
||||
@ -150,7 +149,7 @@ namespace Wabbajack.VirtualFileSystem
|
||||
Name = path,
|
||||
Parent = vparent,
|
||||
Size = file.Size,
|
||||
LastModified = absPath.LastModifiedUtc.AsUnixTime(),
|
||||
LastModified = extractedFile.LastModifiedUtc.AsUnixTime(),
|
||||
LastAnalyzed = DateTime.Now.AsUnixTime(),
|
||||
Hash = file.Hash
|
||||
};
|
||||
@ -169,8 +168,8 @@ namespace Wabbajack.VirtualFileSystem
|
||||
Context = context,
|
||||
Name = relPath,
|
||||
Parent = parent,
|
||||
Size = absPath.Size,
|
||||
LastModified = absPath.LastModifiedUtc.AsUnixTime(),
|
||||
Size = extractedFile.Size,
|
||||
LastModified = extractedFile.LastModifiedUtc.AsUnixTime(),
|
||||
LastAnalyzed = DateTime.Now.AsUnixTime(),
|
||||
Hash = hash
|
||||
};
|
||||
@ -178,19 +177,17 @@ namespace Wabbajack.VirtualFileSystem
|
||||
self.FillFullPath(depth);
|
||||
|
||||
if (context.UseExtendedHashes)
|
||||
self.ExtendedHashes = ExtendedHashes.FromFile(absPath);
|
||||
self.ExtendedHashes = ExtendedHashes.FromFile(extractedFile);
|
||||
|
||||
if (await FileExtractor.CanExtract(absPath))
|
||||
{
|
||||
await using var tempFolder = Context.GetTemporaryFolder();
|
||||
await FileExtractor.ExtractAll(context.Queue, absPath, tempFolder.FullName);
|
||||
if (!await extractedFile.CanExtract()) return self;
|
||||
|
||||
var list = await tempFolder.FullName.EnumerateFiles()
|
||||
.PMap(context.Queue,
|
||||
absSrc => Analyze(context, self, absSrc, absSrc.RelativeTo(tempFolder.FullName), depth + 1));
|
||||
await using var extracted = await extractedFile.ExtractAll(context.Queue);
|
||||
|
||||
self.Children = list.ToImmutableList();
|
||||
}
|
||||
var list = await extracted
|
||||
.PMap(context.Queue,
|
||||
file => Analyze(context, self, file.Value, file.Key, depth + 1));
|
||||
|
||||
self.Children = list.ToImmutableList();
|
||||
|
||||
return self;
|
||||
}
|
||||
@ -320,9 +317,9 @@ namespace Wabbajack.VirtualFileSystem
|
||||
return path;
|
||||
}
|
||||
|
||||
public FileStream OpenRead()
|
||||
public Stream OpenRead()
|
||||
{
|
||||
return StagedPath.OpenRead();
|
||||
return StagedFile.OpenRead();
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,7 +330,7 @@ namespace Wabbajack.VirtualFileSystem
|
||||
public string MD5 { get; set; }
|
||||
public string CRC { get; set; }
|
||||
|
||||
public static ExtendedHashes FromFile(AbsolutePath file)
|
||||
public static ExtendedHashes FromFile(IExtractedFile file)
|
||||
{
|
||||
var hashes = new ExtendedHashes();
|
||||
using (var stream = file.OpenRead())
|
||||
|
@ -84,7 +84,7 @@ namespace Wabbajack
|
||||
.ObserveOnGuiThread()
|
||||
.SelectTask(async msg =>
|
||||
{
|
||||
using var _ = await singleton_lock.Wait();
|
||||
using var _ = await singleton_lock.WaitAsync();
|
||||
try
|
||||
{
|
||||
await UserInterventionHandlers.Handle(msg);
|
||||
|
Loading…
Reference in New Issue
Block a user