using AC2RE.Definitions; namespace LandblockExtraction.Tools; public static class DDSHeader { //MagicNumber for DDS file. private static readonly uint MAGICNUMBER = 0x20534444; //Length of header private static readonly int LENGTH = 125; public static byte[] Generate(RenderSurface renderSurface) { if (renderSurface.pixelFormat == PixelFormat.CUSTOM_RAW_JPEG) return renderSurface.sourceData; DDS_HEADER header = genHeadStruct(renderSurface.pixelFormat, renderSurface.height, renderSurface.width); using (MemoryStream stream = new MemoryStream(LENGTH + renderSurface.sourceData.Length)) { stream.Write(BitConverter.GetBytes(MAGICNUMBER)); stream.Write(BitConverter.GetBytes(header.dwSize)); stream.Write(BitConverter.GetBytes((uint)header.dwFlags)); stream.Write(BitConverter.GetBytes(header.dwHeight)); stream.Write(BitConverter.GetBytes(header.dwWidth)); stream.Write(BitConverter.GetBytes(header.dwPitchOrLinearSize)); stream.Write(BitConverter.GetBytes(header.dwDepth)); stream.Write(BitConverter.GetBytes(header.dwMipMapCount)); foreach (var i in header.dwReserved1) stream.Write(BitConverter.GetBytes(i)); stream.Write(BitConverter.GetBytes(header.ddspf.dwSize)); stream.Write(BitConverter.GetBytes((uint)header.ddspf.dwFlags)); stream.Write(BitConverter.GetBytes(header.ddspf.dwFourCC)); stream.Write(BitConverter.GetBytes(header.ddspf.dwRGBBitCount)); stream.Write(BitConverter.GetBytes(header.ddspf.dwRBitMask)); stream.Write(BitConverter.GetBytes(header.ddspf.dwGBitMask)); stream.Write(BitConverter.GetBytes(header.ddspf.dwBBitMask)); stream.Write(BitConverter.GetBytes(header.ddspf.dwABitMask)); stream.Write(BitConverter.GetBytes(header.dwCaps)); stream.Write(BitConverter.GetBytes(header.dwCaps2)); stream.Write(BitConverter.GetBytes(header.dwCaps3)); stream.Write(BitConverter.GetBytes(header.dwCaps4)); stream.Write(BitConverter.GetBytes(header.dwReserved2)); stream.Write(renderSurface.sourceData); return stream.ToArray(); } } private static DDS_HEADER genHeadStruct(PixelFormat format, uint height, uint width) { //I think always same each format? DDS_HEADER dDS_HEADER = new DDS_HEADER(); dDS_HEADER.dwFlags |= DDSHeaderFlags.DDSD_CAPS | DDSHeaderFlags.DDSD_HEIGHT | DDSHeaderFlags.DDSD_WIDTH | DDSHeaderFlags.DDSD_PIXELFORMAT; if (dDS_HEADER.dwFlags.HasFlag(DDSHeaderFlags.DDSD_HEIGHT)) dDS_HEADER.dwHeight = height; if (dDS_HEADER.dwFlags.HasFlag(DDSHeaderFlags.DDSD_WIDTH)) dDS_HEADER.dwWidth = width; if (format == PixelFormat.DXT1) { dDS_HEADER.dwFlags |= DDSHeaderFlags.DDSD_LINEARSIZE; dDS_HEADER.dwPitchOrLinearSize = CalculateCompressedBlockSize(height, width, (uint)new byte[8].Length); } if (format == PixelFormat.DXT3 || format == PixelFormat.DXT2 || format == PixelFormat.DXT4 || format == PixelFormat.DXT5) { dDS_HEADER.dwFlags |= DDSHeaderFlags.DDSD_LINEARSIZE; dDS_HEADER.dwPitchOrLinearSize = CalculateCompressedBlockSize(height, width, (uint)new byte[16].Length); } if (dDS_HEADER.dwFlags.HasFlag(DDSHeaderFlags.DDSD_PIXELFORMAT)) dDS_HEADER.ddspf = genPixelformatStruct(format); return dDS_HEADER; } private static DDS_PIXELFORMAT genPixelformatStruct(PixelFormat surface) { DDS_PIXELFORMAT dDS_PIXELFORMAT = new DDS_PIXELFORMAT(); switch (surface) { case PixelFormat.DXT1: case PixelFormat.DXT3: case PixelFormat.DXT4: case PixelFormat.DXT5: dDS_PIXELFORMAT.dwFlags |= DDSPixelformatFlags.DDPF_FOURCC; dDS_PIXELFORMAT.dwFourCC = (uint)surface; break; case PixelFormat.A8R8G8B8: dDS_PIXELFORMAT.dwFlags |= DDSPixelformatFlags.DDPF_ALPHAPIXELS | DDSPixelformatFlags.DDPF_RGB; dDS_PIXELFORMAT.dwRGBBitCount = 0x20; dDS_PIXELFORMAT.dwRBitMask = 0x00FF0000; dDS_PIXELFORMAT.dwGBitMask = 0x0000FF00; dDS_PIXELFORMAT.dwBBitMask = 0x000000FF; dDS_PIXELFORMAT.dwABitMask = 0xFF000000; break; case PixelFormat.R8G8B8: dDS_PIXELFORMAT.dwFlags |= DDSPixelformatFlags.DDPF_RGB; dDS_PIXELFORMAT.dwRGBBitCount = 0x18; dDS_PIXELFORMAT.dwRBitMask = 0x00FF0000; dDS_PIXELFORMAT.dwGBitMask = 0x0000FF00; dDS_PIXELFORMAT.dwBBitMask = 0x000000FF; break; case PixelFormat.A8: dDS_PIXELFORMAT.dwFlags |= DDSPixelformatFlags.DDPF_ALPHAPIXELS; dDS_PIXELFORMAT.dwRGBBitCount = 0x8; dDS_PIXELFORMAT.dwABitMask = 0xFF; break; default: dDS_PIXELFORMAT.dwFlags |= DDSPixelformatFlags.DDPF_FOURCC; break; } return dDS_PIXELFORMAT; } public static uint CalculateCompressedBlockSize(uint height, uint width, uint blockSize) { return ((width + 3) / 4) * ((height + 3) / 4) * blockSize; } } public struct DDS_HEADER { public uint dwSize; public DDSHeaderFlags dwFlags; public uint dwHeight; public uint dwWidth; public uint dwPitchOrLinearSize; public uint dwDepth; public uint dwMipMapCount; public uint[] dwReserved1; public DDS_PIXELFORMAT ddspf; public uint dwCaps; public uint dwCaps2; public uint dwCaps3; public uint dwCaps4; public uint dwReserved2; public DDS_HEADER() { dwSize = 124; //Fixed on 124 octets dwFlags = 0; dwHeight = 0; dwWidth = 0; dwPitchOrLinearSize = 0; dwDepth = 0; dwMipMapCount = 0; dwReserved1 = new uint[11]; foreach (var i in dwReserved1) dwReserved1[i] = 0x00000000; ddspf = new DDS_PIXELFORMAT(); dwCaps = 0x1000; dwCaps2 = 0; dwCaps3 = 0; dwCaps4 = 0; dwReserved2 = 0; } } public struct DDS_PIXELFORMAT { public uint dwSize; public DDSPixelformatFlags dwFlags; public uint dwFourCC; public uint dwRGBBitCount; public uint dwRBitMask; public uint dwGBitMask; public uint dwBBitMask; public uint dwABitMask; public DDS_PIXELFORMAT() { dwSize = 32; //Fixed on 32 octets dwFlags = 0; dwFourCC = 0; dwRGBBitCount = 0; dwRBitMask = 0; dwGBitMask = 0; dwBBitMask = 0; dwABitMask = 0; } } [Flags] public enum DDSHeaderFlags : uint { NONE = 0, DDSD_CAPS = 1 << 0, // 0x00000001 DDSD_HEIGHT = 1 << 1, // 0x00000002 DDSD_WIDTH = 1 << 2, // 0x00000004 DDSD_PITCH = 1 << 3, // 0x00000008 DDSD_PIXELFORMAT = 1 << 12, // 0x00001000 DDSD_MIPMAPCOUNT = 1 << 17, // 0x00020000 DDSD_LINEARSIZE = 1 << 19, // 0x00080000 DDSD_DEPTH = 1 << 23, // 0x00800000 } [Flags] public enum DDSPixelformatFlags : uint { NONE = 0, DDPF_ALPHAPIXELS = 1 << 0, // 0x00000001 DDPF_ALPHA = 1 << 1, // 0x00000002 DDPF_FOURCC = 1 << 2, // 0x00000004 DDPF_RGB = 1 << 6, // 0x00000040 DDPF_YUV = 1 << 9, // 0x00000200 DDPF_LUMINANCE = 1 << 17, // 0x00020000 }