#pragma once
#include <array>
#include <cstring>
#include <optional>
#include <whiteout/common_types.h>
#include <whiteout/textures/texture.h>
namespace whiteout::textures::d2r_texture {
static constexpr u32 D2R_MAGIC = 0x2845443Cu;
static constexpr u8 D2R_TYPE_2D = 2;
static constexpr u32 D2R_RESERVED2 = 4;
static constexpr u32 D2R_HEADER_SIZE = 0x24; static constexpr u32 D2R_MIP_ENTRY_SIZE = 8;
static constexpr u16 D2R_FMT_RGBA8 = 31; static constexpr u16 D2R_FMT_BC1 = 58; static constexpr u16 D2R_FMT_BC3_LINEAR = 61; static constexpr u16 D2R_FMT_BC3_SRGB = 62; static constexpr u16 D2R_FMT_BC4 = 63; static constexpr u16 D2R_FMT_BC5 = 65;
#pragma pack(push, 1)
struct Header {
u32 magic; u16 format; u8 type; u8 mipCount; u32 width; u32 height; u32 arraySize; u32 reserved0; u32 reserved1; u32 mipCountU32;
u32 reserved2; };
struct MipEntry {
u32 size;
u32 offset;
};
#pragma pack(pop)
static_assert(sizeof(Header) == D2R_HEADER_SIZE, "D2R Header must be 36 bytes");
static_assert(sizeof(MipEntry) == D2R_MIP_ENTRY_SIZE, "D2R MipEntry must be 8 bytes");
struct FormatMapping {
PixelFormat format;
bool is_srgb; bool is_bgra; };
inline std::optional<FormatMapping> d2r_format_to_pixel_format(u16 code) {
switch (code) {
case D2R_FMT_RGBA8:
return FormatMapping{PixelFormat::RGBA8, false, true};
case D2R_FMT_BC1:
return FormatMapping{PixelFormat::BC1, true, false};
case D2R_FMT_BC3_LINEAR:
return FormatMapping{PixelFormat::BC3, false, false};
case D2R_FMT_BC3_SRGB:
return FormatMapping{PixelFormat::BC3, true, false};
case D2R_FMT_BC4:
return FormatMapping{PixelFormat::BC4, false, false};
case D2R_FMT_BC5:
return FormatMapping{PixelFormat::BC5, false, false};
default:
return std::nullopt;
}
}
inline std::optional<u16> pixel_format_to_d2r_format(PixelFormat fmt, bool is_srgb) {
switch (fmt) {
case PixelFormat::RGBA8:
return D2R_FMT_RGBA8;
case PixelFormat::BC1:
return D2R_FMT_BC1;
case PixelFormat::BC3:
return is_srgb ? D2R_FMT_BC3_SRGB : D2R_FMT_BC3_LINEAR;
case PixelFormat::BC4:
return D2R_FMT_BC4;
case PixelFormat::BC5:
return D2R_FMT_BC5;
default:
return std::nullopt;
}
}
inline void swizzle_bgra_rgba(const u8* src, u8* dst, u64 pixel_count) {
for (u64 i = 0; i < pixel_count; ++i) {
const u8* s = src + i * 4;
u8* d = dst + i * 4;
d[0] = s[2];
d[1] = s[1];
d[2] = s[0];
d[3] = s[3];
}
}
inline bool looks_like_d2r_texture(std::span<const u8> buffer) {
if (buffer.size() < D2R_HEADER_SIZE)
return false;
u32 magic = 0;
std::memcpy(&magic, buffer.data(), sizeof(u32));
if (magic != D2R_MAGIC)
return false;
u16 fmt = 0;
std::memcpy(&fmt, buffer.data() + 4, sizeof(u16));
return d2r_format_to_pixel_format(fmt).has_value();
}
}