diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4e53377b..c95b062b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
### Changelog
-#### Version - 3.6.2.0 - TBD
+#### Version - 3.7.0.0 - TBD
+* Added Starfield support
+ * Note: Hashes were added earlier, but the earlier version was not fully compatible due to Wabbajack extracting the BA2 archives incorrectly. This has been fixed.
* Updated GameFinder dependency
* Updated WebView dependency
* Updated other dependencies
diff --git a/Wabbajack.App.Wpf/Properties/PublishProfiles/FolderProfile.pubxml b/Wabbajack.App.Wpf/Properties/PublishProfiles/FolderProfile.pubxml
new file mode 100644
index 00000000..acb3e335
--- /dev/null
+++ b/Wabbajack.App.Wpf/Properties/PublishProfiles/FolderProfile.pubxml
@@ -0,0 +1,18 @@
+
+
+
+
+ Release
+ Any CPU
+ bin\Release\Publish
+ FileSystem
+ <_TargetId>Folder
+ net8.0-windows
+ win-x64
+ true
+ false
+ false
+
+
\ No newline at end of file
diff --git a/Wabbajack.CLI/Properties/launchSettings.json b/Wabbajack.CLI/Properties/launchSettings.json
new file mode 100644
index 00000000..be163303
--- /dev/null
+++ b/Wabbajack.CLI/Properties/launchSettings.json
@@ -0,0 +1,7 @@
+{
+ "profiles": {
+ "Wabbajack.CLI": {
+ "commandName": "Project"
+ }
+ }
+}
\ No newline at end of file
diff --git a/Wabbajack.Compression.BSA.Test/CompressionTests.cs b/Wabbajack.Compression.BSA.Test/CompressionTests.cs
index ead5ee50..d881891e 100644
--- a/Wabbajack.Compression.BSA.Test/CompressionTests.cs
+++ b/Wabbajack.Compression.BSA.Test/CompressionTests.cs
@@ -55,6 +55,7 @@ public class CompressionTests
{
if (name == "tes4.bsa") return; // not sure why is is failing
+
var reader = await BSADispatch.Open(path);
var dataStates = await reader.Files
@@ -78,7 +79,6 @@ public class CompressionTests
var rebuiltStream = new MemoryStream();
await build.Build(rebuiltStream, CancellationToken.None);
- rebuiltStream.Position = 0;
var reader2 = await BSADispatch.Open(new MemoryStreamFactory(rebuiltStream, path, path.LastModifiedUtc()));
await reader.Files.Zip(reader2.Files)
diff --git a/Wabbajack.Compression.BSA/FO4Archive/Builder.cs b/Wabbajack.Compression.BSA/BA2Archive/Builder.cs
similarity index 88%
rename from Wabbajack.Compression.BSA/FO4Archive/Builder.cs
rename to Wabbajack.Compression.BSA/BA2Archive/Builder.cs
index 7116fdb7..ca13f449 100644
--- a/Wabbajack.Compression.BSA/FO4Archive/Builder.cs
+++ b/Wabbajack.Compression.BSA/BA2Archive/Builder.cs
@@ -10,7 +10,7 @@ using Wabbajack.DTOs.BSA.ArchiveStates;
using Wabbajack.DTOs.BSA.FileStates;
using Wabbajack.Paths.IO;
-namespace Wabbajack.Compression.BSA.FO4Archive;
+namespace Wabbajack.Compression.BSA.BA2Archive;
public class Builder : IBuilder
{
@@ -33,7 +33,7 @@ public class Builder : IBuilder
break;
case BA2EntryType.DX10:
- var resultdx10 = await DX10FileEntryBuilder.Create((BA2DX10File)state, src, _slab, token);
+ var resultdx10 = await DX10FileEntryBuilder.Create((BA2DX10File)state, src, _slab, _state.Compression == 3, token);
lock (_entries)
{
_entries.Add(resultdx10);
@@ -59,6 +59,13 @@ public class Builder : IBuilder
bw.Write((uint) _entries.Count);
var tableOffsetLoc = bw.BaseStream.Position;
bw.Write((ulong) 0);
+ if(_state.Version == 2 || _state.Version == 3)
+ {
+ bw.Write(_state.Unknown1);
+ bw.Write(_state.Unknown2);
+ if (_state.Version == 3)
+ bw.Write(_state.Compression);
+ }
foreach (var entry in _entries) entry.WriteHeader(bw, token);
diff --git a/Wabbajack.Compression.BSA/BA2Archive/ChunkBuilder.cs b/Wabbajack.Compression.BSA/BA2Archive/ChunkBuilder.cs
new file mode 100644
index 00000000..97906795
--- /dev/null
+++ b/Wabbajack.Compression.BSA/BA2Archive/ChunkBuilder.cs
@@ -0,0 +1,93 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using ICSharpCode.SharpZipLib.Zip.Compression;
+using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
+using K4os.Compression.LZ4;
+using K4os.Compression.LZ4.Encoders;
+using Wabbajack.Common;
+using Wabbajack.DTOs.BSA.FileStates;
+using Wabbajack.DTOs.GitHub;
+
+namespace Wabbajack.Compression.BSA.BA2Archive;
+
+public class ChunkBuilder
+{
+ private BA2Chunk _chunk;
+ private Stream _dataSlab;
+ private long _offsetOffset;
+ private uint _packSize;
+
+ public static async Task Create(BA2DX10File state, BA2Chunk chunk, Stream src,
+ DiskSlabAllocator slab, bool useLz4Compression, CancellationToken token)
+ {
+ var builder = new ChunkBuilder {_chunk = chunk};
+
+ if (!chunk.Compressed)
+ {
+ builder._dataSlab = slab.Allocate(chunk.FullSz);
+ await src.CopyToLimitAsync(builder._dataSlab, (int) chunk.FullSz, token);
+ }
+ else
+ {
+ if (!useLz4Compression)
+ {
+ var deflater = new Deflater(Deflater.BEST_COMPRESSION);
+ await using var ms = new MemoryStream();
+ await using (var ds = new DeflaterOutputStream(ms, deflater))
+ {
+ ds.IsStreamOwner = false;
+ await src.CopyToLimitAsync(ds, (int)chunk.FullSz, token);
+ }
+
+ builder._dataSlab = slab.Allocate(ms.Length);
+ ms.Position = 0;
+ await ms.CopyToLimitAsync(builder._dataSlab, (int)ms.Length, token);
+ builder._packSize = (uint)ms.Length;
+ }
+ else
+ {
+ byte[] full = new byte[chunk.FullSz];
+ await using (var copyStream = new MemoryStream())
+ {
+ await src.CopyToLimitAsync(copyStream, (int)chunk.FullSz, token);
+ full = copyStream.ToArray();
+ }
+ var maxOutput = LZ4Codec.MaximumOutputSize((int)chunk.FullSz);
+ byte[] compressed = new byte[maxOutput];
+ int compressedSize = LZ4Codec.Encode(full, 0, full.Length, compressed, 0, compressed.Length, LZ4Level.L12_MAX);
+ var ms = new MemoryStream(compressed, 0, compressedSize);
+ builder._dataSlab = slab.Allocate(compressedSize);
+ ms.Position = 0;
+ await ms.CopyToLimitAsync(builder._dataSlab, compressedSize, token);
+ builder._packSize = (uint)compressedSize;
+ }
+ }
+
+ builder._dataSlab.Position = 0;
+
+ return builder;
+ }
+
+ public void WriteHeader(BinaryWriter bw)
+ {
+ _offsetOffset = bw.BaseStream.Position;
+ bw.Write((ulong) 0);
+ bw.Write(_packSize);
+ bw.Write(_chunk.FullSz);
+ bw.Write(_chunk.StartMip);
+ bw.Write(_chunk.EndMip);
+ bw.Write(_chunk.Align);
+ }
+
+ public async ValueTask WriteData(BinaryWriter bw, CancellationToken token)
+ {
+ var pos = bw.BaseStream.Position;
+ bw.BaseStream.Position = _offsetOffset;
+ bw.Write((ulong) pos);
+ bw.BaseStream.Position = pos;
+ await _dataSlab.CopyToLimitAsync(bw.BaseStream, (int) _dataSlab.Length, token);
+ await _dataSlab.DisposeAsync();
+ }
+}
\ No newline at end of file
diff --git a/Wabbajack.Compression.BSA/BA2Archive/DX10Entry.cs b/Wabbajack.Compression.BSA/BA2Archive/DX10Entry.cs
new file mode 100644
index 00000000..00c88312
--- /dev/null
+++ b/Wabbajack.Compression.BSA/BA2Archive/DX10Entry.cs
@@ -0,0 +1,179 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using DirectXTex;
+using ICSharpCode.SharpZipLib.Zip.Compression;
+using K4os.Compression.LZ4;
+using K4os.Compression.LZ4.Streams;
+using Wabbajack.Common;
+using Wabbajack.Compression.BSA.BA2Archive;
+using Wabbajack.DTOs.BSA.FileStates;
+using Wabbajack.DTOs.Streams;
+using Wabbajack.Paths;
+
+namespace Wabbajack.Compression.BSA.BA2Archive;
+
+public class DX10Entry : IBA2FileEntry
+{
+ private readonly Reader _bsa;
+ private ushort _chunkHdrLen;
+ private List _chunks;
+ private uint _dirHash;
+ private string _extension;
+ private byte _format;
+ private ushort _height;
+ private int _index;
+ private uint _nameHash;
+ private byte _numChunks;
+ private byte _numMips;
+ private ushort _unk16;
+ private byte _unk8;
+ private ushort _width;
+ private readonly byte _isCubemap;
+ private readonly byte _tileMode;
+
+ public DX10Entry(Reader ba2Reader, int idx)
+ {
+ _bsa = ba2Reader;
+ var _rdr = ba2Reader._rdr;
+ _nameHash = _rdr.ReadUInt32();
+ FullPath = _nameHash.ToString("X");
+ _extension = Encoding.UTF8.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();
+ _isCubemap = _rdr.ReadByte();
+ _tileMode = _rdr.ReadByte();
+ _index = idx;
+
+ _chunks = Enumerable.Range(0, _numChunks)
+ .Select(_ => new TextureChunk(_rdr))
+ .ToList();
+ }
+ private DirectXTexUtility.TexMetadata? _metadata = null;
+
+ public DirectXTexUtility.TexMetadata Metadata
+ {
+ get
+ {
+ if (_metadata == null)
+ _metadata = DirectXTexUtility.GenerateMetadata(_width, _height, _numMips, (DirectXTexUtility.DXGIFormat)_format, _isCubemap == 1);
+ return (DirectXTexUtility.TexMetadata)_metadata;
+ }
+ }
+
+ private uint _headerSize = 0;
+ public uint HeaderSize
+ {
+ get
+ {
+ if (_headerSize > 0)
+ return _headerSize;
+ uint size = 0;
+ size += (uint)Marshal.SizeOf(DirectXTexUtility.DDSHeader.DDSMagic);
+ size += (uint)Marshal.SizeOf();
+ var pixelFormat = DirectXTexUtility.GetPixelFormat(Metadata);
+ var hasDx10Header = DirectXTexUtility.HasDx10Header(pixelFormat);
+ if (hasDx10Header)
+ size += (uint)Marshal.SizeOf();
+
+ return _headerSize = size;
+ }
+ }
+
+ public string FullPath { get; set; }
+
+ public RelativePath Path => FullPath.ToRelativePath();
+ public uint Size => (uint)_chunks.Sum(f => f._fullSz) + HeaderSize;
+
+ public AFile State => new BA2DX10File
+ {
+ Path = Path,
+ NameHash = _nameHash,
+ Extension = _extension,
+ DirHash = _dirHash,
+ Unk8 = _unk8,
+ ChunkHdrLen = _chunkHdrLen,
+ Height = _height,
+ Width = _width,
+ NumMips = _numMips,
+ PixelFormat = _format,
+ IsCubeMap = _isCubemap,
+ TileMode = _tileMode,
+ Index = _index,
+ Chunks = _chunks.Select(ch => new BA2Chunk
+ {
+ FullSz = ch._fullSz,
+ StartMip = ch._startMip,
+ EndMip = ch._endMip,
+ Align = ch._align,
+ Compressed = ch._packSz != 0
+ }).ToArray()
+ };
+
+ public async ValueTask CopyDataTo(Stream output, CancellationToken token)
+ {
+ var bw = new BinaryWriter(output);
+
+ WriteHeader(bw);
+
+ await using var fs = await _bsa._streamFactory.GetStream();
+ using var br = new BinaryReader(fs);
+ foreach (var chunk in _chunks)
+ {
+ var full = new byte[chunk._fullSz];
+ var isCompressed = chunk._packSz != 0;
+
+ br.BaseStream.Seek((long)chunk._offset, SeekOrigin.Begin);
+
+ if (!isCompressed)
+ {
+ await br.BaseStream.ReadAsync(full, token);
+ }
+ else
+ {
+ var compressed = new byte[chunk._packSz];
+ await br.BaseStream.ReadAsync(compressed, token);
+ if (_bsa._compression == 3)
+ {
+ LZ4Codec.PartialDecode(compressed, full);
+ }
+ else
+ {
+ var inflater = new Inflater();
+ inflater.SetInput(compressed);
+ inflater.Inflate(full);
+ }
+ }
+
+ await bw.BaseStream.WriteAsync(full, token);
+ }
+ }
+
+
+ public async ValueTask GetStreamFactory(CancellationToken token)
+ {
+ var ms = new MemoryStream();
+ await CopyDataTo(ms, token);
+ ms.Position = 0;
+ return new MemoryStreamFactory(ms, Path, _bsa._streamFactory.LastModifiedUtc);
+ }
+
+ private void WriteHeader(BinaryWriter bw)
+ {
+ DirectXTexUtility.GenerateDDSHeader(Metadata, DirectXTexUtility.DDSFlags.FORCEDX10EXTMISC2, out var header, out var header10);
+ var headerBytes = DirectXTexUtility.EncodeDDSHeader(header, header10);
+ bw.Write(headerBytes);
+ }
+}
+
diff --git a/Wabbajack.Compression.BSA/FO4Archive/DX10FileEntryBuilder.cs b/Wabbajack.Compression.BSA/BA2Archive/DX10FileEntryBuilder.cs
similarity index 62%
rename from Wabbajack.Compression.BSA/FO4Archive/DX10FileEntryBuilder.cs
rename to Wabbajack.Compression.BSA/BA2Archive/DX10FileEntryBuilder.cs
index 86045356..0af601d9 100644
--- a/Wabbajack.Compression.BSA/FO4Archive/DX10FileEntryBuilder.cs
+++ b/Wabbajack.Compression.BSA/BA2Archive/DX10FileEntryBuilder.cs
@@ -1,18 +1,20 @@
+using DirectXTex;
using System.Collections.Generic;
using System.IO;
+using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using Compression.BSA;
using Wabbajack.DTOs.BSA.FileStates;
using Wabbajack.DTOs.Texture;
-namespace Wabbajack.Compression.BSA.FO4Archive;
+namespace Wabbajack.Compression.BSA.BA2Archive;
public class DX10FileEntryBuilder : IFileBuilder
{
private List _chunks;
private BA2DX10File _state;
+ private uint _headerSize = 0;
public uint FileHash => _state.NameHash;
public uint DirHash => _state.DirHash;
@@ -43,20 +45,36 @@ public class DX10FileEntryBuilder : IFileBuilder
foreach (var chunk in _chunks)
await chunk.WriteData(wtr, token);
}
+ public uint GetHeaderSize(BA2DX10File state)
+ {
+ if (_headerSize > 0)
+ return _headerSize;
- public static async Task Create(BA2DX10File state, Stream src, DiskSlabAllocator slab,
+ uint size = 0;
+ size += (uint)Marshal.SizeOf(DirectXTexUtility.DDSHeader.DDSMagic);
+ size += (uint)Marshal.SizeOf();
+ var metadata = DirectXTexUtility.GenerateMetadata(state.Width, state.Height, state.NumMips, (DirectXTexUtility.DXGIFormat)state.PixelFormat, state.IsCubeMap == 1);
+ var pixelFormat = DirectXTexUtility.GetPixelFormat(metadata);
+ var hasDx10Header = DirectXTexUtility.HasDx10Header(pixelFormat);
+ if (hasDx10Header)
+ size += (uint)Marshal.SizeOf();
+
+ return _headerSize = size;
+ }
+
+ public static async Task Create(BA2DX10File state, Stream src, DiskSlabAllocator slab, bool useLz4Compression,
CancellationToken token)
{
var builder = new DX10FileEntryBuilder {_state = state};
- var headerSize = DDS.HeaderSizeForFormat((DXGI_FORMAT) state.PixelFormat) + 4;
+ var headerSize = builder.GetHeaderSize(state);
new BinaryReader(src).ReadBytes((int) headerSize);
// This can't be parallel because it all runs off the same base IO stream.
builder._chunks = new List();
foreach (var chunk in state.Chunks)
- builder._chunks.Add(await ChunkBuilder.Create(state, chunk, src, slab, token));
+ builder._chunks.Add(await ChunkBuilder.Create(state, chunk, src, slab, useLz4Compression, token));
return builder;
}
diff --git a/Wabbajack.Compression.BSA/FO4Archive/FileEntry.cs b/Wabbajack.Compression.BSA/BA2Archive/FileEntry.cs
similarity index 98%
rename from Wabbajack.Compression.BSA/FO4Archive/FileEntry.cs
rename to Wabbajack.Compression.BSA/BA2Archive/FileEntry.cs
index 8b3961b3..7d0edc28 100644
--- a/Wabbajack.Compression.BSA/FO4Archive/FileEntry.cs
+++ b/Wabbajack.Compression.BSA/BA2Archive/FileEntry.cs
@@ -9,7 +9,7 @@ using Wabbajack.DTOs.BSA.FileStates;
using Wabbajack.DTOs.Streams;
using Wabbajack.Paths;
-namespace Wabbajack.Compression.BSA.FO4Archive;
+namespace Wabbajack.Compression.BSA.BA2Archive;
public class FileEntry : IBA2FileEntry
{
diff --git a/Wabbajack.Compression.BSA/FO4Archive/FileEntryBuilder.cs b/Wabbajack.Compression.BSA/BA2Archive/FileEntryBuilder.cs
similarity index 97%
rename from Wabbajack.Compression.BSA/FO4Archive/FileEntryBuilder.cs
rename to Wabbajack.Compression.BSA/BA2Archive/FileEntryBuilder.cs
index c620c624..3d11a0d4 100644
--- a/Wabbajack.Compression.BSA/FO4Archive/FileEntryBuilder.cs
+++ b/Wabbajack.Compression.BSA/BA2Archive/FileEntryBuilder.cs
@@ -6,7 +6,7 @@ using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using Wabbajack.Common;
using Wabbajack.DTOs.BSA.FileStates;
-namespace Wabbajack.Compression.BSA.FO4Archive;
+namespace Wabbajack.Compression.BSA.BA2Archive;
public class FileEntryBuilder : IFileBuilder
{
diff --git a/Wabbajack.Compression.BSA/FO4Archive/IBA2FileEntry.cs b/Wabbajack.Compression.BSA/BA2Archive/IBA2FileEntry.cs
similarity index 71%
rename from Wabbajack.Compression.BSA/FO4Archive/IBA2FileEntry.cs
rename to Wabbajack.Compression.BSA/BA2Archive/IBA2FileEntry.cs
index a89e965e..d9cd8521 100644
--- a/Wabbajack.Compression.BSA/FO4Archive/IBA2FileEntry.cs
+++ b/Wabbajack.Compression.BSA/BA2Archive/IBA2FileEntry.cs
@@ -1,6 +1,6 @@
using Wabbajack.Compression.BSA.Interfaces;
-namespace Wabbajack.Compression.BSA.FO4Archive;
+namespace Wabbajack.Compression.BSA.BA2Archive;
internal interface IBA2FileEntry : IFile
{
diff --git a/Wabbajack.Compression.BSA/FO4Archive/IFileBuilder.cs b/Wabbajack.Compression.BSA/BA2Archive/IFileBuilder.cs
similarity index 87%
rename from Wabbajack.Compression.BSA/FO4Archive/IFileBuilder.cs
rename to Wabbajack.Compression.BSA/BA2Archive/IFileBuilder.cs
index d9f5ffda..cf78a00f 100644
--- a/Wabbajack.Compression.BSA/FO4Archive/IFileBuilder.cs
+++ b/Wabbajack.Compression.BSA/BA2Archive/IFileBuilder.cs
@@ -2,7 +2,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
-namespace Wabbajack.Compression.BSA.FO4Archive;
+namespace Wabbajack.Compression.BSA.BA2Archive;
internal interface IFileBuilder
{
diff --git a/Wabbajack.Compression.BSA/FO4Archive/Reader.cs b/Wabbajack.Compression.BSA/BA2Archive/Reader.cs
similarity index 81%
rename from Wabbajack.Compression.BSA/FO4Archive/Reader.cs
rename to Wabbajack.Compression.BSA/BA2Archive/Reader.cs
index ecbf4dae..686812c5 100644
--- a/Wabbajack.Compression.BSA/FO4Archive/Reader.cs
+++ b/Wabbajack.Compression.BSA/BA2Archive/Reader.cs
@@ -7,7 +7,7 @@ using Wabbajack.Compression.BSA.Interfaces;
using Wabbajack.DTOs.BSA.ArchiveStates;
using Wabbajack.DTOs.Streams;
-namespace Wabbajack.Compression.BSA.FO4Archive;
+namespace Wabbajack.Compression.BSA.BA2Archive;
public class Reader : IReader
{
@@ -15,9 +15,17 @@ public class Reader : IReader
internal string _headerMagic;
internal ulong _nameTableOffset;
internal uint _numFiles;
+ internal uint _unknown1;
+ internal uint _unknown2;
+ internal uint _compression;
internal BinaryReader _rdr;
public IStreamFactory _streamFactory;
internal BA2EntryType _type;
+
+ ///
+ /// Fallout 4 - Version 1, 7 or 8
+ /// Starfield - Version 2 or 3
+ ///
internal uint _version;
private Reader(Stream stream)
@@ -37,7 +45,10 @@ public class Reader : IReader
Version = _version,
HeaderMagic = _headerMagic,
Type = _type,
- HasNameTable = HasNameTable
+ HasNameTable = HasNameTable,
+ Unknown1 = _unknown1,
+ Unknown2 = _unknown2,
+ Compression = _compression,
};
@@ -67,6 +78,10 @@ public class Reader : IReader
_numFiles = _rdr.ReadUInt32();
_nameTableOffset = _rdr.ReadUInt64();
+ _unknown1 = (_version == 2 || _version == 3) ? _rdr.ReadUInt32() : 0;
+ _unknown2 = (_version == 2 || _version == 3) ? _rdr.ReadUInt32() : 0;
+ _compression = (_version == 3) ? _rdr.ReadUInt32() : 0;
+
var files = new List();
for (var idx = 0; idx < _numFiles; idx += 1)
switch (_type)
@@ -94,4 +109,4 @@ public class Reader : IReader
return Task.CompletedTask;
}
-}
\ No newline at end of file
+}
diff --git a/Wabbajack.Compression.BSA/FO4Archive/TextureChunk.cs b/Wabbajack.Compression.BSA/BA2Archive/TextureChunk.cs
similarity index 90%
rename from Wabbajack.Compression.BSA/FO4Archive/TextureChunk.cs
rename to Wabbajack.Compression.BSA/BA2Archive/TextureChunk.cs
index 5003348c..6028ccca 100644
--- a/Wabbajack.Compression.BSA/FO4Archive/TextureChunk.cs
+++ b/Wabbajack.Compression.BSA/BA2Archive/TextureChunk.cs
@@ -1,6 +1,6 @@
using System.IO;
-namespace Wabbajack.Compression.BSA.FO4Archive;
+namespace Wabbajack.Compression.BSA.BA2Archive;
public class TextureChunk
{
diff --git a/Wabbajack.Compression.BSA/BSADispatch.cs b/Wabbajack.Compression.BSA/BSADispatch.cs
index 7e989440..8cff05ac 100644
--- a/Wabbajack.Compression.BSA/BSADispatch.cs
+++ b/Wabbajack.Compression.BSA/BSADispatch.cs
@@ -22,7 +22,7 @@ public static class BSADispatch
{
FileType.TES3 => await Reader.Load(new NativeFileStreamFactory(filename)),
FileType.BSA => await TES5Archive.Reader.Load(new NativeFileStreamFactory(filename)),
- FileType.BA2 => await FO4Archive.Reader.Load(new NativeFileStreamFactory(filename)),
+ FileType.BA2 => await BA2Archive.Reader.Load(new NativeFileStreamFactory(filename)),
_ => throw new InvalidDataException("Filename is not a .bsa or .ba2")
};
}
@@ -34,7 +34,7 @@ public static class BSADispatch
{
FileType.TES3 => await Reader.Load(factory),
FileType.BSA => await TES5Archive.Reader.Load(factory),
- FileType.BA2 => await FO4Archive.Reader.Load(factory),
+ FileType.BA2 => await BA2Archive.Reader.Load(factory),
_ => throw new InvalidDataException("Filename is not a .bsa or .ba2")
};
}
@@ -46,7 +46,7 @@ public static class BSADispatch
{
FileType.TES3 => await Reader.Load(factory),
FileType.BSA => await TES5Archive.Reader.Load(factory),
- FileType.BA2 => await FO4Archive.Reader.Load(factory),
+ FileType.BA2 => await BA2Archive.Reader.Load(factory),
_ => throw new InvalidDataException("Filename is not a .bsa or .ba2")
};
}
@@ -57,7 +57,7 @@ public static class BSADispatch
{
TES3State tes3 => new Builder(tes3),
BSAState bsa => TES5Archive.Builder.Create(bsa, manager),
- BA2State ba2 => FO4Archive.Builder.Create(ba2, manager),
+ BA2State ba2 => BA2Archive.Builder.Create(ba2, manager),
_ => throw new NotImplementedException()
};
}
diff --git a/Wabbajack.Compression.BSA/DDS.cs b/Wabbajack.Compression.BSA/DDS.cs
deleted file mode 100644
index 8a7a4334..00000000
--- a/Wabbajack.Compression.BSA/DDS.cs
+++ /dev/null
@@ -1,215 +0,0 @@
-using System;
-using System.IO;
-using System.Runtime.InteropServices;
-using Wabbajack.DTOs.Texture;
-
-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 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;
-
- public static uint HeaderSizeForFormat(DXGI_FORMAT fmt)
- {
- switch (fmt)
- {
- case DXGI_FORMAT.BC1_UNORM_SRGB:
- case DXGI_FORMAT.BC3_UNORM_SRGB:
- case DXGI_FORMAT.BC4_UNORM:
- case DXGI_FORMAT.BC5_SNORM:
- case DXGI_FORMAT.BC6H_UF16:
- case DXGI_FORMAT.BC7_UNORM:
- case DXGI_FORMAT.BC7_UNORM_SRGB:
- return DDS_HEADER_DXT10.Size + DDS_HEADER.Size;
- default:
- return DDS_HEADER.Size;
- }
- }
-
- public static uint MAKEFOURCC(char ch0, char ch1, char ch2, char ch3)
- {
- // This is alien to me...
- return (byte) ch0 | ((uint) (byte) ch1 << 8) | ((uint) (byte) ch2 << 16) | ((uint) (byte) ch3 << 24);
- }
-}
-
-public enum DXT10_RESOURCE_DIMENSION
-{
- DIMENSION_TEXTURE1D = 2,
- DIMENSION_TEXTURE2D = 3,
- DIMENSION_TEXTURE3D = 4
-}
-
-[Flags]
-public enum DDSCAPS2 : uint
-{
- CUBEMAP = 0x200,
- CUBEMAP_POSITIVEX = 0x400,
- CUBEMAP_NEGATIVEX = 0x800,
- CUBEMAP_POSITIVEY = 0x1000,
- CUBEMAP_NEGATIVEY = 0x2000,
- CUBEMAP_POSITIVEZ = 0x4000,
- CUBEMAP_NEGATIVEZ = 0x8000,
- CUBEMAP_ALLFACES = 0xFC00
-}
-
-[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(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 (var 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 (var 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(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 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;
- }
-}
\ No newline at end of file
diff --git a/Wabbajack.Compression.BSA/DirectXTexUtil.cs b/Wabbajack.Compression.BSA/DirectXTexUtil.cs
new file mode 100644
index 00000000..b1c302fb
--- /dev/null
+++ b/Wabbajack.Compression.BSA/DirectXTexUtil.cs
@@ -0,0 +1,1116 @@
+// ------------------------------------------------------------------------
+// DirectXTex Utility - A simple class for generating DDS Headers
+// Copyright(c) 2018 Philip/Scobalula
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+// ------------------------------------------------------------------------
+// Author: Philip/Scobalula
+// Description: DirectXTex DDS Header Utilities
+// Source: https://gist.github.com/Scobalula/d9474f3fcf3d5a2ca596fceb64e16c98
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.IO;
+
+namespace DirectXTex
+{
+ public class DirectXTexUtility
+ {
+ #region Enumerators
+ ///
+ /// DDS Formats
+ ///
+ public enum DXGIFormat : uint
+ {
+ UNKNOWN = 0,
+ R32G32B32A32TYPELESS = 1,
+ R32G32B32A32FLOAT = 2,
+ R32G32B32A32UINT = 3,
+ R32G32B32A32SINT = 4,
+ R32G32B32TYPELESS = 5,
+ R32G32B32FLOAT = 6,
+ R32G32B32UINT = 7,
+ R32G32B32SINT = 8,
+ R16G16B16A16TYPELESS = 9,
+ R16G16B16A16FLOAT = 10,
+ R16G16B16A16UNORM = 11,
+ R16G16B16A16UINT = 12,
+ R16G16B16A16SNORM = 13,
+ R16G16B16A16SINT = 14,
+ R32G32TYPELESS = 15,
+ R32G32FLOAT = 16,
+ R32G32UINT = 17,
+ R32G32SINT = 18,
+ R32G8X24TYPELESS = 19,
+ D32FLOATS8X24UINT = 20,
+ R32FLOATX8X24TYPELESS = 21,
+ X32TYPELESSG8X24UINT = 22,
+ R10G10B10A2TYPELESS = 23,
+ R10G10B10A2UNORM = 24,
+ R10G10B10A2UINT = 25,
+ R11G11B10FLOAT = 26,
+ R8G8B8A8TYPELESS = 27,
+ R8G8B8A8UNORM = 28,
+ R8G8B8A8UNORMSRGB = 29,
+ R8G8B8A8UINT = 30,
+ R8G8B8A8SNORM = 31,
+ R8G8B8A8SINT = 32,
+ R16G16TYPELESS = 33,
+ R16G16FLOAT = 34,
+ R16G16UNORM = 35,
+ R16G16UINT = 36,
+ R16G16SNORM = 37,
+ R16G16SINT = 38,
+ R32TYPELESS = 39,
+ D32FLOAT = 40,
+ R32FLOAT = 41,
+ R32UINT = 42,
+ R32SINT = 43,
+ R24G8TYPELESS = 44,
+ D24UNORMS8UINT = 45,
+ R24UNORMX8TYPELESS = 46,
+ X24TYPELESSG8UINT = 47,
+ R8G8TYPELESS = 48,
+ R8G8UNORM = 49,
+ R8G8UINT = 50,
+ R8G8SNORM = 51,
+ R8G8SINT = 52,
+ R16TYPELESS = 53,
+ R16FLOAT = 54,
+ D16UNORM = 55,
+ R16UNORM = 56,
+ R16UINT = 57,
+ R16SNORM = 58,
+ R16SINT = 59,
+ R8TYPELESS = 60,
+ R8UNORM = 61,
+ R8UINT = 62,
+ R8SNORM = 63,
+ R8SINT = 64,
+ A8UNORM = 65,
+ R1UNORM = 66,
+ R9G9B9E5SHAREDEXP = 67,
+ R8G8B8G8UNORM = 68,
+ G8R8G8B8UNORM = 69,
+ BC1TYPELESS = 70,
+ BC1UNORM = 71,
+ BC1UNORMSRGB = 72,
+ BC2TYPELESS = 73,
+ BC2UNORM = 74,
+ BC2UNORMSRGB = 75,
+ BC3TYPELESS = 76,
+ BC3UNORM = 77,
+ BC3UNORMSRGB = 78,
+ BC4TYPELESS = 79,
+ BC4UNORM = 80,
+ BC4SNORM = 81,
+ BC5TYPELESS = 82,
+ BC5UNORM = 83,
+ BC5SNORM = 84,
+ B5G6R5UNORM = 85,
+ B5G5R5A1UNORM = 86,
+ B8G8R8A8UNORM = 87,
+ B8G8R8X8UNORM = 88,
+ R10G10B10XRBIASA2UNORM = 89,
+ B8G8R8A8TYPELESS = 90,
+ B8G8R8A8UNORMSRGB = 91,
+ B8G8R8X8TYPELESS = 92,
+ B8G8R8X8UNORMSRGB = 93,
+ BC6HTYPELESS = 94,
+ BC6HUF16 = 95,
+ BC6HSF16 = 96,
+ BC7TYPELESS = 97,
+ BC7UNORM = 98,
+ BC7UNORMSRGB = 99,
+ AYUV = 100,
+ Y410 = 101,
+ Y416 = 102,
+ NV12 = 103,
+ P010 = 104,
+ P016 = 105,
+ OPAQUE420 = 106,
+ YUY2 = 107,
+ Y210 = 108,
+ Y216 = 109,
+ NV11 = 110,
+ AI44 = 111,
+ IA44 = 112,
+ P8 = 113,
+ A8P8 = 114,
+ B4G4R4A4UNORM = 115,
+ FORCEUINT = 0xffffffff
+ }
+
+ ///
+ /// DDS Flags
+ ///
+ [Flags]
+ public enum DDSFlags
+ {
+ NONE = 0x0,
+ LEGACYDWORD = 0x1,
+ NOLEGACYEXPANSION = 0x2,
+ NOR10B10G10A2FIXUP = 0x4,
+ FORCERGB = 0x8,
+ NO16BPP = 0x10,
+ EXPANDLUMINANCE = 0x20,
+ BADDXTNTAILS = 0x40,
+ FORCEDX10EXT = 0x10000,
+ FORCEDX10EXTMISC2 = 0x20000,
+ }
+
+ ///
+ /// Texture Dimension
+ ///
+ public enum TexDimension
+ {
+ TEXTURE1D = 2,
+ TEXTURE2D = 3,
+ TEXTURE3D = 4,
+ }
+
+ ///
+ /// Misc. Texture Flags
+ ///
+ public enum TexMiscFlags : uint
+ {
+ TEXTURECUBE = 0x4,
+ };
+
+ ///
+ /// Misc. Texture Flags
+ ///
+ public enum TexMiscFlags2 : uint
+ {
+ TEXMISC2ALPHAMODEMASK = 0x7,
+ };
+
+ ///
+ /// Texture Alpha Modes
+ ///
+ public enum TexAlphaMode
+ {
+ UNKNOWN = 0,
+ STRAIGHT = 1,
+ PREMULTIPLIED = 2,
+ OPAQUE = 3,
+ CUSTOM = 4,
+ };
+
+ ///
+ /// CP Flags
+ ///
+ [Flags]
+ public enum CPFLAGS
+ {
+ NONE = 0x0, // Normal operation
+ LEGACYDWORD = 0x1, // Assume pitch is DWORD aligned instead of BYTE aligned
+ PARAGRAPH = 0x2, // Assume pitch is 16-byte aligned instead of BYTE aligned
+ YMM = 0x4, // Assume pitch is 32-byte aligned instead of BYTE aligned
+ ZMM = 0x8, // Assume pitch is 64-byte aligned instead of BYTE aligned
+ PAGE4K = 0x200, // Assume pitch is 4096-byte aligned instead of BYTE aligned
+ BADDXTNTAILS = 0x1000, // BC formats with malformed mipchain blocks smaller than 4x4
+ BPP24 = 0x10000, // Override with a legacy 24 bits-per-pixel format size
+ BPP16 = 0x20000, // Override with a legacy 16 bits-per-pixel format size
+ BPP8 = 0x40000, // Override with a legacy 8 bits-per-pixel format size
+ };
+ #endregion
+
+ #region Structs/Classes
+ ///
+ /// Common Pixel Formats
+ ///
+ public class PixelFormats
+ {
+ ///
+ /// DDS Pixel Format Size
+ ///
+ public static readonly uint Size = (uint)Marshal.SizeOf();
+
+ #region PixelFormatsConstants
+ public const uint DDSFOURCC = 0x00000004; // DDPFFOURCC
+ public const uint DDSRGB = 0x00000040; // DDPFRGB
+ public const uint DDSRGBA = 0x00000041; // DDPFRGB | DDPFALPHAPIXELS
+ public const uint DDSLUMINANCE = 0x00020000; // DDPFLUMINANCE
+ public const uint DDSLUMINANCEA = 0x00020001; // DDPFLUMINANCE | DDPFALPHAPIXELS
+ public const uint DDSALPHAPIXELS = 0x00000001; // DDPFALPHAPIXELS
+ public const uint DDSALPHA = 0x00000002; // DDPFALPHA
+ public const uint DDSPAL8 = 0x00000020; // DDPFPALETTEINDEXED8
+ public const uint DDSPAL8A = 0x00000021; // DDPFPALETTEINDEXED8 | DDPFALPHAPIXELS
+ public const uint DDSBUMPDUDV = 0x00080000; // DDPFBUMPDUDV
+ #endregion
+
+ #region DDSPixelFormats
+ public static DDSHeader.DDSPixelFormat DXT1 =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('D', 'X', 'T', '1'), 0, 0, 0, 0, 0);
+
+ public static DDSHeader.DDSPixelFormat DXT2 =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('D', 'X', 'T', '2'), 0, 0, 0, 0, 0);
+
+ public static DDSHeader.DDSPixelFormat DXT3 =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('D', 'X', 'T', '3'), 0, 0, 0, 0, 0);
+
+ public static DDSHeader.DDSPixelFormat DXT4 =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('D', 'X', 'T', '4'), 0, 0, 0, 0, 0);
+
+ public static DDSHeader.DDSPixelFormat DXT5 =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('D', 'X', 'T', '5'), 0, 0, 0, 0, 0);
+
+ public static DDSHeader.DDSPixelFormat BC4UNORM =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('B', 'C', '4', 'U'), 0, 0, 0, 0, 0);
+
+ public static DDSHeader.DDSPixelFormat BC4SNORM =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('B', 'C', '4', 'S'), 0, 0, 0, 0, 0);
+
+ public static DDSHeader.DDSPixelFormat BC5UNORM =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('B', 'C', '5', 'U'), 0, 0, 0, 0, 0);
+
+ public static DDSHeader.DDSPixelFormat BC5SNORM =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('B', 'C', '5', 'S'), 0, 0, 0, 0, 0);
+
+ public static DDSHeader.DDSPixelFormat R8G8B8G8 =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('R', 'G', 'B', 'G'), 0, 0, 0, 0, 0);
+
+ public static DDSHeader.DDSPixelFormat G8R8G8B8 =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('G', 'R', 'G', 'B'), 0, 0, 0, 0, 0);
+
+ public static DDSHeader.DDSPixelFormat YUY2 =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('Y', 'U', 'Y', '2'), 0, 0, 0, 0, 0);
+
+ public static DDSHeader.DDSPixelFormat A8R8G8B8 =
+ new DDSHeader.DDSPixelFormat(Size, DDSRGBA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
+
+ public static DDSHeader.DDSPixelFormat X8R8G8B8 =
+ new DDSHeader.DDSPixelFormat(Size, DDSRGB, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000);
+
+ public static DDSHeader.DDSPixelFormat A8B8G8R8 =
+ new DDSHeader.DDSPixelFormat(Size, DDSRGBA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+
+ public static DDSHeader.DDSPixelFormat X8B8G8R8 =
+ new DDSHeader.DDSPixelFormat(Size, DDSRGB, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000);
+
+ public static DDSHeader.DDSPixelFormat G16R16 =
+ new DDSHeader.DDSPixelFormat(Size, DDSRGB, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000);
+
+ public static DDSHeader.DDSPixelFormat R5G6B5 =
+ new DDSHeader.DDSPixelFormat(Size, DDSRGB, 0, 16, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000);
+
+ public static DDSHeader.DDSPixelFormat A1R5G5B5 =
+ new DDSHeader.DDSPixelFormat(Size, DDSRGBA, 0, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000);
+
+ public static DDSHeader.DDSPixelFormat A4R4G4B4 =
+ new DDSHeader.DDSPixelFormat(Size, DDSRGBA, 0, 16, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000);
+
+ public static DDSHeader.DDSPixelFormat R8G8B8 =
+ new DDSHeader.DDSPixelFormat(Size, DDSRGB, 0, 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000);
+
+ public static DDSHeader.DDSPixelFormat L8 =
+ new DDSHeader.DDSPixelFormat(Size, DDSLUMINANCE, 0, 8, 0xff, 0x00, 0x00, 0x00);
+
+ public static DDSHeader.DDSPixelFormat L16 =
+ new DDSHeader.DDSPixelFormat(Size, DDSLUMINANCE, 0, 16, 0xffff, 0x0000, 0x0000, 0x0000);
+
+ public static DDSHeader.DDSPixelFormat A8L8 =
+ new DDSHeader.DDSPixelFormat(Size, DDSLUMINANCEA, 0, 16, 0x00ff, 0x0000, 0x0000, 0xff00);
+
+ public static DDSHeader.DDSPixelFormat A8L8ALT =
+ new DDSHeader.DDSPixelFormat(Size, DDSLUMINANCEA, 0, 8, 0x00ff, 0x0000, 0x0000, 0xff00);
+
+ public static DDSHeader.DDSPixelFormat A8 =
+ new DDSHeader.DDSPixelFormat(Size, DDSALPHA, 0, 8, 0x00, 0x00, 0x00, 0xff);
+
+ public static DDSHeader.DDSPixelFormat V8U8 =
+ new DDSHeader.DDSPixelFormat(Size, DDSBUMPDUDV, 0, 16, 0x00ff, 0xff00, 0x0000, 0x0000);
+
+ public static DDSHeader.DDSPixelFormat Q8W8V8U8 =
+ new DDSHeader.DDSPixelFormat(Size, DDSBUMPDUDV, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+
+ public static DDSHeader.DDSPixelFormat V16U16 =
+ new DDSHeader.DDSPixelFormat(Size, DDSBUMPDUDV, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000);
+
+ public static DDSHeader.DDSPixelFormat DX10 =
+ new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('D', 'X', '1', '0'), 0, 0, 0, 0, 0);
+ #endregion
+ }
+
+ ///
+ /// DDS Header
+ ///
+ public struct DDSHeader
+ {
+ ///
+ /// DDS Header Flags
+ ///
+ [Flags]
+ public enum HeaderFlags : uint
+ {
+ TEXTURE = 0x00001007, // DDSDCAPS | DDSDHEIGHT | DDSDWIDTH | DDSDPIXELFORMAT
+ MIPMAP = 0x00020000, // DDSDMIPMAPCOUNT
+ VOLUME = 0x00800000, // DDSDDEPTH
+ PITCH = 0x00000008, // DDSDPITCH
+ LINEARSIZE = 0x00080000, // DDSDLINEARSIZE
+ }
+
+ ///
+ /// DDS Surface Flags
+ ///
+ public enum SurfaceFlags : uint
+ {
+ TEXTURE = 0x00001000, // DDSCAPSTEXTURE
+ MIPMAP = 0x00400008, // DDSCAPSCOMPLEX | DDSCAPSMIPMAP
+ CUBEMAP = 0x00000008, // DDSCAPSCOMPLEX
+ }
+
+ ///
+ /// DDS Magic/Four CC
+ ///
+ public const uint DDSMagic = 0x20534444;
+
+ ///
+ /// DDS Pixel Format
+ ///
+ public struct DDSPixelFormat
+ {
+ public uint Size;
+ public uint Flags;
+ public uint FourCC;
+ public uint RGBBitCount;
+ public uint RBitMask;
+ public uint GBitMask;
+ public uint BBitMask;
+ public uint ABitMask;
+
+ ///
+ /// Creates a new DDS Pixel Format
+ ///
+ public DDSPixelFormat(uint size, uint flags, uint fourCC, uint rgbBitCount, uint rBitMask, uint gBitMask, uint bBitMask, uint aBitMask)
+ {
+ Size = size;
+ Flags = flags;
+ FourCC = fourCC;
+ RGBBitCount = rgbBitCount;
+ RBitMask = rBitMask;
+ GBitMask = gBitMask;
+ BBitMask = bBitMask;
+ ABitMask = aBitMask;
+ }
+
+ }
+
+ public uint Size;
+ public HeaderFlags Flags;
+ public uint Height;
+ public uint Width;
+ public uint PitchOrLinearSize;
+ public uint Depth; // only if DDSHEADERFLAGSVOLUME is set in flags
+ public uint MipMapCount;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
+ public uint[] Reserved1;
+ public DDSPixelFormat PixelFormat;
+ public uint Caps;
+ public uint Caps2;
+ public uint Caps3;
+ public uint Caps4;
+ public uint Reserved2;
+ }
+
+ ///
+ /// DDS DX10 Header
+ ///
+ public struct DX10Header
+ {
+ public DXGIFormat Format;
+ public TexDimension ResourceDimension;
+ public TexMiscFlags MiscFlag; // see D3D11RESOURCEMISCFLAG
+ public uint ArraySize;
+ public uint MiscFlags2; // see DDSMISCFLAGS2
+ }
+
+ ///
+ /// Texture Metadata
+ ///
+ public struct TexMetadata
+ {
+ #region Properties
+ public long Width;
+ public long Height; // Should be 1 for 1D textures
+ public long Depth; // Should be 1 for 1D or 2D textures
+ public long ArraySize; // For cubemap, this is a multiple of 6
+ public long MipLevels;
+ public TexMiscFlags MiscFlags;
+ public TexMiscFlags2 MiscFlags2;
+ public DXGIFormat Format;
+ public TexDimension Dimension;
+ #endregion
+
+ ///
+ /// Creates a new Texture Metadata Structe
+ ///
+ public TexMetadata(long width, long height, long depth, long arraySize, long mipLevels, TexMiscFlags flags, TexMiscFlags2 flags2, DXGIFormat format, TexDimension dimension)
+ {
+ Width = width;
+ Height = height;
+ Depth = depth;
+ ArraySize = arraySize;
+ MipLevels = mipLevels;
+ MiscFlags = flags;
+ MiscFlags2 = flags2;
+ Format = format;
+ Dimension = dimension;
+ }
+
+ ///
+ /// Checks Alpha Mode
+ ///
+ public bool IsPMAlpha()
+ {
+ return (TexAlphaMode)(MiscFlags2 & TexMiscFlags2.TEXMISC2ALPHAMODEMASK) == TexAlphaMode.PREMULTIPLIED;
+ }
+
+ public bool IsCubeMap()
+ {
+ return (MiscFlags & TexMiscFlags.TEXTURECUBE) == TexMiscFlags.TEXTURECUBE;
+ }
+ }
+ #endregion
+
+ #region HelperMethods
+ ///
+ /// Clamps Value to a range.
+ ///
+ /// Value to Clamp
+ /// Max value
+ /// Min value
+ /// Clamped Value
+ private static T Clamp(T value, T max, T min) where T : IComparable
+ {
+ return value.CompareTo(min) < 0 ? min : value.CompareTo(max) > 0 ? max : value;
+ }
+
+ ///
+ /// Converts a Struct to a Byte array
+ ///
+ private static byte[] StructToBytes(T value)
+ {
+ // Size of Struct
+ int length = Marshal.SizeOf();
+ // Destination
+ byte[] destination = new byte[length];
+ // Get Pointer
+ IntPtr pointer = Marshal.AllocHGlobal(length);
+ // Convert it
+ Marshal.StructureToPtr(value, pointer, false);
+ Marshal.Copy(pointer, destination, 0, length);
+ Marshal.FreeHGlobal(pointer);
+ // Done
+ return destination;
+ }
+
+ ///
+ /// Generates a FourCC Integer from Pixel Format Characters
+ ///
+ private static uint MakePixelFormatFourCC(char char1, char char2, char char3, char char4)
+ {
+ return Convert.ToByte(char1) | (uint)Convert.ToByte(char2) << 8 | (uint)Convert.ToByte(char3) << 16 | (uint)Convert.ToByte(char4) << 24;
+ }
+
+ ///
+ /// Gets the Bits Per Pixel for the given format
+ ///
+ private static ulong BitsPerPixel(DXGIFormat format)
+ {
+ switch (format)
+ {
+ case DXGIFormat.R32G32B32A32TYPELESS:
+ case DXGIFormat.R32G32B32A32FLOAT:
+ case DXGIFormat.R32G32B32A32UINT:
+ case DXGIFormat.R32G32B32A32SINT:
+ return 128;
+ case DXGIFormat.R32G32B32TYPELESS:
+ case DXGIFormat.R32G32B32FLOAT:
+ case DXGIFormat.R32G32B32UINT:
+ case DXGIFormat.R32G32B32SINT:
+ return 96;
+ case DXGIFormat.R16G16B16A16TYPELESS:
+ case DXGIFormat.R16G16B16A16FLOAT:
+ case DXGIFormat.R16G16B16A16UNORM:
+ case DXGIFormat.R16G16B16A16UINT:
+ case DXGIFormat.R16G16B16A16SNORM:
+ case DXGIFormat.R16G16B16A16SINT:
+ case DXGIFormat.R32G32TYPELESS:
+ case DXGIFormat.R32G32FLOAT:
+ case DXGIFormat.R32G32UINT:
+ case DXGIFormat.R32G32SINT:
+ case DXGIFormat.R32G8X24TYPELESS:
+ case DXGIFormat.D32FLOATS8X24UINT:
+ case DXGIFormat.R32FLOATX8X24TYPELESS:
+ case DXGIFormat.X32TYPELESSG8X24UINT:
+ case DXGIFormat.Y416:
+ case DXGIFormat.Y210:
+ case DXGIFormat.Y216:
+ return 64;
+ case DXGIFormat.R10G10B10A2TYPELESS:
+ case DXGIFormat.R10G10B10A2UNORM:
+ case DXGIFormat.R10G10B10A2UINT:
+ case DXGIFormat.R11G11B10FLOAT:
+ case DXGIFormat.R8G8B8A8TYPELESS:
+ case DXGIFormat.R8G8B8A8UNORM:
+ case DXGIFormat.R8G8B8A8UNORMSRGB:
+ case DXGIFormat.R8G8B8A8UINT:
+ case DXGIFormat.R8G8B8A8SNORM:
+ case DXGIFormat.R8G8B8A8SINT:
+ case DXGIFormat.R16G16TYPELESS:
+ case DXGIFormat.R16G16FLOAT:
+ case DXGIFormat.R16G16UNORM:
+ case DXGIFormat.R16G16UINT:
+ case DXGIFormat.R16G16SNORM:
+ case DXGIFormat.R16G16SINT:
+ case DXGIFormat.R32TYPELESS:
+ case DXGIFormat.D32FLOAT:
+ case DXGIFormat.R32FLOAT:
+ case DXGIFormat.R32UINT:
+ case DXGIFormat.R32SINT:
+ case DXGIFormat.R24G8TYPELESS:
+ case DXGIFormat.D24UNORMS8UINT:
+ case DXGIFormat.R24UNORMX8TYPELESS:
+ case DXGIFormat.X24TYPELESSG8UINT:
+ case DXGIFormat.R9G9B9E5SHAREDEXP:
+ case DXGIFormat.R8G8B8G8UNORM:
+ case DXGIFormat.G8R8G8B8UNORM:
+ case DXGIFormat.B8G8R8A8UNORM:
+ case DXGIFormat.B8G8R8X8UNORM:
+ case DXGIFormat.R10G10B10XRBIASA2UNORM:
+ case DXGIFormat.B8G8R8A8TYPELESS:
+ case DXGIFormat.B8G8R8A8UNORMSRGB:
+ case DXGIFormat.B8G8R8X8TYPELESS:
+ case DXGIFormat.B8G8R8X8UNORMSRGB:
+ case DXGIFormat.AYUV:
+ case DXGIFormat.Y410:
+ case DXGIFormat.YUY2:
+ return 32;
+ case DXGIFormat.P010:
+ case DXGIFormat.P016:
+ return 24;
+ case DXGIFormat.R8G8TYPELESS:
+ case DXGIFormat.R8G8UNORM:
+ case DXGIFormat.R8G8UINT:
+ case DXGIFormat.R8G8SNORM:
+ case DXGIFormat.R8G8SINT:
+ case DXGIFormat.R16TYPELESS:
+ case DXGIFormat.R16FLOAT:
+ case DXGIFormat.D16UNORM:
+ case DXGIFormat.R16UNORM:
+ case DXGIFormat.R16UINT:
+ case DXGIFormat.R16SNORM:
+ case DXGIFormat.R16SINT:
+ case DXGIFormat.B5G6R5UNORM:
+ case DXGIFormat.B5G5R5A1UNORM:
+ case DXGIFormat.A8P8:
+ case DXGIFormat.B4G4R4A4UNORM:
+ return 16;
+ case DXGIFormat.NV12:
+ case DXGIFormat.OPAQUE420:
+ case DXGIFormat.NV11:
+ return 12;
+ case DXGIFormat.R8TYPELESS:
+ case DXGIFormat.R8UNORM:
+ case DXGIFormat.R8UINT:
+ case DXGIFormat.R8SNORM:
+ case DXGIFormat.R8SINT:
+ case DXGIFormat.A8UNORM:
+ case DXGIFormat.BC2TYPELESS:
+ case DXGIFormat.BC2UNORM:
+ case DXGIFormat.BC2UNORMSRGB:
+ case DXGIFormat.BC3TYPELESS:
+ case DXGIFormat.BC3UNORM:
+ case DXGIFormat.BC3UNORMSRGB:
+ case DXGIFormat.BC5TYPELESS:
+ case DXGIFormat.BC5UNORM:
+ case DXGIFormat.BC5SNORM:
+ case DXGIFormat.BC6HTYPELESS:
+ case DXGIFormat.BC6HUF16:
+ case DXGIFormat.BC6HSF16:
+ case DXGIFormat.BC7TYPELESS:
+ case DXGIFormat.BC7UNORM:
+ case DXGIFormat.BC7UNORMSRGB:
+ case DXGIFormat.AI44:
+ case DXGIFormat.IA44:
+ case DXGIFormat.P8:
+ return 8;
+ case DXGIFormat.R1UNORM:
+ return 1;
+ case DXGIFormat.BC1TYPELESS:
+ case DXGIFormat.BC1UNORM:
+ case DXGIFormat.BC1UNORMSRGB:
+ case DXGIFormat.BC4TYPELESS:
+ case DXGIFormat.BC4UNORM:
+ case DXGIFormat.BC4SNORM:
+ return 4;
+ default:
+ return 0;
+ }
+ }
+
+ ///
+ /// Computes Row and Slice Pitch
+ ///
+ private static void ComputePitch(DXGIFormat format, uint width, uint height, out ulong rowPitch, out ulong slicePitch, CPFLAGS flags)
+ {
+ switch (format)
+ {
+ case DXGIFormat.BC1TYPELESS:
+ case DXGIFormat.BC1UNORM:
+ case DXGIFormat.BC1UNORMSRGB:
+ case DXGIFormat.BC4TYPELESS:
+ case DXGIFormat.BC4UNORM:
+ case DXGIFormat.BC4SNORM:
+ {
+ if (flags.HasFlag(CPFLAGS.BADDXTNTAILS))
+ {
+ var nbw = width >> 2;
+ var nbh = height >> 2;
+ rowPitch = Clamp((ulong) nbw * 8u, ulong.MaxValue, 1u);
+ slicePitch = Clamp(rowPitch * nbh, ulong.MaxValue, 1u);
+ }
+ else
+ {
+ var nbw = Clamp(((ulong) width + 3u) / 4u, ulong.MaxValue, 1u);
+ var nbh = Clamp(((ulong) height + 3u) / 4u, ulong.MaxValue, 1u);
+ rowPitch = nbw * 8u;
+ slicePitch = rowPitch * nbh;
+ }
+ }
+ break;
+ case DXGIFormat.BC2TYPELESS:
+ case DXGIFormat.BC2UNORM:
+ case DXGIFormat.BC2UNORMSRGB:
+ case DXGIFormat.BC3TYPELESS:
+ case DXGIFormat.BC3UNORM:
+ case DXGIFormat.BC3UNORMSRGB:
+ case DXGIFormat.BC5TYPELESS:
+ case DXGIFormat.BC5UNORM:
+ case DXGIFormat.BC5SNORM:
+ case DXGIFormat.BC6HTYPELESS:
+ case DXGIFormat.BC6HUF16:
+ case DXGIFormat.BC6HSF16:
+ case DXGIFormat.BC7TYPELESS:
+ case DXGIFormat.BC7UNORM:
+ case DXGIFormat.BC7UNORMSRGB:
+ {
+ if (flags.HasFlag(CPFLAGS.BADDXTNTAILS))
+ {
+ var nbw = width >> 2;
+ var nbh = height >> 2;
+ rowPitch = Clamp((ulong) nbw * 16u, ulong.MaxValue, 1u);
+ slicePitch = Clamp(rowPitch * nbh, ulong.MaxValue, 1u);
+ }
+ else
+ {
+ var nbw = Clamp((width + 3) / 4, ulong.MaxValue, 1u);
+ var nbh = Clamp((height + 3) / 4, ulong.MaxValue, 1u);
+ rowPitch = nbw * 16u;
+ slicePitch = rowPitch * nbh;
+ }
+ }
+ break;
+ case DXGIFormat.R8G8B8G8UNORM:
+ case DXGIFormat.G8R8G8B8UNORM:
+ case DXGIFormat.YUY2:
+ rowPitch = ((width + 1) >> 1) * 4;
+ slicePitch = rowPitch * height;
+ break;
+ case DXGIFormat.Y210:
+ case DXGIFormat.Y216:
+ rowPitch = ((width + 1) >> 1) * 8;
+ slicePitch = rowPitch * height;
+ break;
+
+ case DXGIFormat.NV12:
+ case DXGIFormat.OPAQUE420:
+ rowPitch = ((width + 1) >> 1) * 2;
+ slicePitch = rowPitch * (height + ((height + 1) >> 1));
+ break;
+
+ case DXGIFormat.P010:
+ case DXGIFormat.P016:
+ rowPitch = ((width + 1) >> 1) * 4;
+ slicePitch = rowPitch * (height + ((height + 1) >> 1));
+ break;
+ case DXGIFormat.NV11:
+ rowPitch = ((width + 3) >> 2) * 4;
+ slicePitch = rowPitch * height * 2;
+ break;
+ default:
+ {
+ ulong bpp;
+
+ if (flags.HasFlag(CPFLAGS.BPP24))
+ bpp = 24;
+ else if (flags.HasFlag(CPFLAGS.BPP16))
+ bpp = 16;
+ else if (flags.HasFlag(CPFLAGS.BPP8))
+ bpp = 8;
+ else
+ bpp = BitsPerPixel(format);
+
+ if (flags.HasFlag(CPFLAGS.LEGACYDWORD | CPFLAGS.PARAGRAPH | CPFLAGS.YMM | CPFLAGS.ZMM | CPFLAGS.PAGE4K))
+ {
+ if (flags.HasFlag(CPFLAGS.PAGE4K))
+ {
+ rowPitch = (width * bpp + 32767u) / 32768u * 4096u;
+ slicePitch = rowPitch * height;
+ }
+ else if (flags.HasFlag(CPFLAGS.ZMM))
+ {
+ rowPitch = (width * bpp + 511u) / 512u * 64u;
+ slicePitch = rowPitch * height;
+ }
+ else if (flags.HasFlag(CPFLAGS.YMM))
+ {
+ rowPitch = (width * bpp + 255u) / 256u * 32u;
+ slicePitch = rowPitch * height;
+ }
+ else if (flags.HasFlag(CPFLAGS.PARAGRAPH))
+ {
+ rowPitch = (width * bpp + 127u) / 128u * 16u;
+ slicePitch = rowPitch * height;
+ }
+ else // DWORD alignment
+ {
+ // Special computation for some incorrectly created DDS files based on
+ // legacy DirectDraw assumptions about pitch alignment
+ rowPitch = (width * bpp + 31u) / 32u * sizeof(uint);
+ slicePitch = rowPitch * height;
+ }
+ }
+ else
+ {
+ // Default byte alignment
+ rowPitch = (width * bpp + 7u) / 8u;
+ slicePitch = rowPitch * height;
+ }
+ }
+ break;
+ }
+ }
+
+ ///
+ /// Checks is the given format compressed
+ ///
+ private static bool IsCompressed(DXGIFormat format)
+ {
+ switch (format)
+ {
+ case DXGIFormat.BC1TYPELESS:
+ case DXGIFormat.BC1UNORM:
+ case DXGIFormat.BC1UNORMSRGB:
+ case DXGIFormat.BC2TYPELESS:
+ case DXGIFormat.BC2UNORM:
+ case DXGIFormat.BC2UNORMSRGB:
+ case DXGIFormat.BC3TYPELESS:
+ case DXGIFormat.BC3UNORM:
+ case DXGIFormat.BC3UNORMSRGB:
+ case DXGIFormat.BC4TYPELESS:
+ case DXGIFormat.BC4UNORM:
+ case DXGIFormat.BC4SNORM:
+ case DXGIFormat.BC5TYPELESS:
+ case DXGIFormat.BC5UNORM:
+ case DXGIFormat.BC5SNORM:
+ case DXGIFormat.BC6HTYPELESS:
+ case DXGIFormat.BC6HUF16:
+ case DXGIFormat.BC6HSF16:
+ case DXGIFormat.BC7TYPELESS:
+ case DXGIFormat.BC7UNORM:
+ case DXGIFormat.BC7UNORMSRGB:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ #endregion
+
+ #region MainMethods
+ ///
+ /// Encodes the DDS Header and if DX10, the DX10 Header
+ ///
+ /// DDS Header
+ /// DX10 Header
+ /// Resulting DDS File Header in bytes
+ public static byte[] EncodeDDSHeader(DDSHeader header, DX10Header dx10Header)
+ {
+ // Create stream
+ using (var output = new BinaryWriter(new MemoryStream()))
+ {
+ // Write DDS Magic
+ output.Write(DDSHeader.DDSMagic);
+ // Write Header
+ output.Write(StructToBytes(header));
+ // Check for DX10 Header
+ if (header.PixelFormat.FourCC == PixelFormats.DX10.FourCC)
+ // Write Header
+ output.Write(StructToBytes(dx10Header));
+ // Done
+ return ((MemoryStream)(output.BaseStream)).ToArray();
+ }
+ }
+
+ ///
+ /// Generates DirectXTex Meta Data
+ ///
+ /// Image Width
+ /// Image Height
+ /// Number of Mip Maps
+ /// Compression Format
+ /// Whether or not this is a cube map
+ /// Resulting TexMetaData Object
+ public static TexMetadata GenerateMetadata(int width, int height, int mipMapLevels, DXGIFormat format, bool isCubeMap)
+ {
+ // Create Texture MetaData
+ return new TexMetadata(
+ width,
+ height,
+ 1,
+ isCubeMap ? 6 : 1,
+ mipMapLevels,
+ isCubeMap ? TexMiscFlags.TEXTURECUBE : 0,
+ 0,
+ format,
+ TexDimension.TEXTURE2D
+ );
+ }
+
+ private static readonly uint[] _elevenUInts = Enumerable.Repeat((uint)0, 11).ToArray();
+
+ ///
+ /// Generates a DDS Header, and if requires, a DX10 Header
+ ///
+ /// Meta Data
+ /// Flags
+ /// DDS Header Output
+ /// DX10 Header Output
+ public static void GenerateDDSHeader(TexMetadata metaData, DDSFlags flags, out DDSHeader header, out DX10Header dx10Header)
+ {
+ // Check array size
+ if (metaData.ArraySize > 1)
+ // Check if we have an array and whether we're cube maps/non-2D
+ if (metaData.ArraySize != 6 || metaData.Dimension != TexDimension.TEXTURE2D || !metaData.IsCubeMap())
+ // Texture1D arrays, Texture2D arrays, and Cubemap arrays must be stored using 'DX10' extended header
+ flags |= DDSFlags.FORCEDX10EXT;
+
+ // Check for DX10 Ext
+ if (flags.HasFlag(DDSFlags.FORCEDX10EXTMISC2))
+ flags |= DDSFlags.FORCEDX10EXT;
+
+ // Create DDS Header
+ header = new DDSHeader
+ {
+ // Set Data
+ Size = (uint) Marshal.SizeOf(),
+ Flags = DDSHeader.HeaderFlags.TEXTURE,
+ Height = 0,
+ Width = 0,
+ PitchOrLinearSize = 0,
+ Depth = 0,
+ MipMapCount = 0,
+ Reserved1 = _elevenUInts,
+ Caps = (uint) DDSHeader.SurfaceFlags.TEXTURE,
+ Caps2 = 0,
+ Caps3 = 0,
+ Caps4 = 0,
+ Reserved2 = 0,
+ PixelFormat = new DDSHeader.DDSPixelFormat(),
+
+ };
+
+ // Create DX10 Header
+ dx10Header = new DX10Header
+ {
+ Format = 0,
+ ResourceDimension = (TexDimension) 0,
+ MiscFlag = (TexMiscFlags) 0,
+ ArraySize = 0,
+ MiscFlags2 = 0,
+ };
+
+ // Switch format
+ header.PixelFormat = GetPixelFormat(metaData);
+ // Check for mips
+ if (metaData.MipLevels > 0)
+ {
+ // Set flag
+ header.Flags |= DDSHeader.HeaderFlags.MIPMAP;
+ // Check size
+ if (metaData.MipLevels > UInt16.MaxValue)
+ throw new ArgumentException(String.Format("Too many mipmaps: {0}. Max: {1}", metaData.MipLevels, UInt16.MaxValue));
+ // Set
+ header.MipMapCount = (uint)metaData.MipLevels;
+ // Check count
+ if (header.MipMapCount > 1)
+ header.Caps |= (uint)DDSHeader.SurfaceFlags.MIPMAP;
+ }
+
+ // Switch Dimension
+ switch (metaData.Dimension)
+ {
+ case TexDimension.TEXTURE1D:
+ {
+ // Check size
+ if (metaData.Width > Int32.MaxValue)
+ throw new ArgumentException(String.Format("Image Width too large: {0}. Max: {1}", metaData.Width, Int32.MaxValue));
+ // Set
+ header.Width = (uint)metaData.Width;
+ header.Height = header.Depth = 1;
+ // Check size
+ break;
+ }
+ case TexDimension.TEXTURE2D:
+ {
+ // Check size
+ if (metaData.Width > Int32.MaxValue || metaData.Height > Int32.MaxValue)
+ throw new ArgumentException(String.Format("Image Width and/or Height too large: {0}x{1}. Max: {2}",
+ metaData.Width,
+ metaData.Height,
+ Int32.MaxValue));
+ // Set
+ header.Width = (uint)metaData.Width;
+ header.Height = (uint)metaData.Height;
+ header.Depth = 1;
+ // Check size
+ break;
+ }
+ case TexDimension.TEXTURE3D:
+ {
+ // Check size
+ if (metaData.Width > Int32.MaxValue || metaData.Height > Int32.MaxValue)
+ throw new ArgumentException(String.Format("Image Width and/or Height too large: {0}x{1}. Max: {2}",
+ metaData.Width,
+ metaData.Height,
+ Int32.MaxValue));
+ // Check size
+ if (metaData.Depth > UInt16.MaxValue)
+ throw new ArgumentException(String.Format("Image Depth too large: {0}. Max: {1}", metaData.Depth, UInt16.MaxValue));
+ // Set
+ header.Flags |= DDSHeader.HeaderFlags.VOLUME;
+ header.Caps2 |= 0x00200000;
+ header.Width = (uint)metaData.Width;
+ header.Height = (uint)metaData.Height;
+ header.Depth = (uint)metaData.Depth;
+ // Check size
+ break;
+ }
+ default:
+ throw new ArgumentException("Invalid Texture Dimension.");
+
+ }
+ // Calculate the Pitch
+ ComputePitch(metaData.Format, (uint) metaData.Width, (uint) metaData.Height, out var rowPitch, out var slicePitch, CPFLAGS.NONE);
+ // Validate results
+ if (slicePitch > UInt32.MaxValue || rowPitch > UInt32.MaxValue)
+ throw new ArgumentException("Failed to calculate row and/or slice pitch, values returned were too large");
+ // Check is it compressed
+ if (IsCompressed(metaData.Format))
+ {
+ header.Flags |= DDSHeader.HeaderFlags.LINEARSIZE;
+ header.PitchOrLinearSize = (uint)slicePitch;
+ }
+ else
+ {
+ header.Flags |= DDSHeader.HeaderFlags.PITCH;
+ header.PitchOrLinearSize = (uint)rowPitch;
+ }
+
+ // Check for do we need to create the DX10 Header
+ if (HasDx10Header(header.PixelFormat))
+ {
+ // Check size
+ if (metaData.ArraySize > UInt16.MaxValue)
+ throw new ArgumentException(String.Format("Array Size too large: {0}. Max: {1}", metaData.ArraySize, UInt16.MaxValue));
+ // Set Pixel format
+ header.PixelFormat = PixelFormats.DX10;
+ // Set Data
+ dx10Header.Format = metaData.Format;
+ dx10Header.ResourceDimension = metaData.Dimension;
+ dx10Header.MiscFlag = metaData.MiscFlags & ~TexMiscFlags.TEXTURECUBE;
+ dx10Header.ArraySize = (uint)metaData.ArraySize;
+ // Check for Cube Maps
+ if (metaData.MiscFlags.HasFlag(TexMiscFlags.TEXTURECUBE))
+ {
+ // Check array size, must be a multiple of 6 for cube maps
+ if ((metaData.ArraySize % 6) != 0)
+ throw new ArgumentException("Array size must be a multiple of 6");
+ // Set Flag
+ dx10Header.MiscFlag |= TexMiscFlags.TEXTURECUBE;
+ dx10Header.ArraySize /= 6;
+ }
+ // Check for mist flags
+ if (flags.HasFlag(DDSFlags.FORCEDX10EXTMISC2))
+ // This was formerly 'reserved'. D3DX10 and D3DX11 will fail if this value is anything other than 0
+ dx10Header.MiscFlags2 = (uint)metaData.MiscFlags2;
+ }
+ }
+
+ public static bool HasDx10Header(DDSHeader.DDSPixelFormat pixelFormat) => pixelFormat.Size == 0;
+
+ public static DDSHeader.DDSPixelFormat GetPixelFormat(TexMetadata metaData) =>
+ metaData.Format switch
+ {
+ DXGIFormat.R8G8B8A8UNORM => PixelFormats.A8B8G8R8,
+ DXGIFormat.R16G16UNORM => PixelFormats.G16R16,
+ DXGIFormat.R8G8UNORM => PixelFormats.A8L8,
+ DXGIFormat.R16UNORM => PixelFormats.L16,
+ DXGIFormat.R8UNORM => PixelFormats.L8,
+ DXGIFormat.A8UNORM => PixelFormats.A8,
+ DXGIFormat.R8G8B8G8UNORM => PixelFormats.R8G8B8G8,
+ DXGIFormat.G8R8G8B8UNORM => PixelFormats.G8R8G8B8,
+ DXGIFormat.BC1UNORM => PixelFormats.DXT1,
+ DXGIFormat.BC2UNORM => metaData.IsPMAlpha() ? (PixelFormats.DXT2) : (PixelFormats.DXT3),
+ DXGIFormat.BC3UNORM => metaData.IsPMAlpha() ? (PixelFormats.DXT4) : (PixelFormats.DXT5),
+ DXGIFormat.BC4UNORM => PixelFormats.BC4UNORM,
+ DXGIFormat.BC4SNORM => PixelFormats.BC4SNORM,
+ DXGIFormat.BC5UNORM => PixelFormats.BC5UNORM,
+ DXGIFormat.BC5SNORM => PixelFormats.BC5SNORM,
+ DXGIFormat.B5G6R5UNORM => PixelFormats.R5G6B5,
+ DXGIFormat.B5G5R5A1UNORM => PixelFormats.A1R5G5B5,
+ DXGIFormat.R8G8SNORM => PixelFormats.V8U8,
+ DXGIFormat.R8G8B8A8SNORM => PixelFormats.Q8W8V8U8,
+ DXGIFormat.R16G16SNORM => PixelFormats.V16U16,
+ DXGIFormat.B8G8R8A8UNORM => PixelFormats.A8R8G8B8,
+ DXGIFormat.B8G8R8X8UNORM => PixelFormats.X8R8G8B8,
+ DXGIFormat.B4G4R4A4UNORM => PixelFormats.A4R4G4B4,
+ DXGIFormat.YUY2 => PixelFormats.YUY2,
+ // Legacy D3DX formats using D3DFMT enum value as FourCC
+ DXGIFormat.R32G32B32A32FLOAT => new DDSHeader.DDSPixelFormat { Flags = PixelFormats.DDSFOURCC, FourCC = 116 /* D3DFMTA32B32G32R32F */ },
+ DXGIFormat.R16G16B16A16FLOAT => new DDSHeader.DDSPixelFormat { Flags = PixelFormats.DDSFOURCC, FourCC = 113 /* D3DFMTA16B16G16R16F */ },
+ DXGIFormat.R16G16B16A16UNORM => new DDSHeader.DDSPixelFormat { Flags = PixelFormats.DDSFOURCC, FourCC = 36 /* D3DFMTA16B16G16R16 */ },
+ DXGIFormat.R16G16B16A16SNORM => new DDSHeader.DDSPixelFormat { Flags = PixelFormats.DDSFOURCC, FourCC = 110 /* D3DFMTQ16W16V16U16 */ },
+ DXGIFormat.R32G32FLOAT => new DDSHeader.DDSPixelFormat { Flags = PixelFormats.DDSFOURCC, FourCC = 115 /* D3DFMTG32R32F */ },
+ DXGIFormat.R16G16FLOAT => new DDSHeader.DDSPixelFormat { Flags = PixelFormats.DDSFOURCC, FourCC = 112 /* D3DFMTG16R16F */ },
+ DXGIFormat.R32FLOAT => new DDSHeader.DDSPixelFormat { Flags = PixelFormats.DDSFOURCC, FourCC = 114 /* D3DFMTR32F */ },
+ DXGIFormat.R16FLOAT => new DDSHeader.DDSPixelFormat { Flags = PixelFormats.DDSFOURCC, FourCC = 111 /* D3DFMTR16F */ },
+ _ => new DDSHeader.DDSPixelFormat()
+ };
+
+ #endregion
+ }
+}
diff --git a/Wabbajack.Compression.BSA/FO4Archive/ChunkBuilder.cs b/Wabbajack.Compression.BSA/FO4Archive/ChunkBuilder.cs
deleted file mode 100644
index 12f930e3..00000000
--- a/Wabbajack.Compression.BSA/FO4Archive/ChunkBuilder.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using ICSharpCode.SharpZipLib.Zip.Compression;
-using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
-using Wabbajack.Common;
-using Wabbajack.DTOs.BSA.FileStates;
-
-namespace Wabbajack.Compression.BSA.FO4Archive;
-
-public class ChunkBuilder
-{
- private BA2Chunk _chunk;
- private Stream _dataSlab;
- private long _offsetOffset;
- private uint _packSize;
-
- public static async Task Create(BA2DX10File state, BA2Chunk chunk, Stream src,
- DiskSlabAllocator slab, CancellationToken token)
- {
- var builder = new ChunkBuilder {_chunk = chunk};
-
- if (!chunk.Compressed)
- {
- builder._dataSlab = slab.Allocate(chunk.FullSz);
- await src.CopyToLimitAsync(builder._dataSlab, (int) chunk.FullSz, token);
- }
- else
- {
- var deflater = new Deflater(Deflater.BEST_COMPRESSION);
- await using var ms = new MemoryStream();
- await using (var ds = new DeflaterOutputStream(ms, deflater))
- {
- ds.IsStreamOwner = false;
- await src.CopyToLimitAsync(ds, (int) chunk.FullSz, token);
- }
-
- builder._dataSlab = slab.Allocate(ms.Length);
- ms.Position = 0;
- await ms.CopyToLimitAsync(builder._dataSlab, (int) ms.Length, token);
- builder._packSize = (uint) ms.Length;
- }
-
- builder._dataSlab.Position = 0;
-
- return builder;
- }
-
- public void WriteHeader(BinaryWriter bw)
- {
- _offsetOffset = bw.BaseStream.Position;
- bw.Write((ulong) 0);
- bw.Write(_packSize);
- bw.Write(_chunk.FullSz);
- bw.Write(_chunk.StartMip);
- bw.Write(_chunk.EndMip);
- bw.Write(_chunk.Align);
- }
-
- public async ValueTask WriteData(BinaryWriter bw, CancellationToken token)
- {
- var pos = bw.BaseStream.Position;
- bw.BaseStream.Position = _offsetOffset;
- bw.Write((ulong) pos);
- bw.BaseStream.Position = pos;
- await _dataSlab.CopyToLimitAsync(bw.BaseStream, (int) _dataSlab.Length, token);
- await _dataSlab.DisposeAsync();
- }
-}
\ No newline at end of file
diff --git a/Wabbajack.Compression.BSA/FO4Archive/DX10Entry.cs b/Wabbajack.Compression.BSA/FO4Archive/DX10Entry.cs
deleted file mode 100644
index a793f4f4..00000000
--- a/Wabbajack.Compression.BSA/FO4Archive/DX10Entry.cs
+++ /dev/null
@@ -1,249 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using Compression.BSA;
-using ICSharpCode.SharpZipLib.Zip.Compression;
-using Wabbajack.Common;
-using Wabbajack.DTOs.BSA.FileStates;
-using Wabbajack.DTOs.Streams;
-using Wabbajack.DTOs.Texture;
-using Wabbajack.Paths;
-
-namespace Wabbajack.Compression.BSA.FO4Archive;
-
-public class DX10Entry : IBA2FileEntry
-{
- private readonly Reader _bsa;
- private ushort _chunkHdrLen;
- private List _chunks;
- private uint _dirHash;
- private string _extension;
- private byte _format;
- private ushort _height;
- private int _index;
- private uint _nameHash;
- private byte _numChunks;
- private byte _numMips;
- private ushort _unk16;
- private byte _unk8;
- private ushort _width;
- private readonly byte _isCubemap;
- private readonly byte _tileMode;
-
- public DX10Entry(Reader ba2Reader, int idx)
- {
- _bsa = ba2Reader;
- var _rdr = ba2Reader._rdr;
- _nameHash = _rdr.ReadUInt32();
- FullPath = _nameHash.ToString("X");
- _extension = Encoding.UTF8.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();
- _isCubemap = _rdr.ReadByte();
- _tileMode = _rdr.ReadByte();
- _index = idx;
-
- _chunks = Enumerable.Range(0, _numChunks)
- .Select(_ => new TextureChunk(_rdr))
- .ToList();
- }
-
- public uint HeaderSize => DDS.HeaderSizeForFormat((DXGI_FORMAT) _format);
-
- public string FullPath { get; set; }
-
- public RelativePath Path => FullPath.ToRelativePath();
- public uint Size => (uint) _chunks.Sum(f => f._fullSz) + HeaderSize + sizeof(uint);
-
- public AFile State => new BA2DX10File
- {
- Path = Path,
- NameHash = _nameHash,
- Extension = _extension,
- DirHash = _dirHash,
- Unk8 = _unk8,
- ChunkHdrLen = _chunkHdrLen,
- Height = _height,
- Width = _width,
- NumMips = _numMips,
- PixelFormat = _format,
- IsCubeMap = _isCubemap,
- TileMode = _tileMode,
- Index = _index,
- Chunks = _chunks.Select(ch => new BA2Chunk
- {
- FullSz = ch._fullSz,
- StartMip = ch._startMip,
- EndMip = ch._endMip,
- Align = ch._align,
- Compressed = ch._packSz != 0
- }).ToArray()
- };
-
- public async ValueTask CopyDataTo(Stream output, CancellationToken token)
- {
- var bw = new BinaryWriter(output);
-
- WriteHeader(bw);
-
- await using var fs = await _bsa._streamFactory.GetStream();
- using var br = new BinaryReader(fs);
- foreach (var chunk in _chunks)
- {
- var full = new byte[chunk._fullSz];
- var isCompressed = chunk._packSz != 0;
-
- br.BaseStream.Seek((long) chunk._offset, SeekOrigin.Begin);
-
- if (!isCompressed)
- {
- await br.BaseStream.ReadAsync(full, token);
- }
- else
- {
- var compressed = new byte[chunk._packSz];
- await br.BaseStream.ReadAsync(compressed, token);
- var inflater = new Inflater();
- inflater.SetInput(compressed);
- inflater.Inflate(full);
- }
-
- await bw.BaseStream.WriteAsync(full, token);
- }
- }
-
- public async ValueTask GetStreamFactory(CancellationToken token)
- {
- var ms = new MemoryStream();
- await CopyDataTo(ms, token);
- ms.Position = 0;
- return new MemoryStreamFactory(ms, Path, _bsa._streamFactory.LastModifiedUtc);
- }
-
- 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.dwDepth = 1;
- ddsHeader.dwSurfaceFlags = DDS.DDS_SURFACE_FLAGS_TEXTURE | DDS.DDS_SURFACE_FLAGS_MIPMAP;
- ddsHeader.dwCubemapFlags = _isCubemap == 1 ? (uint)(DDSCAPS2.CUBEMAP
- | DDSCAPS2.CUBEMAP_NEGATIVEX | DDSCAPS2.CUBEMAP_POSITIVEX
- | DDSCAPS2.CUBEMAP_NEGATIVEY | DDSCAPS2.CUBEMAP_POSITIVEY
- | DDSCAPS2.CUBEMAP_NEGATIVEZ | DDSCAPS2.CUBEMAP_POSITIVEZ
- | DDSCAPS2.CUBEMAP_ALLFACES) : 0u;
-
-
- switch ((DXGI_FORMAT) _format)
- {
- case 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.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.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.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('B', 'C', '5', 'U');
- ddsHeader.dwPitchOrLinearSize = (uint) (_width * _height); // 8bpp
- break;
- case 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.BC3_UNORM_SRGB:
- case DXGI_FORMAT.BC6H_UF16:
- case DXGI_FORMAT.BC4_UNORM:
- case DXGI_FORMAT.BC5_SNORM:
- case DXGI_FORMAT.BC7_UNORM:
- case 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.R8G8B8A8_UNORM:
- case 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.B8G8R8A8_UNORM:
- case 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.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: " + FullPath);
- }
-
- bw.Write((uint) DDS.DDS_MAGIC);
- ddsHeader.Write(bw);
-
- switch ((DXGI_FORMAT) _format)
- {
- case DXGI_FORMAT.BC1_UNORM_SRGB:
- case DXGI_FORMAT.BC3_UNORM_SRGB:
- case DXGI_FORMAT.BC4_UNORM:
- case DXGI_FORMAT.BC5_SNORM:
- case DXGI_FORMAT.BC6H_UF16:
- case DXGI_FORMAT.BC7_UNORM:
- case 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;
- }
- }
-}
\ No newline at end of file
diff --git a/Wabbajack.DTOs/ModList/BSA/ArchiveStates/BA2State.cs b/Wabbajack.DTOs/ModList/BSA/ArchiveStates/BA2State.cs
index dd94cadc..165b07f4 100644
--- a/Wabbajack.DTOs/ModList/BSA/ArchiveStates/BA2State.cs
+++ b/Wabbajack.DTOs/ModList/BSA/ArchiveStates/BA2State.cs
@@ -17,4 +17,7 @@ public class BA2State : IArchive
public BA2EntryType Type { get; set; }
public string HeaderMagic { get; set; }
public uint Version { get; set; }
+ public uint Unknown1 { get; set; }
+ public uint Unknown2 { get; set; }
+ public uint Compression { get; set; }
}
\ No newline at end of file