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")]