implement new BSA framework for old BSA routines

This commit is contained in:
Timothy Baldridge 2019-10-08 15:20:43 -06:00
parent ce4a3e17dd
commit 88fa091d07
4 changed files with 104 additions and 21 deletions

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -9,17 +10,24 @@ namespace Compression.BSA.Test
{ {
internal class Program 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 TestDir = @"D:\Steam\steamapps\common\Fallout 4";
private const string TempDir = @"c:\tmp\out\f4ee"; 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) 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($"From {bsa}");
Console.WriteLine("Cleaning Output Dir"); Console.WriteLine("Cleaning Output Dir");
if (Directory.Exists(TempDir)) Directory.Delete(TempDir, true); if (Directory.Exists(TempDir)) Directory.Delete(TempDir, true);
if (Directory.Exists(ArchiveTempDir)) Directory.Delete(ArchiveTempDir, true);
Directory.CreateDirectory(TempDir); Directory.CreateDirectory(TempDir);
Console.WriteLine($"Reading {bsa}"); Console.WriteLine($"Reading {bsa}");
@ -28,6 +36,7 @@ namespace Compression.BSA.Test
Parallel.ForEach(a.Files, file => Parallel.ForEach(a.Files, file =>
{ {
var abs_name = Path.Combine(TempDir, file.Path); var abs_name = Path.Combine(TempDir, file.Path);
ViaJson(file.State);
if (!Directory.Exists(Path.GetDirectoryName(abs_name))) if (!Directory.Exists(Path.GetDirectoryName(abs_name)))
Directory.CreateDirectory(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}"); Console.WriteLine($"Building {bsa}");
@ -59,9 +84,6 @@ namespace Compression.BSA.Test
}); });
w.Build("c:\\tmp\\tmp.bsa"); w.Build("c:\\tmp\\tmp.bsa");
} }
Console.WriteLine($"Verifying {bsa}"); Console.WriteLine($"Verifying {bsa}");
@ -163,7 +185,7 @@ namespace Compression.BSA.Test
for (var idx = 0; idx < a.Length; idx++) for (var idx = 0; idx < a.Length; idx++)
if (a[idx] != b[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]}");
} }
} }
} }

View File

@ -250,6 +250,7 @@ namespace Compression.BSA
ddsHeader.dwWidth = _width; ddsHeader.dwWidth = _width;
ddsHeader.dwMipMapCount = _numMips; ddsHeader.dwMipMapCount = _numMips;
ddsHeader.PixelFormat.dwSize = ddsHeader.PixelFormat.GetSize(); ddsHeader.PixelFormat.dwSize = ddsHeader.PixelFormat.GetSize();
ddsHeader.dwDepth = 1;
ddsHeader.dwSurfaceFlags = DDS.DDS_SURFACE_FLAGS_TEXTURE | DDS.DDS_SURFACE_FLAGS_MIPMAP; ddsHeader.dwSurfaceFlags = DDS.DDS_SURFACE_FLAGS_TEXTURE | DDS.DDS_SURFACE_FLAGS_MIPMAP;
switch ((DXGI_FORMAT)_format) switch ((DXGI_FORMAT)_format)
@ -274,7 +275,7 @@ namespace Compression.BSA
if (_bsa.UseATIFourCC) 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 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 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 ddsHeader.dwPitchOrLinearSize = (uint)(_width * _height); // 8bpp
break; break;
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB: case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:

View File

@ -11,7 +11,7 @@ using Path = Alphaleonis.Win32.Filesystem.Path;
namespace Compression.BSA namespace Compression.BSA
{ {
public class BSABuilder : IDisposable public class BSABuilder : IDisposable, IBSABuilder
{ {
internal uint _archiveFlags; internal uint _archiveFlags;
internal uint _fileCount; internal uint _fileCount;
@ -32,6 +32,13 @@ namespace Compression.BSA
_offset = 0x24; _offset = 0x24;
} }
public BSABuilder(BSAStateObject bsaStateObject) : this()
{
_version = bsaStateObject.Version;
_fileFlags = bsaStateObject.FileFlags;
_archiveFlags = bsaStateObject.ArchiveFlags;
}
public IEnumerable<FileEntry> Files => _files; public IEnumerable<FileEntry> Files => _files;
public ArchiveFlags ArchiveFlags public ArchiveFlags ArchiveFlags
@ -85,6 +92,18 @@ namespace Compression.BSA
return r; 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) public void Build(string outputName)
{ {
RegenFolderRecords(); RegenFolderRecords();

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams; using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using K4os.Compression.LZ4.Streams; using K4os.Compression.LZ4.Streams;
using File = Alphaleonis.Win32.Filesystem.File; using File = Alphaleonis.Win32.Filesystem.File;
@ -48,19 +49,19 @@ namespace Compression.BSA
public class BSAReader : IDisposable, IBSAReader public class BSAReader : IDisposable, IBSAReader
{ {
private uint _archiveFlags; internal uint _archiveFlags;
private uint _fileCount; internal uint _fileCount;
private uint _fileFlags; internal uint _fileFlags;
internal string _fileName; internal string _fileName;
private uint _folderCount; internal uint _folderCount;
private uint _folderRecordOffset; internal uint _folderRecordOffset;
private List<FolderRecord> _folders; private List<FolderRecord> _folders;
private string _magic; internal string _magic;
private readonly BinaryReader _rdr; private readonly BinaryReader _rdr;
private readonly Stream _stream; private readonly Stream _stream;
private uint _totalFileNameLength; internal uint _totalFileNameLength;
private uint _totalFolderNameLength; internal uint _totalFolderNameLength;
private uint _version; internal uint _version;
public BSAReader(string filename) : this(File.OpenRead(filename)) 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; 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 public class FolderRecord
{ {
private readonly uint _fileCount; private readonly uint _fileCount;
@ -181,7 +205,8 @@ namespace Compression.BSA
if (bsa.HasFolderNames) Name = src.ReadStringLen(bsa.HeaderType); if (bsa.HasFolderNames) Name = src.ReadStringLen(bsa.HeaderType);
_files = new List<FileRecord>(); _files = new List<FileRecord>();
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 _onDiskSize;
private readonly uint _originalSize; private readonly uint _originalSize;
private readonly uint _size; 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; _bsa = bsa;
Hash = src.ReadUInt64(); Hash = src.ReadUInt64();
var size = src.ReadUInt32(); var size = src.ReadUInt32();
@ -262,7 +289,7 @@ namespace Compression.BSA
} }
public uint Size => _dataSize; public uint Size => _dataSize;
public FileStateObject State { get; } public FileStateObject State => new BSAFileStateObject(this);
public ulong Hash { get; } public ulong Hash { get; }
@ -314,4 +341,18 @@ namespace Compression.BSA
return ms.ToArray(); 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; }
}
} }