diff --git a/BinderTool.Core/BinderTool.Core.csproj b/BinderTool.Core/BinderTool.Core.csproj index 657c808..c2913a8 100644 --- a/BinderTool.Core/BinderTool.Core.csproj +++ b/BinderTool.Core/BinderTool.Core.csproj @@ -68,6 +68,16 @@ + + + + + + + + + + diff --git a/BinderTool.Core/Dds/DdsFile.cs b/BinderTool.Core/Dds/DdsFile.cs new file mode 100644 index 0000000..a980c07 --- /dev/null +++ b/BinderTool.Core/Dds/DdsFile.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +using System.Text; +using BinderTool.Core.Dds.Enum; + +namespace BinderTool.Core.Dds +{ + public class DdsFile + { + private const int MagicNumber = 0x20534444; + + public DdsFileHeader Header { get; set; } + + public DdsFileHeaderDx10 HeaderDx10 { get; set; } + + public byte[] Data { get; set; } + + public void Write(Stream outputStream) + { + BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true); + writer.Write(MagicNumber); + Header.Write(outputStream); + if (Header.IsDx10()) + { + HeaderDx10.Write(outputStream); + } + + writer.Write(Data); + } + + public static byte[] ConvertData(byte[] sourceBuffer, int height, int width, DxgiFormat dxgiFormat) + { + if (sourceBuffer == null) + { + return null; + } + + var inputStream = new MemoryStream(sourceBuffer); + byte[] targetBuffer = new byte[sourceBuffer.Length]; + byte[] blockBuffer = new byte[64]; + int heightBlock = height / 4; + int widthBlock = width / 4; + int bytesPerPixel = DdsPixelFormat.DxgiToBytesPerPixel(dxgiFormat) * 2; + for (int y = 0; y < heightBlock; y++) + { + for (int x = 0; x < widthBlock; x++) + { + int mx = x; + int my = y; + if (widthBlock > 1 && heightBlock > 1) + { + MapBlockPosition(x, y, widthBlock, 2, out mx, out my); + } + + if (widthBlock > 2 && heightBlock > 2) + { + MapBlockPosition(mx, my, widthBlock, 4, out mx, out my); + } + + if (widthBlock > 4 && heightBlock > 4) + { + MapBlockPosition(mx, my, widthBlock, 8, out mx, out my); + } + + inputStream.Read(blockBuffer, 0, bytesPerPixel); + int destinationIndex = bytesPerPixel * (my * widthBlock + mx); + Array.Copy(blockBuffer, 0, targetBuffer, destinationIndex, bytesPerPixel); + } + } + + return targetBuffer; + } + + private static void MapBlockPosition(int x, int y, int w, int bx, out int mx, out int my) + { + int num1 = bx / 2; + int num2 = x / bx; + int num3 = y / num1; + int num4 = x % bx; + int num5 = y % num1; + int num6 = w / bx; + int num7 = 2 * num6; + int num8 = num2 + num3 * num6; + int num9 = num8 % num7; + int num10 = num9 / 2 + num9 % 2 * num6; + int num11 = num8 / num7 * num7 + num10; + int num12 = num11 % num6; + int num13 = num11 / num6; + + mx = num12 * bx + num4; + my = num13 * num1 + num5; + } + } +} diff --git a/BinderTool.Core/Dds/DdsFileHeader.cs b/BinderTool.Core/Dds/DdsFileHeader.cs new file mode 100644 index 0000000..9c24c10 --- /dev/null +++ b/BinderTool.Core/Dds/DdsFileHeader.cs @@ -0,0 +1,71 @@ +using System; +using System.IO; +using System.Text; +using BinderTool.Core.Dds.Enum; + +namespace BinderTool.Core.Dds +{ + public class DdsFileHeader + { + public const int DefaultHeaderSize = 124; + + public int Size { get; set; } = DefaultHeaderSize; + + public DdsFileHeaderFlags Flags { get; set; } + + public int Height { get; set; } + + public int Width { get; set; } + + public int PitchOrLinearSize { get; set; } + + public int Depth { get; set; } + + public int MipMapCount { get; set; } + + public DdsPixelFormat PixelFormat { get; set; } + + public DdsSurfaceFlags Caps { get; set; } + + public DdsCaps2Flags Caps2 { get; set; } + + public int Caps3 { get; set; } + + public int Caps4 { get; set; } + + public void Write(Stream outputStream) + { + BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true); + writer.Write(Size); + writer.Write(Convert.ToInt32(Flags)); + writer.Write(Height); + writer.Write(Width); + writer.Write(PitchOrLinearSize); + writer.Write(Depth); + writer.Write(MipMapCount); + // int Reserved1[11]; + writer.WriteZeros(44); + PixelFormat.Write(outputStream); + writer.Write(Convert.ToInt32(Caps)); + writer.Write(Convert.ToInt32(Caps2)); + writer.Write(Caps3); + writer.Write(Caps4); + // int Reserved2 + writer.WriteZeros(4); + } + + public bool IsDx10() + { + return PixelFormat != null && + PixelFormat.Flags.HasFlag(DdsPixelFormatFlag.FourCc) && + PixelFormat.FourCc == DdsPixelFormat.Dx10FourCc; + } + + public override string ToString() + { + return $"Size: {Size}, Flags: {Flags}, Height: {Height}, Width: {Width}," + + $" PitchOrLinearSize: {PitchOrLinearSize}, Depth: {Depth}, MipMapCount: {MipMapCount}," + + $" PixelFormat: {PixelFormat}, Caps: {Caps}, Caps2: {Caps2}, Caps3: {Caps3}, " + $"Caps4: {Caps4}"; + } + } +} diff --git a/BinderTool.Core/Dds/DdsFileHeaderDx10.cs b/BinderTool.Core/Dds/DdsFileHeaderDx10.cs new file mode 100644 index 0000000..ed04918 --- /dev/null +++ b/BinderTool.Core/Dds/DdsFileHeaderDx10.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; +using System.Text; +using BinderTool.Core.Dds.Enum; + +namespace BinderTool.Core.Dds +{ + public class DdsFileHeaderDx10 + { + public DxgiFormat Format { get; set; } + + public D3D10ResourceDimension ResourceDimension { get; set; } + + public uint MiscFlag { get; set; } + + public uint ArraySize { get; set; } + + public uint MiscFlags2 { get; set; } + + public void Write(Stream outputStream) + { + BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true); + writer.Write(Convert.ToUInt32(Format)); + writer.Write(Convert.ToInt32(ResourceDimension)); + writer.Write(MiscFlag); + writer.Write(ArraySize); + writer.Write(MiscFlags2); + } + } +} diff --git a/BinderTool.Core/Dds/DdsPixelFormat.cs b/BinderTool.Core/Dds/DdsPixelFormat.cs new file mode 100644 index 0000000..3e422b8 --- /dev/null +++ b/BinderTool.Core/Dds/DdsPixelFormat.cs @@ -0,0 +1,335 @@ +using System; +using System.IO; +using System.Text; +using BinderTool.Core.Dds.Enum; + +namespace BinderTool.Core.Dds +{ + public class DdsPixelFormat + { + private const int DefaultSize = 32; + internal const int Dxt1FourCc = 0x31545844; + internal const int Dxt2FourCc = 0x32545844; + internal const int Dxt3FourCc = 0x33545844; + internal const int Dtx4FourCc = 0x34545844; + internal const int Dtx5FourCc = 0x35545844; + internal const int Dx10FourCc = 0x30315844; + internal const int Ati1FourCc = 0x31495441; + internal const int Ati2FourCc = 0x32495441; + public int Size { get; set; } + public DdsPixelFormatFlag Flags { get; set; } + public int FourCc { get; set; } + public int RgbBitCount { get; set; } + public uint RBitMask { get; set; } + public uint GBitMask { get; set; } + public uint BBitMask { get; set; } + public uint ABitMask { get; set; } + + public void Write(Stream outputStream) + { + BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true); + writer.Write(Size); + writer.Write(Convert.ToUInt32(Flags)); + writer.Write(FourCc); + writer.Write(RgbBitCount); + writer.Write(RBitMask); + writer.Write(GBitMask); + writer.Write(BBitMask); + writer.Write(ABitMask); + } + + public static int DxgiToBytesPerPixel(DxgiFormat dxgiFormat) + { + switch (dxgiFormat) + { + case DxgiFormat.Unknown: + return 0; + 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: + 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: + return 32; + 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: + return 16; + case DxgiFormat.R8Typeless: + case DxgiFormat.R8Unorm: + case DxgiFormat.R8Uint: + case DxgiFormat.R8Snorm: + case DxgiFormat.R8Sint: + case DxgiFormat.A8Unorm: + return 8; + case DxgiFormat.R1Unorm: + return 1; + case DxgiFormat.R9G9B9E5Sharedexp: + case DxgiFormat.R8G8B8G8Unorm: + case DxgiFormat.G8R8G8B8Unorm: + return 32; + case DxgiFormat.Bc1Typeless: + case DxgiFormat.Bc1Unorm: + case DxgiFormat.Bc1UnormSrgb: + return 4; + case DxgiFormat.Bc2Typeless: + case DxgiFormat.Bc2Unorm: + case DxgiFormat.Bc2UnormSrgb: + case DxgiFormat.Bc3Typeless: + case DxgiFormat.Bc3Unorm: + case DxgiFormat.Bc3UnormSrgb: + return 8; + case DxgiFormat.Bc4Typeless: + case DxgiFormat.Bc4Unorm: + case DxgiFormat.Bc4Snorm: + return 4; + case DxgiFormat.Bc5Typeless: + case DxgiFormat.Bc5Unorm: + case DxgiFormat.Bc5Snorm: + return 8; + case DxgiFormat.B5G6R5Unorm: + case DxgiFormat.B5G5R5A1Unorm: + return 16; + case DxgiFormat.B8G8R8A8Unorm: + case DxgiFormat.B8G8R8X8Unorm: + case DxgiFormat.R10G10B10XrBiasA2Unorm: + case DxgiFormat.B8G8R8A8Typeless: + case DxgiFormat.B8G8R8A8UnormSrgb: + case DxgiFormat.B8G8R8X8Typeless: + case DxgiFormat.B8G8R8X8UnormSrgb: + return 32; + case DxgiFormat.Bc6HTypeless: + case DxgiFormat.Bc6HUf16: + case DxgiFormat.Bc6HSf16: + case DxgiFormat.Bc7Typeless: + case DxgiFormat.Bc7Unorm: + case DxgiFormat.Bc7UnormSrgb: + return 8; + case DxgiFormat.Ayuv: + case DxgiFormat.Y410: + case DxgiFormat.Y416: + case DxgiFormat.Nv12: + case DxgiFormat.P010: + case DxgiFormat.P016: + case DxgiFormat.Opaque420: + case DxgiFormat.Yuy2: + case DxgiFormat.Y210: + case DxgiFormat.Y216: + case DxgiFormat.Nv11: + case DxgiFormat.Ai44: + case DxgiFormat.Ia44: + case DxgiFormat.P8: + case DxgiFormat.A8P8: + return 0; + case DxgiFormat.B4G4R4A4Unorm: + return 16; + default: + return 0; + } + } + + public static DdsPixelFormat DxgiToDdsPixelFormat(DxgiFormat dxgiFormat) + { + DdsPixelFormat pixelFormat; + switch (dxgiFormat) + { + case DxgiFormat.Bc1Unorm: + case DxgiFormat.Bc1UnormSrgb: + pixelFormat = DdsPfDxt1(); + break; + case DxgiFormat.Bc2Unorm: + pixelFormat = DdsPfDxt3(); + break; + case DxgiFormat.Bc3Unorm: + pixelFormat = DdsPfDxt5(); + break; + case DxgiFormat.Bc4Unorm: + pixelFormat = DdsPfAti1(); + break; + case DxgiFormat.Bc5Unorm: + pixelFormat = DdsPfAti2(); + break; + default: + pixelFormat = DdsPfDx10(); + break; + } + + return pixelFormat; + } + + private static int DxgiToFourCc(DxgiFormat dxgiFormat) + { + int fourCc; + switch (dxgiFormat) + { + case DxgiFormat.Bc1Unorm: + case DxgiFormat.Bc1UnormSrgb: + fourCc = Dxt1FourCc; // DXT1 + break; + case DxgiFormat.Bc2Unorm: + fourCc = Dxt3FourCc; // DXT3 + break; + case DxgiFormat.Bc3Unorm: + fourCc = Dtx5FourCc; // DXT5 + break; + case DxgiFormat.Bc4Unorm: + fourCc = Ati1FourCc; // ATI1 + break; + case DxgiFormat.Bc5Unorm: + fourCc = Ati2FourCc; // ATI2 + break; + default: + fourCc = Dx10FourCc; // DX10 + break; + } + + return fourCc; + } + + public static DdsPixelFormat DdsPfDxt1() + { + return DdsPfDx(Dxt1FourCc); // DXT1 + } + + public static DdsPixelFormat DdsPfDxt2() + { + return DdsPfDx(Dxt2FourCc); // DXT2 + } + + public static DdsPixelFormat DdsPfDxt3() + { + return DdsPfDx(Dxt3FourCc); // DXT3 + } + + public static DdsPixelFormat DdsPfDxt4() + { + return DdsPfDx(Dtx4FourCc); // DXT4 + } + + public static DdsPixelFormat DdsPfDxt5() + { + return DdsPfDx(Dtx5FourCc); // DXT5 + } + + public static DdsPixelFormat DdsPfAti1() + { + return DdsPfDx(Ati1FourCc); // ATI1 + } + + public static DdsPixelFormat DdsPfAti2() + { + return DdsPfDx(Ati2FourCc); // ATI2 + } + + public static DdsPixelFormat DdsPfDx10() + { + return DdsPfDx(Dx10FourCc); // DX10 + } + + private static DdsPixelFormat DdsPfDx(int fourCc) + { + DdsPixelFormat pixelFormat = new DdsPixelFormat + { + Size = DefaultSize, + Flags = DdsPixelFormatFlag.FourCc, + FourCc = fourCc + }; + return pixelFormat; + } + + protected bool Equals(DdsPixelFormat other) + { + return Size == other.Size && + Flags == other.Flags && + FourCc == other.FourCc && + RgbBitCount == other.RgbBitCount && + RBitMask == other.RBitMask && + GBitMask == other.GBitMask && + BBitMask == other.BBitMask && + ABitMask == other.ABitMask; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((DdsPixelFormat) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Size; + hashCode = (hashCode*397) ^ (int) Flags; + hashCode = (hashCode*397) ^ FourCc; + hashCode = (hashCode*397) ^ RgbBitCount; + hashCode = (hashCode*397) ^ (int) RBitMask; + hashCode = (hashCode*397) ^ (int) GBitMask; + hashCode = (hashCode*397) ^ (int) BBitMask; + hashCode = (hashCode*397) ^ (int) ABitMask; + return hashCode; + } + } + + public override string ToString() + { + return $"Size: {Size}, Flags: {Flags}, FourCc: {FourCc}, RgbBitCount: {RgbBitCount}," + + $" RBitMask: {RBitMask}, GBitMask: {GBitMask}, BBitMask: {BBitMask}, ABitMask: {ABitMask}"; + } + } +} diff --git a/BinderTool.Core/Dds/Enum/D3D10ResourceDimension.cs b/BinderTool.Core/Dds/Enum/D3D10ResourceDimension.cs new file mode 100644 index 0000000..5dcd672 --- /dev/null +++ b/BinderTool.Core/Dds/Enum/D3D10ResourceDimension.cs @@ -0,0 +1,11 @@ +namespace BinderTool.Core.Dds.Enum +{ + public enum D3D10ResourceDimension + { + Unknown = 0, + Buffer = 1, + Texture1D = 2, + Texture2D = 3, + Texture3D = 4 + } +} diff --git a/BinderTool.Core/Dds/Enum/DdsCaps2Flags.cs b/BinderTool.Core/Dds/Enum/DdsCaps2Flags.cs new file mode 100644 index 0000000..ebd9780 --- /dev/null +++ b/BinderTool.Core/Dds/Enum/DdsCaps2Flags.cs @@ -0,0 +1,51 @@ +using System; + +namespace BinderTool.Core.Dds.Enum +{ + [Flags] + public enum DdsCaps2Flags + { + /// + /// DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX + /// + PositiveX = 0x00000600, + + /// + /// DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX + /// + NegativeX = 0x00000a00, + + /// + /// DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY + /// + PositiveY = 0x00001200, + + /// + /// DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY + /// + NegativeY = 0x00002200, + + /// + /// DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ + /// + PositiveZ = 0x00004200, + + /// + /// DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ + /// + NegativeZ = 0x00008200, + + /// + /// DDSCAPS2_CUBEMAP | + /// DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX | + /// DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_POSITIVEY | + /// DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ + /// + AllFaces = PositiveX | NegativeX | PositiveY | NegativeY | PositiveZ | NegativeZ, + + /// + /// DDSCAPS2_VOLUME + /// + Volume = 0x00200000 + } +} diff --git a/BinderTool.Core/Dds/Enum/DdsFileHeaderFlags.cs b/BinderTool.Core/Dds/Enum/DdsFileHeaderFlags.cs new file mode 100644 index 0000000..b4f1faa --- /dev/null +++ b/BinderTool.Core/Dds/Enum/DdsFileHeaderFlags.cs @@ -0,0 +1,33 @@ +using System; + +namespace BinderTool.Core.Dds.Enum +{ + [Flags] + public enum DdsFileHeaderFlags + { + /// + /// DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT + /// + Texture = 0x00001007, + + /// + /// DDSD_MIPMAPCOUNT + /// + MipMap = 0x00020000, + + /// + /// DDSD_DEPTH + /// + Volume = 0x00800000, + + /// + /// DDSD_PITCH + /// + Pitch = 0x00000008, + + /// + /// DDSD_LINEARSIZE + /// + LinearSize = 0x00080000 + } +} diff --git a/BinderTool.Core/Dds/Enum/DdsPixelFormatFlag.cs b/BinderTool.Core/Dds/Enum/DdsPixelFormatFlag.cs new file mode 100644 index 0000000..6b4e4e5 --- /dev/null +++ b/BinderTool.Core/Dds/Enum/DdsPixelFormatFlag.cs @@ -0,0 +1,38 @@ +using System; + +namespace BinderTool.Core.Dds.Enum +{ + [Flags] + public enum DdsPixelFormatFlag : uint + { + /// + /// DDPF_ALPHA + /// + Alpha = 0x00000002, + + /// + /// DDPF_FOURCC + /// + FourCc = 0x00000004, + + /// + /// DDPF_RGB + /// + Rgb = 0x00000040, + + /// + /// DDPF_RGB | DDPF_ALPHAPIXELS + /// + Rgba = 0x00000041, + + /// + /// DDPF_LUMINANCE + /// + Luminance = 0x00020000, + + /// + /// Nvidia custom DDPF_NORMA + /// + Normal = 0x80000000 + } +} diff --git a/BinderTool.Core/Dds/Enum/DdsSurfaceFlags.cs b/BinderTool.Core/Dds/Enum/DdsSurfaceFlags.cs new file mode 100644 index 0000000..637657f --- /dev/null +++ b/BinderTool.Core/Dds/Enum/DdsSurfaceFlags.cs @@ -0,0 +1,23 @@ +using System; + +namespace BinderTool.Core.Dds.Enum +{ + [Flags] + public enum DdsSurfaceFlags + { + /// + /// DDSCAPS_TEXTURE + /// + Texture = 0x00001000, + + /// + /// DDSCAPS_COMPLEX | DDSCAPS_MIPMAP + /// + MipMap = 0x00400008, + + /// + /// DDSCAPS_COMPLEX + /// + CubeMap = 0x00000008 + } +} diff --git a/BinderTool.Core/Dds/Enum/DxgiFormat.cs b/BinderTool.Core/Dds/Enum/DxgiFormat.cs new file mode 100644 index 0000000..215289b --- /dev/null +++ b/BinderTool.Core/Dds/Enum/DxgiFormat.cs @@ -0,0 +1,122 @@ +namespace BinderTool.Core.Dds.Enum +{ + 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, // 420_OPAQUE + Yuy2 = 107, + Y210 = 108, + Y216 = 109, + Nv11 = 110, + Ai44 = 111, + Ia44 = 112, + P8 = 113, + A8P8 = 114, + B4G4R4A4Unorm = 115 + } +} diff --git a/BinderTool.Core/Enfl/EntryFileListFile.cs b/BinderTool.Core/Enfl/EntryFileListFile.cs index 4292cd1..c96847a 100644 --- a/BinderTool.Core/Enfl/EntryFileListFile.cs +++ b/BinderTool.Core/Enfl/EntryFileListFile.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using System.IO; +using System.IO; using System.Text; using ICSharpCode.SharpZipLib.Zip.Compression.Streams; diff --git a/BinderTool.Core/ExtensionMethods.cs b/BinderTool.Core/ExtensionMethods.cs index 44d4e7b..fa68c27 100644 --- a/BinderTool.Core/ExtensionMethods.cs +++ b/BinderTool.Core/ExtensionMethods.cs @@ -10,6 +10,12 @@ internal static void Skip(this BinaryReader reader, int count) reader.BaseStream.Position += count; } + internal static void WriteZeros(this BinaryWriter writer, int count) + { + byte[] zeros = new byte[count]; + writer.Write(zeros); + } + internal static string ReadString(this BinaryReader reader, int count) { return new string(reader.ReadChars(count)); diff --git a/BinderTool.Core/Fmg/FmgFile.cs b/BinderTool.Core/Fmg/FmgFile.cs index 3edc163..6331682 100644 --- a/BinderTool.Core/Fmg/FmgFile.cs +++ b/BinderTool.Core/Fmg/FmgFile.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Text; diff --git a/BinderTool.Core/GameVersion.cs b/BinderTool.Core/GameVersion.cs index 22a8c0f..f40d498 100644 --- a/BinderTool.Core/GameVersion.cs +++ b/BinderTool.Core/GameVersion.cs @@ -4,6 +4,7 @@ public enum GameVersion { Common, DarkSouls2, - DarkSouls3 + DarkSouls3, + Bloodborne } } \ No newline at end of file diff --git a/BinderTool.Core/Properties/AssemblyInfo.cs b/BinderTool.Core/Properties/AssemblyInfo.cs index 37308c8..029da94 100644 --- a/BinderTool.Core/Properties/AssemblyInfo.cs +++ b/BinderTool.Core/Properties/AssemblyInfo.cs @@ -6,10 +6,10 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BinderTool.Core")] -[assembly: AssemblyCopyright("Copyright © 2016 Atvaark")] +[assembly: AssemblyCopyright("Copyright © 2018 Atvaark")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("615e60b8-3eb0-4f54-ad9b-4fa1a9fe0df0")] -[assembly: AssemblyVersion("0.5.1.0")] -[assembly: AssemblyFileVersion("0.5.1.0")] +[assembly: AssemblyVersion("0.5.2.0")] +[assembly: AssemblyFileVersion("0.5.2.0")] diff --git a/BinderTool.Core/Tpf/TpfFile.cs b/BinderTool.Core/Tpf/TpfFile.cs index 9353913..86c33ef 100644 --- a/BinderTool.Core/Tpf/TpfFile.cs +++ b/BinderTool.Core/Tpf/TpfFile.cs @@ -23,7 +23,7 @@ private void Read(Stream inputStream) int sizeSum = reader.ReadInt32(); int entryCount = reader.ReadInt32(); - byte flag1 = reader.ReadByte(); // 00 + byte versionFlag = reader.ReadByte(); // 00 DS / 02 DeS / 04 BB byte flag2 = reader.ReadByte(); // 03 byte encoding = reader.ReadByte(); byte flag3 = reader.ReadByte(); // 00 @@ -41,10 +41,15 @@ private void Read(Stream inputStream) break; } + // TODO: Verify that versionFlag is actually different between DS and BB + GameVersion gameVersion = versionFlag == 0x04 + ? GameVersion.Bloodborne + : GameVersion.Common; + List entries = new List(entryCount); for (int i = 0; i < entryCount; i++) { - entries.Add(TpfFileEntry.Read(reader)); + entries.Add(TpfFileEntry.Read(reader, gameVersion)); } Entries = entries; diff --git a/BinderTool.Core/Tpf/TpfFileEntry.cs b/BinderTool.Core/Tpf/TpfFileEntry.cs index 0e56599..d1544e7 100644 --- a/BinderTool.Core/Tpf/TpfFileEntry.cs +++ b/BinderTool.Core/Tpf/TpfFileEntry.cs @@ -1,9 +1,13 @@ using System.IO; +using BinderTool.Core.Dds; +using BinderTool.Core.Dds.Enum; namespace BinderTool.Core.Tpf { public class TpfFileEntry { + public GameVersion GameVersion { get; set; } + public byte Format { get; set; } public byte Type { get; set; } @@ -14,13 +18,20 @@ public class TpfFileEntry public int Unknown { get; set; } - public string FileName { get; set; } + public int DxgiFormat { get; set; } + + public short Height { get; set; } + + public short Width { get; set; } public byte[] Data { get; set; } - public static TpfFileEntry Read(BinaryReader reader) + public string FileName { get; set; } + + public static TpfFileEntry Read(BinaryReader reader, GameVersion gameVersion) { TpfFileEntry result = new TpfFileEntry(); + result.GameVersion = gameVersion; int dataOffset = reader.ReadInt32(); int dataSize = reader.ReadInt32(); @@ -42,12 +53,27 @@ public static TpfFileEntry Read(BinaryReader reader) // 113 = ? result.Format = reader.ReadByte(); result.Type = reader.ReadByte(); // 0 = texture, 1 = cubemap - result.MipMapCount = reader.ReadByte(); - result.Flags = reader.ReadByte(); // 00 - int fileNameOffset = reader.ReadInt32(); - result.Unknown = reader.ReadInt32(); // 0 = ?, 1 = ? + result.MipMapCount = reader.ReadByte(); + result.Flags = reader.ReadByte(); + int fileNameOffset; + if (gameVersion == GameVersion.Bloodborne) + { + result.Width = reader.ReadInt16(); + result.Height = reader.ReadInt16(); + int unknown1 = reader.ReadInt32(); // 1 + int unknown2 = reader.ReadInt32(); // 13 + fileNameOffset = reader.ReadInt32(); + int unknown3 = reader.ReadInt32(); // 0 + result.DxgiFormat = reader.ReadInt32(); + } + else + { + fileNameOffset = reader.ReadInt32(); + result.Unknown = reader.ReadInt32(); // 0 = ?, 1 = ? + } + long position = reader.GetPosition(); if (fileNameOffset > 0) { @@ -66,6 +92,50 @@ public static TpfFileEntry Read(BinaryReader reader) return result; } + + public void Write(Stream outputStream) + { + if (Data == null) + { + return; + } + byte[] data = Data; + if (GameVersion == GameVersion.Bloodborne) + { + DxgiFormat dxgiFormat = (DxgiFormat) DxgiFormat; + DdsPixelFormat pixelFormat = DdsPixelFormat.DxgiToDdsPixelFormat(dxgiFormat); + DdsFile ddsFile = new DdsFile + { + Header = new DdsFileHeader + { + Flags = DdsFileHeaderFlags.Texture | (MipMapCount > 1 ? DdsFileHeaderFlags.MipMap : 0), + Height = Height, + Width = Width, + Depth = 0, + PitchOrLinearSize = Data.Length, + MipMapCount = MipMapCount, + PixelFormat = pixelFormat, + Caps = DdsSurfaceFlags.Texture | (MipMapCount > 1 ? DdsSurfaceFlags.MipMap : 0) + }, + HeaderDx10 = pixelFormat.FourCc != DdsPixelFormat.Dx10FourCc + ? null + : new DdsFileHeaderDx10 + { + Format = dxgiFormat, + ResourceDimension = D3D10ResourceDimension.Texture2D, + MiscFlag = 0, + ArraySize = 1, + MiscFlags2 = 0 + }, + Data = DdsFile.ConvertData(data, Height, Width, dxgiFormat) + }; + ddsFile.Write(outputStream); + } + else + { + outputStream.Write(data, 0, data.Length); + } + } } } \ No newline at end of file diff --git a/BinderTool/Program.cs b/BinderTool/Program.cs index 23c1aa9..eba2f75 100644 --- a/BinderTool/Program.cs +++ b/BinderTool/Program.cs @@ -97,7 +97,7 @@ private static void ShowUsageInfo() { Console.WriteLine( "BinderTool by Atvaark\n" + - " A tool for unpacking Dark Souls II/III Bdt, Bhd, Dcx, Sl2, Tpf, Param and Fmg files\n" + + " A tool for unpacking Dark Souls II/III/Bloodborne Bdt, Bhd, Dcx, Sl2, Tpf, Param and Fmg files\n" + "Usage:\n" + " BinderTool file_path [output_path]\n" + "Examples:\n" + @@ -583,7 +583,10 @@ private static void UnpackTpfFile(Options options) { string outputFilePath = Path.Combine(options.OutputPath, entry.FileName); Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath)); - File.WriteAllBytes(outputFilePath, entry.Data); + using (var outputStream = File.Create(outputFilePath)) + { + entry.Write(outputStream); + } } } } diff --git a/BinderTool/Properties/AssemblyInfo.cs b/BinderTool/Properties/AssemblyInfo.cs index 0e09cc0..51a1022 100644 --- a/BinderTool/Properties/AssemblyInfo.cs +++ b/BinderTool/Properties/AssemblyInfo.cs @@ -6,10 +6,10 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BinderTool")] -[assembly: AssemblyCopyright("Copyright © 2016 Atvaark")] +[assembly: AssemblyCopyright("Copyright © 2018 Atvaark")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("5d388695-6078-4404-8f5a-0bb1a94df97b")] -[assembly: AssemblyVersion("0.5.1.0")] -[assembly: AssemblyFileVersion("0.5.1.0")] +[assembly: AssemblyVersion("0.5.2.0")] +[assembly: AssemblyFileVersion("0.5.2.0")]