mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
start of bs2 support, can read the 4 .bs2 files I threw at it
This commit is contained in:
parent
ad661871a5
commit
817576d085
@ -7,6 +7,7 @@
|
||||
* Remove nexus timeout for login, it's pointless.
|
||||
* Force slides to load before displaying
|
||||
* Supress slide load failures
|
||||
* Setup Crash handling at the very start of the app
|
||||
|
||||
#### Version 0.9.4 - 10/2/2019
|
||||
* Point github icon to https://github.com/wabbajack-tools/wabbajack
|
||||
|
@ -8,20 +8,20 @@ namespace Compression.BSA.Test
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private const string TestDir = @"D:\MO2 Instances\";
|
||||
private const string TempDir = "c:\\tmp\\out";
|
||||
private const string TestDir = @"D:\MO2 Instances\F4EE";
|
||||
private const string TempDir = @"c:\tmp\out\f4ee";
|
||||
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
foreach (var bsa in Directory.EnumerateFiles(TestDir, "*.bsa", SearchOption.AllDirectories).Skip(0))
|
||||
foreach (var bsa in Directory.EnumerateFiles(TestDir, "*.ba2", SearchOption.AllDirectories).Skip(1))
|
||||
{
|
||||
Console.WriteLine($"From {bsa}");
|
||||
Console.WriteLine("Cleaning Output Dir");
|
||||
if (Directory.Exists(TempDir)) Directory.Delete(TempDir, true);
|
||||
//if (Directory.Exists(TempDir)) Directory.Delete(TempDir, true);
|
||||
Directory.CreateDirectory(TempDir);
|
||||
|
||||
Console.WriteLine($"Reading {bsa}");
|
||||
using (var a = new BSAReader(bsa))
|
||||
using (var a = BSADispatch.OpenRead(bsa))
|
||||
{
|
||||
Parallel.ForEach(a.Files, file =>
|
||||
{
|
||||
@ -30,15 +30,18 @@ namespace Compression.BSA.Test
|
||||
if (!Directory.Exists(Path.GetDirectoryName(abs_name)))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(abs_name));
|
||||
|
||||
|
||||
using (var fs = File.OpenWrite(abs_name))
|
||||
{
|
||||
file.CopyDataTo(fs);
|
||||
}
|
||||
|
||||
|
||||
Equal(file.Size, new FileInfo(abs_name).Length);
|
||||
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
Console.WriteLine($"Building {bsa}");
|
||||
|
||||
using (var w = new BSABuilder())
|
||||
@ -62,16 +65,12 @@ namespace Compression.BSA.Test
|
||||
Equal(a.Files.Count(), w.Files.Count());
|
||||
Equal(a.Files.Select(f => f.Path).ToHashSet(), w.Files.Select(f => f.Path).ToHashSet());
|
||||
|
||||
/*foreach (var pair in Enumerable.Zip(a.Files, w.Files, (ai, bi) => (ai, bi)))
|
||||
{
|
||||
Console.WriteLine($"{pair.ai.Path}, {pair.ai.Hash}, {pair.bi.Path}, {pair.bi.Hash}");
|
||||
}*/
|
||||
|
||||
foreach (var pair in a.Files.Zip(w.Files, (ai, bi) => (ai, bi)))
|
||||
{
|
||||
Equal(pair.ai.Path, pair.bi.Path);
|
||||
Equal(pair.ai.Hash, pair.bi.Hash);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Console.WriteLine($"Verifying {bsa}");
|
||||
@ -93,7 +92,7 @@ namespace Compression.BSA.Test
|
||||
Equal(pair.ai.Size, pair.bi.Size);
|
||||
Equal(pair.ai.GetData(), pair.bi.GetData());
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
398
Compression.BSA/BA2Reader.cs
Normal file
398
Compression.BSA/BA2Reader.cs
Normal file
@ -0,0 +1,398 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
|
||||
namespace Compression.BSA
|
||||
{
|
||||
enum EntryType
|
||||
{
|
||||
GNRL,
|
||||
DX10,
|
||||
GNMF
|
||||
}
|
||||
|
||||
interface IFileEntry : IFile
|
||||
{
|
||||
string FullPath { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class BA2Reader : IBSAReader
|
||||
{
|
||||
internal string _filename;
|
||||
private Stream _stream;
|
||||
internal BinaryReader _rdr;
|
||||
private uint _version;
|
||||
private string _headerMagic;
|
||||
private EntryType _type;
|
||||
private uint _numFiles;
|
||||
private ulong _nameTableOffset;
|
||||
public bool UseATIFourCC { get; set; } = false;
|
||||
|
||||
public bool HasNameTable => _nameTableOffset > 0;
|
||||
|
||||
public BA2Reader(string filename) : this(File.OpenRead(filename))
|
||||
{
|
||||
_filename = filename;
|
||||
}
|
||||
|
||||
public BA2Reader(Stream stream)
|
||||
{
|
||||
_stream = stream;
|
||||
_rdr = new BinaryReader(_stream, Encoding.UTF7);
|
||||
LoadHeaders();
|
||||
}
|
||||
|
||||
public void LoadHeaders()
|
||||
{
|
||||
_headerMagic = Encoding.ASCII.GetString(_rdr.ReadBytes(4));
|
||||
|
||||
if (_headerMagic != "BTDX")
|
||||
throw new InvalidDataException("Unknown header type: " + _headerMagic);
|
||||
|
||||
_version = _rdr.ReadUInt32();
|
||||
|
||||
string fourcc = Encoding.ASCII.GetString(_rdr.ReadBytes(4));
|
||||
|
||||
if (Enum.TryParse(fourcc, out EntryType entryType))
|
||||
{
|
||||
_type = entryType;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidDataException($"Can't parse entry types of {fourcc}");
|
||||
}
|
||||
|
||||
_numFiles = _rdr.ReadUInt32();
|
||||
_nameTableOffset = _rdr.ReadUInt64();
|
||||
|
||||
var files = new List<IFileEntry>();
|
||||
for (var idx = 0; idx < _numFiles; idx += 1)
|
||||
{
|
||||
switch (_type)
|
||||
{
|
||||
case EntryType.GNRL:
|
||||
files.Add(new BA2FileEntry(this));
|
||||
break;
|
||||
case EntryType.DX10:
|
||||
files.Add(new BA2DX10Entry(this));
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (HasNameTable)
|
||||
{
|
||||
_rdr.BaseStream.Seek((long) _nameTableOffset, SeekOrigin.Begin);
|
||||
foreach (var file in files)
|
||||
file.FullPath = Encoding.UTF7.GetString(_rdr.ReadBytes(_rdr.ReadInt16()));
|
||||
}
|
||||
Files = files;
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_stream?.Dispose();
|
||||
_rdr?.Dispose();
|
||||
}
|
||||
|
||||
public IEnumerable<IFile> Files { get; private set; }
|
||||
}
|
||||
|
||||
public class BA2DX10Entry : IFileEntry
|
||||
{
|
||||
private uint _nameHash;
|
||||
private string _extension;
|
||||
private uint _dirHash;
|
||||
private byte _unk8;
|
||||
private byte _numChunks;
|
||||
private ushort _chunkHdrLen;
|
||||
private ushort _height;
|
||||
private ushort _width;
|
||||
private byte _numMips;
|
||||
private byte _format;
|
||||
private ushort _unk16;
|
||||
private List<BA2TextureChunk> _chunks;
|
||||
private BA2Reader _bsa;
|
||||
|
||||
public BA2DX10Entry(BA2Reader ba2Reader)
|
||||
{
|
||||
_bsa = ba2Reader;
|
||||
var _rdr = ba2Reader._rdr;
|
||||
_nameHash = _rdr.ReadUInt32();
|
||||
FullPath = _nameHash.ToString("X");
|
||||
_extension = Encoding.UTF7.GetString(_rdr.ReadBytes(4));
|
||||
_dirHash = _rdr.ReadUInt32();
|
||||
_unk8 = _rdr.ReadByte();
|
||||
_numChunks = _rdr.ReadByte();
|
||||
_chunkHdrLen = _rdr.ReadUInt16();
|
||||
_height = _rdr.ReadUInt16();
|
||||
_width = _rdr.ReadUInt16();
|
||||
_numMips = _rdr.ReadByte();
|
||||
_format = _rdr.ReadByte();
|
||||
_unk16 = _rdr.ReadUInt16();
|
||||
|
||||
_chunks = Enumerable.Range(0, _numChunks)
|
||||
.Select(idx => new BA2TextureChunk(_rdr))
|
||||
.ToList();
|
||||
|
||||
}
|
||||
|
||||
public string FullPath { get; set; }
|
||||
|
||||
public string Path => FullPath;
|
||||
public uint Size => (uint)_chunks.Sum(f => f._fullSz) + HeaderSize + sizeof(uint);
|
||||
|
||||
public uint HeaderSize
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
switch ((DXGI_FORMAT) _format)
|
||||
{
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
|
||||
return DDS_HEADER_DXT10.Size + DDS_HEADER.Size;
|
||||
default:
|
||||
return DDS_HEADER.Size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyDataTo(Stream output)
|
||||
{
|
||||
var bw = new BinaryWriter(output);
|
||||
|
||||
WriteHeader(bw);
|
||||
|
||||
using (var fs = File.OpenRead(_bsa._filename))
|
||||
using (var br = new BinaryReader(fs))
|
||||
{
|
||||
foreach (var chunk in _chunks)
|
||||
{
|
||||
byte[] full = new byte[chunk._fullSz];
|
||||
var isCompressed = chunk._packSz != 0;
|
||||
|
||||
br.BaseStream.Seek((long)chunk._offset, SeekOrigin.Begin);
|
||||
|
||||
if (!isCompressed)
|
||||
{
|
||||
br.Read(full, 0, full.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] compressed = new byte[chunk._packSz];
|
||||
br.Read(compressed, 0, compressed.Length);
|
||||
var inflater = new Inflater();
|
||||
inflater.SetInput(compressed);
|
||||
inflater.Inflate(full);
|
||||
}
|
||||
|
||||
bw.Write(full);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private void WriteHeader(BinaryWriter bw)
|
||||
{
|
||||
var ddsHeader = new DDS_HEADER();
|
||||
|
||||
ddsHeader.dwSize = ddsHeader.GetSize();
|
||||
ddsHeader.dwHeaderFlags = DDS.DDS_HEADER_FLAGS_TEXTURE | DDS.DDS_HEADER_FLAGS_LINEARSIZE | DDS.DDS_HEADER_FLAGS_MIPMAP;
|
||||
ddsHeader.dwHeight = _height;
|
||||
ddsHeader.dwWidth = _width;
|
||||
ddsHeader.dwMipMapCount = _numMips;
|
||||
ddsHeader.PixelFormat.dwSize = ddsHeader.PixelFormat.GetSize();
|
||||
ddsHeader.dwSurfaceFlags = DDS.DDS_SURFACE_FLAGS_TEXTURE | DDS.DDS_SURFACE_FLAGS_MIPMAP;
|
||||
|
||||
switch ((DXGI_FORMAT)_format)
|
||||
{
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM:
|
||||
ddsHeader.PixelFormat.dwFlags = DDS.DDS_FOURCC;
|
||||
ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('D', 'X', 'T', '1');
|
||||
ddsHeader.dwPitchOrLinearSize = (uint)(_width * _height / 2); // 4bpp
|
||||
break;
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM:
|
||||
ddsHeader.PixelFormat.dwFlags = DDS.DDS_FOURCC;
|
||||
ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('D', 'X', 'T', '3');
|
||||
ddsHeader.dwPitchOrLinearSize = (uint)(_width * _height); // 8bpp
|
||||
break;
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM:
|
||||
ddsHeader.PixelFormat.dwFlags = DDS.DDS_FOURCC;
|
||||
ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('D', 'X', 'T', '5');
|
||||
ddsHeader.dwPitchOrLinearSize = (uint)(_width * _height); // 8bpp
|
||||
break;
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM:
|
||||
ddsHeader.PixelFormat.dwFlags = DDS.DDS_FOURCC;
|
||||
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.dwPitchOrLinearSize = (uint)(_width * _height); // 8bpp
|
||||
break;
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
|
||||
ddsHeader.PixelFormat.dwFlags = DDS.DDS_FOURCC;
|
||||
ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('D', 'X', '1', '0');
|
||||
ddsHeader.dwPitchOrLinearSize = (uint)(_width * _height / 2); // 4bpp
|
||||
break;
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
|
||||
ddsHeader.PixelFormat.dwFlags = DDS.DDS_FOURCC;
|
||||
ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('D', 'X', '1', '0');
|
||||
ddsHeader.dwPitchOrLinearSize = (uint)(_width * _height); // 8bpp
|
||||
break;
|
||||
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
|
||||
ddsHeader.PixelFormat.dwFlags = DDS.DDS_RGBA;
|
||||
ddsHeader.PixelFormat.dwRGBBitCount = 32;
|
||||
ddsHeader.PixelFormat.dwRBitMask = 0x000000FF;
|
||||
ddsHeader.PixelFormat.dwGBitMask = 0x0000FF00;
|
||||
ddsHeader.PixelFormat.dwBBitMask = 0x00FF0000;
|
||||
ddsHeader.PixelFormat.dwABitMask = 0xFF000000;
|
||||
ddsHeader.dwPitchOrLinearSize = (uint)(_width * _height * 4); // 32bpp
|
||||
break;
|
||||
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM:
|
||||
ddsHeader.PixelFormat.dwFlags = DDS.DDS_RGBA;
|
||||
ddsHeader.PixelFormat.dwRGBBitCount = 32;
|
||||
ddsHeader.PixelFormat.dwRBitMask = 0x00FF0000;
|
||||
ddsHeader.PixelFormat.dwGBitMask = 0x0000FF00;
|
||||
ddsHeader.PixelFormat.dwBBitMask = 0x000000FF;
|
||||
ddsHeader.PixelFormat.dwABitMask = 0xFF000000;
|
||||
ddsHeader.dwPitchOrLinearSize = (uint)(_width * _height * 4); // 32bpp
|
||||
break;
|
||||
case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM:
|
||||
ddsHeader.PixelFormat.dwFlags = DDS.DDS_RGB;
|
||||
ddsHeader.PixelFormat.dwRGBBitCount = 8;
|
||||
ddsHeader.PixelFormat.dwRBitMask = 0xFF;
|
||||
ddsHeader.dwPitchOrLinearSize = (uint)(_width * _height); // 8bpp
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unsupported DDS header format. File: " + this.FullPath);
|
||||
}
|
||||
|
||||
bw.Write((uint)DDS.DDS_MAGIC);
|
||||
ddsHeader.Write(bw);
|
||||
|
||||
switch ((DXGI_FORMAT)_format)
|
||||
{
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
|
||||
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
|
||||
var dxt10 = new DDS_HEADER_DXT10()
|
||||
{
|
||||
dxgiFormat = _format,
|
||||
resourceDimension = (uint)DXT10_RESOURCE_DIMENSION.DIMENSION_TEXTURE2D,
|
||||
miscFlag = 0,
|
||||
arraySize = 1,
|
||||
miscFlags2 = DDS.DDS_ALPHA_MODE_UNKNOWN
|
||||
};
|
||||
dxt10.Write(bw);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BA2TextureChunk
|
||||
{
|
||||
internal ulong _offset;
|
||||
internal uint _packSz;
|
||||
internal uint _fullSz;
|
||||
internal ushort _startMip;
|
||||
internal ushort _endMip;
|
||||
internal uint _align;
|
||||
|
||||
public BA2TextureChunk(BinaryReader rdr)
|
||||
{
|
||||
_offset = rdr.ReadUInt64();
|
||||
_packSz = rdr.ReadUInt32();
|
||||
_fullSz = rdr.ReadUInt32();
|
||||
_startMip = rdr.ReadUInt16();
|
||||
_endMip = rdr.ReadUInt16();
|
||||
_align = rdr.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
public class BA2FileEntry : IFileEntry
|
||||
{
|
||||
private uint _nameHash;
|
||||
private string _extension;
|
||||
private uint _dirHash;
|
||||
private uint _flags;
|
||||
private ulong _offset;
|
||||
private uint _size;
|
||||
private uint _realSize;
|
||||
private uint _align;
|
||||
private BA2Reader _bsa;
|
||||
|
||||
private bool Compressed => this.Size != 0;
|
||||
|
||||
public BA2FileEntry(BA2Reader ba2Reader)
|
||||
{
|
||||
_bsa = ba2Reader;
|
||||
var _rdr = ba2Reader._rdr;
|
||||
_nameHash = _rdr.ReadUInt32();
|
||||
FullPath = _nameHash.ToString("X");
|
||||
_extension = Encoding.UTF7.GetString(_rdr.ReadBytes(4));
|
||||
_dirHash = _rdr.ReadUInt32();
|
||||
_flags = _rdr.ReadUInt32();
|
||||
_offset = _rdr.ReadUInt64();
|
||||
_size = _rdr.ReadUInt32();
|
||||
_realSize = _rdr.ReadUInt32();
|
||||
_align = _rdr.ReadUInt32();
|
||||
}
|
||||
|
||||
public string FullPath { get; set; }
|
||||
|
||||
public string Path => FullPath;
|
||||
public uint Size => _realSize;
|
||||
|
||||
public void CopyDataTo(Stream output)
|
||||
{
|
||||
using (var bw = new BinaryWriter(output))
|
||||
using (var fs = File.OpenRead(_bsa._filename))
|
||||
using (var br = new BinaryReader(fs))
|
||||
{
|
||||
br.BaseStream.Seek((long) _offset, SeekOrigin.Begin);
|
||||
uint len = Compressed ? _size : _realSize;
|
||||
|
||||
var bytes = new byte[len];
|
||||
br.Read(bytes, 0, (int) len);
|
||||
|
||||
if (!Compressed)
|
||||
{
|
||||
bw.Write(bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
var uncompressed = new byte[_realSize];
|
||||
var inflater = new Inflater();
|
||||
inflater.SetInput(bytes);
|
||||
inflater.Inflate(uncompressed);
|
||||
bw.Write(uncompressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
Compression.BSA/BSADispatch.cs
Normal file
27
Compression.BSA/BSADispatch.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Compression.BSA
|
||||
{
|
||||
public static class BSADispatch
|
||||
{
|
||||
public static IBSAReader OpenRead(string filename)
|
||||
{
|
||||
string fourcc = "";
|
||||
using (var file = File.OpenRead(filename))
|
||||
{
|
||||
fourcc = Encoding.ASCII.GetString(new BinaryReader(file).ReadBytes(4));
|
||||
}
|
||||
|
||||
if (fourcc == "BSA\0")
|
||||
return new BSAReader(filename);
|
||||
if (fourcc == "BTDX")
|
||||
return new BA2Reader(filename);
|
||||
throw new InvalidDataException("Filename is not a .bsa or .ba2, magic " + fourcc);
|
||||
}
|
||||
}
|
||||
}
|
@ -46,7 +46,7 @@ namespace Compression.BSA
|
||||
Miscellaneous = 0x100
|
||||
}
|
||||
|
||||
public class BSAReader : IDisposable
|
||||
public class BSAReader : IDisposable, IBSAReader
|
||||
{
|
||||
private uint _archiveFlags;
|
||||
private uint _fileCount;
|
||||
@ -74,7 +74,7 @@ namespace Compression.BSA
|
||||
LoadHeaders();
|
||||
}
|
||||
|
||||
public IEnumerable<FileRecord> Files
|
||||
public IEnumerable<IFile> Files
|
||||
{
|
||||
get
|
||||
{
|
||||
@ -183,7 +183,7 @@ namespace Compression.BSA
|
||||
}
|
||||
}
|
||||
|
||||
public class FileRecord
|
||||
public class FileRecord : IFile
|
||||
{
|
||||
private readonly BSAReader _bsa;
|
||||
private readonly long _dataOffset;
|
||||
@ -259,7 +259,7 @@ namespace Compression.BSA
|
||||
}
|
||||
}
|
||||
|
||||
public int Size => (int) _dataSize;
|
||||
public uint Size => _dataSize;
|
||||
|
||||
public ulong Hash { get; }
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
@ -30,6 +31,7 @@
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@ -39,6 +41,7 @@
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
@ -48,6 +51,7 @@
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="AlphaFS, Version=2.2.0.0, Culture=neutral, PublicKeyToken=4d31a58f7d7ad5c9, processorArchitecture=MSIL">
|
||||
@ -89,8 +93,12 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BA2Reader.cs" />
|
||||
<Compile Include="BSABuilder.cs" />
|
||||
<Compile Include="BSADispatch.cs" />
|
||||
<Compile Include="BSAReader.cs" />
|
||||
<Compile Include="DDS.cs" />
|
||||
<Compile Include="IBSAReader.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Utils.cs" />
|
||||
</ItemGroup>
|
||||
|
205
Compression.BSA/DDS.cs
Normal file
205
Compression.BSA/DDS.cs
Normal file
@ -0,0 +1,205 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Compression.BSA
|
||||
{
|
||||
/*
|
||||
* Copied from https://raw.githubusercontent.com/AlexxEG/BSA_Browser/master/Sharp.BSA.BA2/BA2Util/DDS.cs
|
||||
* which is also GPL3 code. Modified slightly for Wabbajack
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copied from dds.h. Includes (almost) only stuff I need in this project.
|
||||
*
|
||||
* Link: https://github.com/digitalutopia1/BA2Lib/blob/master/BA2Lib/dds.h
|
||||
*
|
||||
*/
|
||||
|
||||
public class DDS
|
||||
{
|
||||
public const int DDS_MAGIC = 0x20534444; // "DDS "
|
||||
|
||||
public static uint MAKEFOURCC(char ch0, char ch1, char ch2, char ch3)
|
||||
{
|
||||
// This is alien to me...
|
||||
return ((uint)(byte)(ch0) | ((uint)(byte)(ch1) << 8) | ((uint)(byte)(ch2) << 16 | ((uint)(byte)(ch3) << 24)));
|
||||
}
|
||||
|
||||
public const int DDS_FOURCC = 0x00000004; // DDPF_FOURCC
|
||||
public const int DDS_RGB = 0x00000040; // DDPF_RGB
|
||||
public const int DDS_RGBA = 0x00000041; // DDPF_RGB | DDPF_ALPHAPIXELS
|
||||
|
||||
public const int DDS_HEADER_FLAGS_TEXTURE = 0x00001007; // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT
|
||||
public const int DDS_HEADER_FLAGS_MIPMAP = 0x00020000; // DDSD_MIPMAPCOUNT
|
||||
public const int DDS_HEADER_FLAGS_LINEARSIZE = 0x00080000; // DDSD_LINEARSIZE
|
||||
|
||||
public const int DDS_SURFACE_FLAGS_TEXTURE = 0x00001000; // DDSCAPS_TEXTURE
|
||||
public const int DDS_SURFACE_FLAGS_MIPMAP = 0x00400008; // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP
|
||||
|
||||
public const int DDS_ALPHA_MODE_UNKNOWN = 0x0;
|
||||
}
|
||||
|
||||
#region dxgiformat.h
|
||||
|
||||
public enum DXGI_FORMAT
|
||||
{
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM = 28,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29,
|
||||
DXGI_FORMAT_R8_UNORM = 61,
|
||||
DXGI_FORMAT_BC1_UNORM = 71,
|
||||
DXGI_FORMAT_BC1_UNORM_SRGB = 72,
|
||||
DXGI_FORMAT_BC2_UNORM = 74,
|
||||
DXGI_FORMAT_BC3_UNORM = 77,
|
||||
DXGI_FORMAT_BC3_UNORM_SRGB = 78,
|
||||
DXGI_FORMAT_BC4_UNORM = 80,
|
||||
DXGI_FORMAT_BC5_UNORM = 83,
|
||||
DXGI_FORMAT_BC5_SNORM = 84,
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM = 87,
|
||||
DXGI_FORMAT_B8G8R8X8_UNORM = 88,
|
||||
DXGI_FORMAT_BC7_UNORM = 98,
|
||||
DXGI_FORMAT_BC7_UNORM_SRGB = 99
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public enum DXT10_RESOURCE_DIMENSION
|
||||
{
|
||||
DIMENSION_TEXTURE1D = 2,
|
||||
DIMENSION_TEXTURE2D = 3,
|
||||
DIMENSION_TEXTURE3D = 4,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct DDS_HEADER
|
||||
{
|
||||
public uint dwSize;
|
||||
public uint dwHeaderFlags;
|
||||
public uint dwHeight;
|
||||
public uint dwWidth;
|
||||
public uint dwPitchOrLinearSize;
|
||||
public uint dwDepth; // only if DDS_HEADER_FLAGS_VOLUME is set in dwHeaderFlags
|
||||
public uint dwMipMapCount;
|
||||
public uint dwReserved1; // [11]
|
||||
public DDS_PIXELFORMAT PixelFormat; // ddspf
|
||||
public uint dwSurfaceFlags;
|
||||
public uint dwCubemapFlags;
|
||||
public uint dwReserved2; // [3]
|
||||
|
||||
public uint GetSize()
|
||||
{
|
||||
// 9 uint + DDS_PIXELFORMAT uints + 2 uint arrays with 14 uints total
|
||||
// each uint 4 bytes each
|
||||
return (9 * 4) + PixelFormat.GetSize() + (14 * 4);
|
||||
}
|
||||
|
||||
public void Write(System.IO.BinaryWriter bw)
|
||||
{
|
||||
bw.Write(dwSize);
|
||||
bw.Write(dwHeaderFlags);
|
||||
bw.Write(dwHeight);
|
||||
bw.Write(dwWidth);
|
||||
bw.Write(dwPitchOrLinearSize);
|
||||
bw.Write(dwDepth);
|
||||
bw.Write(dwMipMapCount);
|
||||
|
||||
// Just write it multiple times, since it's never assigned a value anyway
|
||||
for (int i = 0; i < 11; i++)
|
||||
bw.Write(dwReserved1);
|
||||
|
||||
// DDS_PIXELFORMAT
|
||||
bw.Write(PixelFormat.dwSize);
|
||||
bw.Write(PixelFormat.dwFlags);
|
||||
bw.Write(PixelFormat.dwFourCC);
|
||||
bw.Write(PixelFormat.dwRGBBitCount);
|
||||
bw.Write(PixelFormat.dwRBitMask);
|
||||
bw.Write(PixelFormat.dwGBitMask);
|
||||
bw.Write(PixelFormat.dwBBitMask);
|
||||
bw.Write(PixelFormat.dwABitMask);
|
||||
|
||||
bw.Write(dwSurfaceFlags);
|
||||
bw.Write(dwCubemapFlags);
|
||||
|
||||
// Just write it multiple times, since it's never assigned a value anyway
|
||||
for (int i = 0; i < 3; i++)
|
||||
bw.Write(dwReserved2);
|
||||
}
|
||||
|
||||
public static uint Size
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return (uint)(sizeof(DDS_HEADER) + (sizeof(int) * 10) + (sizeof(int) * 2));
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct DDS_HEADER_DXT10
|
||||
{
|
||||
public uint dxgiFormat;
|
||||
public uint resourceDimension;
|
||||
public uint miscFlag;
|
||||
public uint arraySize;
|
||||
public uint miscFlags2;
|
||||
|
||||
public void Write(System.IO.BinaryWriter bw)
|
||||
{
|
||||
bw.Write(dxgiFormat);
|
||||
bw.Write(resourceDimension);
|
||||
bw.Write(miscFlag);
|
||||
bw.Write(arraySize);
|
||||
bw.Write(miscFlags2);
|
||||
}
|
||||
|
||||
public static uint Size
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return (uint)sizeof(DDS_HEADER_DXT10);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public unsafe struct DDS_PIXELFORMAT
|
||||
{
|
||||
public uint dwSize;
|
||||
public uint dwFlags;
|
||||
public uint dwFourCC;
|
||||
public uint dwRGBBitCount;
|
||||
public uint dwRBitMask;
|
||||
public uint dwGBitMask;
|
||||
public uint dwBBitMask;
|
||||
public uint dwABitMask;
|
||||
|
||||
public DDS_PIXELFORMAT(uint size, uint flags, uint fourCC, uint rgbBitCount, uint rBitMask, uint gBitMask, uint bBitMask, uint aBitMask)
|
||||
{
|
||||
dwSize = size;
|
||||
dwFlags = flags;
|
||||
dwFourCC = fourCC;
|
||||
dwRGBBitCount = rgbBitCount;
|
||||
dwRBitMask = rBitMask;
|
||||
dwGBitMask = gBitMask;
|
||||
dwBBitMask = bBitMask;
|
||||
dwABitMask = aBitMask;
|
||||
}
|
||||
|
||||
public uint GetSize()
|
||||
{
|
||||
// 8 uints, each 4 bytes each
|
||||
return 8 * 4;
|
||||
}
|
||||
}
|
||||
}
|
37
Compression.BSA/IBSAReader.cs
Normal file
37
Compression.BSA/IBSAReader.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Compression.BSA
|
||||
{
|
||||
public interface IBSAReader : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The files defined by the archive
|
||||
/// </summary>
|
||||
IEnumerable<IFile> Files { get; }
|
||||
}
|
||||
|
||||
public interface IFile
|
||||
{
|
||||
/// <summary>
|
||||
/// The path of the file inside the archive
|
||||
/// </summary>
|
||||
string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The uncompressed file size
|
||||
/// </summary>
|
||||
uint Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Copies this entry to the given stream. 100% thread safe, the .bsa will be opened multiple times
|
||||
/// in order to maintain thread-safe access.
|
||||
/// </summary>
|
||||
/// <param name="output"></param>
|
||||
void CopyDataTo(Stream output);
|
||||
}
|
||||
}
|
@ -13,6 +13,10 @@ namespace Wabbajack
|
||||
{
|
||||
public App()
|
||||
{
|
||||
|
||||
Utils.Log($"Wabbajack Build - {ThisAssembly.Git.Sha}");
|
||||
SetupHandlers();
|
||||
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
if (args.Length > 1)
|
||||
{
|
||||
@ -27,5 +31,16 @@ namespace Wabbajack
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void SetupHandlers()
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += AppHandler;
|
||||
}
|
||||
|
||||
private void AppHandler(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Utils.Log("Uncaught error:");
|
||||
Utils.Log(((Exception)e.ExceptionObject).ExceptionToString());
|
||||
}
|
||||
}
|
||||
}
|
@ -365,7 +365,7 @@ namespace Wabbajack
|
||||
var element = SlideShowElements[idx];
|
||||
|
||||
var data = new MemoryStream();
|
||||
using (var stream = new HttpClient().GetStreamSync("asdas" + element.ImageURL))
|
||||
using (var stream = new HttpClient().GetStreamSync(element.ImageURL))
|
||||
stream.CopyTo(data);
|
||||
data.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
|
@ -32,8 +32,6 @@ namespace Wabbajack
|
||||
InitializeComponent();
|
||||
|
||||
var context = new AppState(Dispatcher, "Building");
|
||||
context.LogMsg($"Wabbajack Build - {ThisAssembly.Git.Sha}");
|
||||
SetupHandlers(context);
|
||||
DataContext = context;
|
||||
WorkQueue.Init((id, msg, progress) => context.SetProgress(id, msg, progress),
|
||||
(max, current) => context.SetQueueSize(max, current));
|
||||
@ -80,17 +78,6 @@ namespace Wabbajack
|
||||
}).Start();
|
||||
}
|
||||
|
||||
private void SetupHandlers(AppState state)
|
||||
{
|
||||
_state = state;
|
||||
AppDomain.CurrentDomain.UnhandledException += AppHandler;
|
||||
}
|
||||
|
||||
private void AppHandler(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
_state.LogMsg("Uncaught error:");
|
||||
_state.LogMsg(((Exception) e.ExceptionObject).ExceptionToString());
|
||||
}
|
||||
|
||||
|
||||
internal bool ExitWhenClosing = true;
|
||||
|
Loading…
Reference in New Issue
Block a user