start of bs2 support, can read the 4 .bs2 files I threw at it

This commit is contained in:
Timothy Baldridge 2019-10-06 15:58:36 -06:00
parent ad661871a5
commit 817576d085
11 changed files with 707 additions and 30 deletions

View File

@ -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

View File

@ -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());
}
}
}*/
}
}
}

View 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);
}
}
}
}
}

View 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);
}
}
}

View File

@ -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; }

View File

@ -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
View 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;
}
}
}

View 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);
}
}

View File

@ -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());
}
}
}

View File

@ -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);

View File

@ -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;