diff --git a/Wabbajack.Lib/AInstaller.cs b/Wabbajack.Lib/AInstaller.cs index ad291fed..6924cefd 100644 --- a/Wabbajack.Lib/AInstaller.cs +++ b/Wabbajack.Lib/AInstaller.cs @@ -334,7 +334,6 @@ namespace Wabbajack.Lib .PMap(Queue, UpdateTracker, async f => { var relativeTo = f.RelativeTo(OutputFolder); - Utils.Status($"Checking if ModList file {relativeTo}"); if (indexed.ContainsKey(relativeTo) || f.InFolder(DownloadFolder)) return; @@ -378,17 +377,19 @@ namespace Wabbajack.Lib Utils.Log("Error when trying to clean empty folders. This doesn't really matter."); } + var existingfiles = OutputFolder.EnumerateFiles().ToHashSet(); + UpdateTracker.NextStep("Looking for unmodified files"); (await indexed.Values.PMap(Queue, UpdateTracker, async d => { // Bit backwards, but we want to return null for // all files we *want* installed. We return the files // to remove from the install list. - Status($"Optimizing {d.To}"); var path = OutputFolder.Combine(d.To); - if (!path.Exists) return null; + if (!existingfiles.Contains(path)) return null; if (path.Size != d.Size) return null; + Status($"Optimizing {d.To}"); return await path.FileHashCachedAsync() == d.Hash ? d : null; })) diff --git a/Wabbajack.Lib/MO2Installer.cs b/Wabbajack.Lib/MO2Installer.cs index ced679ae..ad9ce598 100644 --- a/Wabbajack.Lib/MO2Installer.cs +++ b/Wabbajack.Lib/MO2Installer.cs @@ -248,23 +248,19 @@ namespace Wabbajack.Lib private async Task InstallIncludedDownloadMetas() { - await ModList.Directives - .OfType() - .PMap(Queue, async directive => + await ModList.Archives + .PMap(Queue, async archive => { - Status($"Writing .meta file {directive.To}"); - foreach (var archive in ModList.Archives) + if (HashedArchives.TryGetValue(archive.Hash, out var paths)) { - if (HashedArchives.TryGetValue(archive.Hash, out var paths)) + var metaPath = paths.WithExtension(Consts.MetaFileExtension); + if (!metaPath.Exists) { - var metaPath = paths.WithExtension(Consts.MetaFileExtension); - if (!metaPath.Exists) - { - var meta = AddInstalled(archive.State.GetMetaIni()).ToArray(); - await metaPath.WriteAllLinesAsync(meta); - } + Status($"Writing {metaPath.FileName}"); + var meta = AddInstalled(archive.State.GetMetaIni()).ToArray(); + await metaPath.WriteAllLinesAsync(meta); } - } + } }); } diff --git a/Wabbajack.VirtualFileSystem.Test/FileExtractorTests.cs b/Wabbajack.VirtualFileSystem.Test/FileExtractorTests.cs index 607410ab..18c39e62 100644 --- a/Wabbajack.VirtualFileSystem.Test/FileExtractorTests.cs +++ b/Wabbajack.VirtualFileSystem.Test/FileExtractorTests.cs @@ -55,10 +55,10 @@ namespace Wabbajack.VirtualFileSystem.Test var results = await FileExtractor2.GatheringExtract(new NativeFileStreamFactory(archive.Path), _ => true, async (path, sfn) => - { - await using var s = await sfn.GetStream(); - return await s.xxHashAsync(); - }); + { + await using var s = await sfn.GetStream(); + return await s.xxHashAsync(); + }); Assert.Equal(10, results.Count); foreach (var (path, hash) in results) @@ -67,6 +67,35 @@ namespace Wabbajack.VirtualFileSystem.Test } } + [Fact] + public async Task CanExtractEmptyFiles() + { + await using var temp = await TempFolder.Create(); + await using var archive = new TempFile(); + + for (int i = 0; i < 1; i ++) + { + await WriteRandomData(temp.Dir.Combine($"{i}.bin"), _rng.Next(10, 1024)); + } + await (await temp.Dir.Combine("empty.txt").Create()).DisposeAsync(); + + await ZipUpFolder(temp.Dir, archive.Path, false); + + var results = await FileExtractor2.GatheringExtract(new NativeFileStreamFactory(archive.Path), + _ => true, + async (path, sfn) => + { + await using var s = await sfn.GetStream(); + return await s.xxHashAsync(); + }); + + Assert.Equal(2, results.Count); + foreach (var (path, hash) in results) + { + Assert.Equal(await temp.Dir.Combine(path).FileHashAsync(), hash); + } + } + private static Extension OMODExtension = new Extension(".omod"); private static Extension CRCExtension = new Extension(".crc"); diff --git a/Wabbajack.VirtualFileSystem/FileExtractor2/GatheringExtractor.cs b/Wabbajack.VirtualFileSystem/FileExtractor2/GatheringExtractor.cs index c86eb922..1fd98d6d 100644 --- a/Wabbajack.VirtualFileSystem/FileExtractor2/GatheringExtractor.cs +++ b/Wabbajack.VirtualFileSystem/FileExtractor2/GatheringExtractor.cs @@ -25,7 +25,6 @@ namespace Wabbajack.VirtualFileSystem public GatheringExtractor(Stream stream, Definitions.FileType sig, Predicate shouldExtract, Func> mapfn) { - _shouldExtract = shouldExtract; _mapFn = mapfn; _results = new Dictionary(); @@ -89,6 +88,14 @@ namespace Wabbajack.VirtualFileSystem { if (_indexes.ContainsKey(index)) { + var path = _indexes[index].Item1; + Utils.Status($"Extracting {path}", Percent.FactoryPutInRange(_results.Count, _indexes.Count)); + // Empty files are never extracted via a write call, so we have to fake that now + if (_indexes[index].Item2 == 0) + { + var result = _mapFn(path, new MemoryStreamFactory(new MemoryStream(), path)).Result; + _results.Add(path, result); + } outStream = new GatheringExtractorStream(this, index); return 0; }