From 77acf01fa088f46f221174955afe10553338fbe8 Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Sat, 2 May 2020 14:15:36 -0600 Subject: [PATCH] Debug code and fixes for BA2 duplicate file bug --- Compression.BSA.Test/BSATests.cs | 7 ++++++ Compression.BSA/BA2Reader.cs | 32 ++++++++++++++++++++++++++ Compression.BSA/BSAReader.cs | 26 +++++++++++++++++++++ Compression.BSA/IBSAReader.cs | 4 ++++ Compression.BSA/TES3Reader.cs | 10 ++++++++ Wabbajack.CLI/OptionsDefinition.cs | 3 ++- Wabbajack.CLI/Program.cs | 1 + Wabbajack.CLI/Verbs/BSADump.cs | 22 ++++++++++++++++++ Wabbajack.Test/EndToEndTests.cs | 1 + Wabbajack.VirtualFileSystem/Context.cs | 2 +- 10 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 Wabbajack.CLI/Verbs/BSADump.cs diff --git a/Compression.BSA.Test/BSATests.cs b/Compression.BSA.Test/BSATests.cs index 78b64723..87c8eb8f 100644 --- a/Compression.BSA.Test/BSATests.cs +++ b/Compression.BSA.Test/BSATests.cs @@ -75,6 +75,7 @@ namespace Compression.BSA.Test [InlineData(Game.Fallout4, 22223)] // 10mm SMG [InlineData(Game.Fallout4, 4472)] // True Storms [InlineData(Game.Morrowind, 44537)] + [InlineData(Game.Fallout4, 43474)] // EM 2 Rifle public async Task BSACompressionRecompression(Game game, int modid) { var filename = await DownloadMod(game, modid); @@ -109,6 +110,11 @@ namespace Compression.BSA.Test Assert.Equal(file.Size, absName.Size); }); + + + // Check Files should be case insensitive + Assert.Equal(a.Files.Count(), a.Files.Select(f => f.Path).ToHashSet().Count); + Assert.Equal(a.Files.Count(), a.Files.Select(f => f.Path.ToString().ToLowerInvariant()).ToHashSet().Count); TestContext.WriteLine($"Building {bsa}"); @@ -132,6 +138,7 @@ namespace Compression.BSA.Test // Check same number of files Assert.Equal(a.Files.Count(), b.Files.Count()); + await a.Files.Zip(b.Files, (ai, bi) => (ai, bi)) .PMap(Queue, pair => diff --git a/Compression.BSA/BA2Reader.cs b/Compression.BSA/BA2Reader.cs index 3acddeae..861e4480 100644 --- a/Compression.BSA/BA2Reader.cs +++ b/Compression.BSA/BA2Reader.cs @@ -108,6 +108,18 @@ namespace Compression.BSA public IEnumerable Files { get; private set; } public ArchiveStateObject State => new BA2StateObject(this); + public void Dump(Action print) + { + print($"HeaderMagic: {_headerMagic}"); + print($"Number of Files: {_numFiles}"); + print($"----------------------------"); + + foreach (var file in Files) + { + print("\n"); + file.Dump(print); + } + } } [JsonName("BA2State")] @@ -218,6 +230,12 @@ namespace Compression.BSA } } + + public void Dump(Action print) + { + throw new NotImplementedException(); + } + private void WriteHeader(BinaryWriter bw) { var ddsHeader = new DDS_HEADER(); @@ -426,6 +444,17 @@ namespace Compression.BSA internal int _index; public bool Compressed => _size != 0; + + public void Dump(Action print) + { + print($"FullPath: {FullPath}"); + print($"Name Hash: {_nameHash}"); + print($"Offset: {_offset}"); + print($"Flags: {_flags:x}"); + print($"Real Size: {_realSize}"); + print($"Index: {_index}"); + } + public BA2FileEntry(BA2Reader ba2Reader, int index) { @@ -442,6 +471,8 @@ namespace Compression.BSA _realSize = _rdr.ReadUInt32(); _align = _rdr.ReadUInt32(); } + + public string FullPath { get; set; } @@ -473,6 +504,7 @@ namespace Compression.BSA } } } + } [JsonName("BA2FileEntryState")] diff --git a/Compression.BSA/BSAReader.cs b/Compression.BSA/BSAReader.cs index 1f5d7c2c..f3b92722 100644 --- a/Compression.BSA/BSAReader.cs +++ b/Compression.BSA/BSAReader.cs @@ -65,6 +65,20 @@ namespace Compression.BSA internal uint _totalFileNameLength; internal uint _totalFolderNameLength; internal uint _version; + + public void Dump(Action print) + { + print($"File Name: {_fileName}"); + print($"File Count: {_fileCount}"); + print($"Magic: {_magic}"); + + foreach (var file in Files) + { + print("\n"); + file.Dump(print); + } + } + public BSAReader(AbsolutePath filename) { @@ -225,6 +239,18 @@ namespace Compression.BSA private readonly uint _originalSize; private readonly uint _size; internal readonly int _index; + + + public void Dump(Action print) + { + print($"Name: {_name}"); + print($"Offset: {_offset}"); + print($"On Disk Size: {_onDiskSize}"); + print($"Original Size: {_originalSize}"); + print($"Size: {_size}"); + print($"Index: {_index}"); + } + public FileRecord(BSAReader bsa, FolderRecord folderRecord, BinaryReader src, int index) { diff --git a/Compression.BSA/IBSAReader.cs b/Compression.BSA/IBSAReader.cs index 0d67d39a..0c6cb3eb 100644 --- a/Compression.BSA/IBSAReader.cs +++ b/Compression.BSA/IBSAReader.cs @@ -14,6 +14,8 @@ namespace Compression.BSA IEnumerable Files { get; } ArchiveStateObject State { get; } + + void Dump(Action print); } public interface IBSABuilder : IAsyncDisposable @@ -59,5 +61,7 @@ namespace Compression.BSA /// /// void CopyDataTo(Stream output); + + void Dump(Action print); } } diff --git a/Compression.BSA/TES3Reader.cs b/Compression.BSA/TES3Reader.cs index 1a2a5a87..63876dd0 100644 --- a/Compression.BSA/TES3Reader.cs +++ b/Compression.BSA/TES3Reader.cs @@ -81,6 +81,11 @@ namespace Compression.BSA }; } } + + public void Dump(Action print) + { + throw new NotImplementedException(); + } } [JsonName("TES3Archive")] @@ -120,6 +125,11 @@ namespace Compression.BSA fs.CopyToLimit(output, (int)Size); } + public void Dump(Action print) + { + throw new NotImplementedException(); + } + public uint Offset { get; set; } public uint NameOffset { get; set; } public uint Hash1 { get; set; } diff --git a/Wabbajack.CLI/OptionsDefinition.cs b/Wabbajack.CLI/OptionsDefinition.cs index 46e7de42..b14a52dd 100644 --- a/Wabbajack.CLI/OptionsDefinition.cs +++ b/Wabbajack.CLI/OptionsDefinition.cs @@ -18,7 +18,8 @@ namespace Wabbajack.CLI typeof(MyFiles), typeof(DeleteFile), typeof(Changelog), - typeof(FindSimilar) + typeof(FindSimilar), + typeof(BSADump) }; } } diff --git a/Wabbajack.CLI/Program.cs b/Wabbajack.CLI/Program.cs index 3e9638f5..c8f4412f 100644 --- a/Wabbajack.CLI/Program.cs +++ b/Wabbajack.CLI/Program.cs @@ -21,6 +21,7 @@ namespace Wabbajack.CLI (DeleteFile opts) => opts.Execute(), (Changelog opts) => opts.Execute(), (FindSimilar opts) => opts.Execute(), + (BSADump opts) => opts.Execute(), errs => 1); } } diff --git a/Wabbajack.CLI/Verbs/BSADump.cs b/Wabbajack.CLI/Verbs/BSADump.cs new file mode 100644 index 00000000..6b452631 --- /dev/null +++ b/Wabbajack.CLI/Verbs/BSADump.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading.Tasks; +using CommandLine; +using Compression.BSA; +using Wabbajack.Common; + +namespace Wabbajack.CLI.Verbs +{ + [Verb("bsa-dump", HelpText = "Print detailed info about the contents of a BSA")] + public class BSADump : AVerb + { + [Option('i', "input", Required = true, HelpText = "Input BSA to dump")] + public string Input { get; set; } = ""; + + protected override async Task Run() + { + await using var bsa = BSADispatch.OpenRead(Input.RelativeTo(AbsolutePath.GetCurrentDirectory())); + bsa.Dump(line => Console.WriteLine(line)); + return ExitCode.Ok; + } + } +} diff --git a/Wabbajack.Test/EndToEndTests.cs b/Wabbajack.Test/EndToEndTests.cs index d2f74495..86b0e12a 100644 --- a/Wabbajack.Test/EndToEndTests.cs +++ b/Wabbajack.Test/EndToEndTests.cs @@ -61,6 +61,7 @@ namespace Wabbajack.Test DownloadAndInstall(Game.SkyrimSpecialEdition, 12604, "SkyUI"), DownloadAndInstall(Game.Fallout4, 11925, "Anti-Tank Rifle"), DownloadAndInstall(Game.SkyrimSpecialEdition, 4783, "Frost Armor UNP"), + DownloadAndInstall(Game.Fallout4, 43474, "EM 2 Rifle No.9 Mk1"), DownloadAndInstall(Game.SkyrimSpecialEdition, 32359, "Frost Armor HDT")); // We're going to fully patch this mod from another source. diff --git a/Wabbajack.VirtualFileSystem/Context.cs b/Wabbajack.VirtualFileSystem/Context.cs index cc1d45ce..95f0ae7a 100644 --- a/Wabbajack.VirtualFileSystem/Context.cs +++ b/Wabbajack.VirtualFileSystem/Context.cs @@ -359,7 +359,7 @@ namespace Wabbajack.VirtualFileSystem public async Task Integrate(ICollection files) { Utils.Log($"Integrating {files.Count} files"); - var allFiles = AllFiles.Concat(files).GroupBy(f => f.Name).Select(g => g.Last()).ToImmutableList(); + var allFiles = AllFiles.Concat(files).GroupBy(f => f.FullPath).Select(g => g.Last()).ToImmutableList(); var byFullPath = Task.Run(() => allFiles.SelectMany(f => f.ThisAndAllChildren) .ToDictionary(f => f.FullPath));