mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Add Starfield support (#2589)
* Add support for new Starfield BA2 versions * Add Starfield BA2 LZ4 texture compression support (WIP, not finished) * Work on replacing DDS.cs with DirectXTexUtility * Fix reading Starfield BA2s with new DirectXTexUtil * Fix builder not exporting new Starfield header options * Fix writing LZ4 chunks in frame format instead of block format * Rename FO4Archive to BA2Archive, merge SFArchive and FO4Archive * Clean up testing code & CLI launch settings * Update changelog
This commit is contained in:
parent
59b2f1a7a1
commit
a545cb375a
@ -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
|
||||
|
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>bin\Release\Publish</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<_TargetId>Folder</_TargetId>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
<PublishReadyToRun>false</PublishReadyToRun>
|
||||
</PropertyGroup>
|
||||
</Project>
|
7
Wabbajack.CLI/Properties/launchSettings.json
Normal file
7
Wabbajack.CLI/Properties/launchSettings.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Wabbajack.CLI": {
|
||||
"commandName": "Project"
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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);
|
||||
|
93
Wabbajack.Compression.BSA/BA2Archive/ChunkBuilder.cs
Normal file
93
Wabbajack.Compression.BSA/BA2Archive/ChunkBuilder.cs
Normal file
@ -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<ChunkBuilder> 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();
|
||||
}
|
||||
}
|
179
Wabbajack.Compression.BSA/BA2Archive/DX10Entry.cs
Normal file
179
Wabbajack.Compression.BSA/BA2Archive/DX10Entry.cs
Normal file
@ -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<TextureChunk> _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<DirectXTexUtility.DDSHeader>();
|
||||
var pixelFormat = DirectXTexUtility.GetPixelFormat(Metadata);
|
||||
var hasDx10Header = DirectXTexUtility.HasDx10Header(pixelFormat);
|
||||
if (hasDx10Header)
|
||||
size += (uint)Marshal.SizeOf<DirectXTexUtility.DX10Header>();
|
||||
|
||||
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<IStreamFactory> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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<ChunkBuilder> _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<DX10FileEntryBuilder> Create(BA2DX10File state, Stream src, DiskSlabAllocator slab,
|
||||
uint size = 0;
|
||||
size += (uint)Marshal.SizeOf(DirectXTexUtility.DDSHeader.DDSMagic);
|
||||
size += (uint)Marshal.SizeOf<DirectXTexUtility.DDSHeader>();
|
||||
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<DirectXTexUtility.DX10Header>();
|
||||
|
||||
return _headerSize = size;
|
||||
}
|
||||
|
||||
public static async Task<DX10FileEntryBuilder> 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<ChunkBuilder>();
|
||||
|
||||
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;
|
||||
}
|
@ -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
|
||||
{
|
@ -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
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
using Wabbajack.Compression.BSA.Interfaces;
|
||||
|
||||
namespace Wabbajack.Compression.BSA.FO4Archive;
|
||||
namespace Wabbajack.Compression.BSA.BA2Archive;
|
||||
|
||||
internal interface IBA2FileEntry : IFile
|
||||
{
|
@ -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
|
||||
{
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Fallout 4 - Version 1, 7 or 8
|
||||
/// Starfield - Version 2 or 3
|
||||
/// </summary>
|
||||
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<IBA2FileEntry>();
|
||||
for (var idx = 0; idx < _numFiles; idx += 1)
|
||||
switch (_type)
|
||||
@ -94,4 +109,4 @@ public class Reader : IReader
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Wabbajack.Compression.BSA.FO4Archive;
|
||||
namespace Wabbajack.Compression.BSA.BA2Archive;
|
||||
|
||||
public class TextureChunk
|
||||
{
|
@ -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()
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
1116
Wabbajack.Compression.BSA/DirectXTexUtil.cs
Normal file
1116
Wabbajack.Compression.BSA/DirectXTexUtil.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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<ChunkBuilder> 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();
|
||||
}
|
||||
}
|
@ -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<TextureChunk> _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<IStreamFactory> 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
Loading…
Reference in New Issue
Block a user