From 88fa091d07bc273a5bcd887dfad2653f30345a6c Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Tue, 8 Oct 2019 15:20:43 -0600 Subject: [PATCH] implement new BSA framework for old BSA routines --- Compression.BSA.Test/Program.cs | 34 ++++++++++++++--- Compression.BSA/BA2Reader.cs | 3 +- Compression.BSA/BSABuilder.cs | 21 ++++++++++- Compression.BSA/BSAReader.cs | 67 ++++++++++++++++++++++++++------- 4 files changed, 104 insertions(+), 21 deletions(-) diff --git a/Compression.BSA.Test/Program.cs b/Compression.BSA.Test/Program.cs index 8e55cd88..2ca219f7 100644 --- a/Compression.BSA.Test/Program.cs +++ b/Compression.BSA.Test/Program.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -9,17 +10,24 @@ namespace Compression.BSA.Test { internal class Program { - private const string TestDir = @"D:\MO2 Instances\F4EE"; + //private const string TestDirBSA = @"D:\MO2 Instances\F4EE"; + //private const string TestDirBA2 = @"D:\MO2 Instances\F4EE"; + private const string TestDir = @"D:\MO2 Instances"; //private const string TestDir = @"D:\Steam\steamapps\common\Fallout 4"; private const string TempDir = @"c:\tmp\out\f4ee"; + private const string ArchiveTempDir = @"c:\tmp\out\archive"; + + //private const string Archive2Location = @"D:\Steam\steamapps\common\Fallout 4\Tools\Archive2\Archive2.exe"; private static void Main(string[] args) { - foreach (var bsa in Directory.EnumerateFiles(TestDir, "*.ba2", SearchOption.AllDirectories).Skip(0)) + foreach (var bsa in Directory.EnumerateFiles(TestDir, "*.ba2", SearchOption.AllDirectories) + .Concat(Directory.EnumerateFiles(TestDir, "*.bsa", SearchOption.AllDirectories)).Skip(200)) { Console.WriteLine($"From {bsa}"); Console.WriteLine("Cleaning Output Dir"); if (Directory.Exists(TempDir)) Directory.Delete(TempDir, true); + if (Directory.Exists(ArchiveTempDir)) Directory.Delete(ArchiveTempDir, true); Directory.CreateDirectory(TempDir); Console.WriteLine($"Reading {bsa}"); @@ -28,6 +36,7 @@ namespace Compression.BSA.Test Parallel.ForEach(a.Files, file => { var abs_name = Path.Combine(TempDir, file.Path); + ViaJson(file.State); if (!Directory.Exists(Path.GetDirectoryName(abs_name))) Directory.CreateDirectory(Path.GetDirectoryName(abs_name)); @@ -43,6 +52,22 @@ namespace Compression.BSA.Test }); + /* + Console.WriteLine("Extracting via Archive.exe"); + if (bsa.ToLower().EndsWith(".ba2")) + { + var p = Process.Start(Archive2Location, $"\"{bsa}\" -e=\"{ArchiveTempDir}\""); + p.WaitForExit(); + + foreach (var file in a.Files) + { + var a_path = Path.Combine(TempDir, file.Path); + var b_path = Path.Combine(ArchiveTempDir, file.Path); + Equal(new FileInfo(a_path).Length, new FileInfo(b_path).Length); + Equal(File.ReadAllBytes(a_path), File.ReadAllBytes(b_path)); + } + }*/ + Console.WriteLine($"Building {bsa}"); @@ -59,9 +84,6 @@ namespace Compression.BSA.Test }); w.Build("c:\\tmp\\tmp.bsa"); - - - } Console.WriteLine($"Verifying {bsa}"); @@ -163,7 +185,7 @@ namespace Compression.BSA.Test for (var idx = 0; idx < a.Length; idx++) if (a[idx] != b[idx]) - throw new InvalidDataException($"Byte array contents not equal at {idx}"); + throw new InvalidDataException($"Byte array contents not equal at {idx} - {a[idx]} vs {b[idx]}"); } } } \ No newline at end of file diff --git a/Compression.BSA/BA2Reader.cs b/Compression.BSA/BA2Reader.cs index 2498a337..a7db1741 100644 --- a/Compression.BSA/BA2Reader.cs +++ b/Compression.BSA/BA2Reader.cs @@ -250,6 +250,7 @@ namespace Compression.BSA ddsHeader.dwWidth = _width; ddsHeader.dwMipMapCount = _numMips; ddsHeader.PixelFormat.dwSize = ddsHeader.PixelFormat.GetSize(); + ddsHeader.dwDepth = 1; ddsHeader.dwSurfaceFlags = DDS.DDS_SURFACE_FLAGS_TEXTURE | DDS.DDS_SURFACE_FLAGS_MIPMAP; switch ((DXGI_FORMAT)_format) @@ -274,7 +275,7 @@ namespace Compression.BSA if (_bsa.UseATIFourCC) ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('A', 'T', 'I', '2'); // this is more correct but the only thing I have found that supports it is the nvidia photoshop plugin else - ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('D', 'X', 'T', '5'); + ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('B', 'C', '5', 'U'); ddsHeader.dwPitchOrLinearSize = (uint)(_width * _height); // 8bpp break; case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB: diff --git a/Compression.BSA/BSABuilder.cs b/Compression.BSA/BSABuilder.cs index 6d70f6f9..2d8d9a51 100644 --- a/Compression.BSA/BSABuilder.cs +++ b/Compression.BSA/BSABuilder.cs @@ -11,7 +11,7 @@ using Path = Alphaleonis.Win32.Filesystem.Path; namespace Compression.BSA { - public class BSABuilder : IDisposable + public class BSABuilder : IDisposable, IBSABuilder { internal uint _archiveFlags; internal uint _fileCount; @@ -32,6 +32,13 @@ namespace Compression.BSA _offset = 0x24; } + public BSABuilder(BSAStateObject bsaStateObject) : this() + { + _version = bsaStateObject.Version; + _fileFlags = bsaStateObject.FileFlags; + _archiveFlags = bsaStateObject.ArchiveFlags; + } + public IEnumerable Files => _files; public ArchiveFlags ArchiveFlags @@ -85,6 +92,18 @@ namespace Compression.BSA return r; } + public void AddFile(FileStateObject state, Stream src) + { + var ostate = (BSAFileStateObject) state; + + var r = new FileEntry(this, ostate.Path, src, ostate.FlipCompression); + + lock (this) + { + _files.Add(r); + } + } + public void Build(string outputName) { RegenFolderRecords(); diff --git a/Compression.BSA/BSAReader.cs b/Compression.BSA/BSAReader.cs index c1fc66b4..7cca4828 100644 --- a/Compression.BSA/BSAReader.cs +++ b/Compression.BSA/BSAReader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Text; +using System.Threading; using ICSharpCode.SharpZipLib.Zip.Compression.Streams; using K4os.Compression.LZ4.Streams; using File = Alphaleonis.Win32.Filesystem.File; @@ -48,19 +49,19 @@ namespace Compression.BSA public class BSAReader : IDisposable, IBSAReader { - private uint _archiveFlags; - private uint _fileCount; - private uint _fileFlags; + internal uint _archiveFlags; + internal uint _fileCount; + internal uint _fileFlags; internal string _fileName; - private uint _folderCount; - private uint _folderRecordOffset; + internal uint _folderCount; + internal uint _folderRecordOffset; private List _folders; - private string _magic; + internal string _magic; private readonly BinaryReader _rdr; private readonly Stream _stream; - private uint _totalFileNameLength; - private uint _totalFolderNameLength; - private uint _version; + internal uint _totalFileNameLength; + internal uint _totalFolderNameLength; + internal uint _version; public BSAReader(string filename) : this(File.OpenRead(filename)) { @@ -84,7 +85,7 @@ namespace Compression.BSA } } - public ArchiveStateObject State { get; } + public ArchiveStateObject State => new BSAStateObject(this); public VersionType HeaderType => (VersionType) _version; @@ -150,6 +151,29 @@ namespace Compression.BSA } } + public class BSAStateObject : ArchiveStateObject + { + public BSAStateObject() { } + public BSAStateObject(BSAReader bsaReader) + { + Magic = bsaReader._magic; + Version = bsaReader._version; + ArchiveFlags = bsaReader._archiveFlags; + FileFlags = bsaReader._fileFlags; + + } + + public override IBSABuilder MakeBuilder() + { + return new BSABuilder(this); + } + + public string Magic { get; set; } + public uint Version { get; set; } + public uint ArchiveFlags { get; set; } + public uint FileFlags { get; set; } + } + public class FolderRecord { private readonly uint _fileCount; @@ -181,7 +205,8 @@ namespace Compression.BSA if (bsa.HasFolderNames) Name = src.ReadStringLen(bsa.HeaderType); _files = new List(); - for (var idx = 0; idx < _fileCount; idx += 1) _files.Add(new FileRecord(bsa, this, src)); + for (var idx = 0; idx < _fileCount; idx += 1) + _files.Add(new FileRecord(bsa, this, src, idx)); } } @@ -196,9 +221,11 @@ namespace Compression.BSA private readonly uint _onDiskSize; private readonly uint _originalSize; private readonly uint _size; + internal readonly int _index; - public FileRecord(BSAReader bsa, FolderRecord folderRecord, BinaryReader src) + public FileRecord(BSAReader bsa, FolderRecord folderRecord, BinaryReader src, int index) { + _index = index; _bsa = bsa; Hash = src.ReadUInt64(); var size = src.ReadUInt32(); @@ -262,7 +289,7 @@ namespace Compression.BSA } public uint Size => _dataSize; - public FileStateObject State { get; } + public FileStateObject State => new BSAFileStateObject(this); public ulong Hash { get; } @@ -314,4 +341,18 @@ namespace Compression.BSA return ms.ToArray(); } } + + public class BSAFileStateObject : FileStateObject + { + public BSAFileStateObject() { } + public BSAFileStateObject(FileRecord fileRecord) + { + FlipCompression = fileRecord.FlipCompression; + Path = fileRecord.Path; + Index = fileRecord._index; + } + + public bool FlipCompression { get; set; } + public string Path { get; set; } + } } \ No newline at end of file