From 267b4885345823ac601e48a0ac0849550b717816 Mon Sep 17 00:00:00 2001 From: Josh Simmons Date: Tue, 14 May 2024 09:28:49 +0200 Subject: [PATCH] narcissus-core: Add basic dds loader --- engine/narcissus-core/src/dds.rs | 291 +++++++++++++++++++++++++++++++ engine/narcissus-core/src/lib.rs | 1 + 2 files changed, 292 insertions(+) create mode 100644 engine/narcissus-core/src/dds.rs diff --git a/engine/narcissus-core/src/dds.rs b/engine/narcissus-core/src/dds.rs new file mode 100644 index 0000000..3fe266a --- /dev/null +++ b/engine/narcissus-core/src/dds.rs @@ -0,0 +1,291 @@ +#![allow(non_camel_case_types)] + +use std::mem::size_of; + +use crate::{flags_def, fourcc, FourCC}; + +const DDS_FOURCC: FourCC = fourcc!("DDS "); +const DX10_FOURCC: FourCC = fourcc!("DX10"); + +#[derive(Clone, Copy, Debug)] +pub enum LoadError { + TooSmall, + BadMagic, + BadHeader, +} + +#[derive(Debug)] +pub struct Dds<'a> { + pub header: DdsHeader, + pub header_dxt10: Option, + pub data: &'a [u8], +} + +impl<'a> Dds<'a> { + pub fn from_buffer(buf: &'a [u8]) -> Result { + if buf.len() < 4 + size_of::() { + return Err(LoadError::TooSmall); + } + + let magic: [u8; 4] = buf[..4].try_into().unwrap(); + let magic: FourCC = magic.into(); + if magic != DDS_FOURCC { + return Err(LoadError::BadMagic); + } + + let header = DdsHeader::from_bytes(buf[4..4 + size_of::()].try_into().unwrap()); + + if header.size != size_of::() as u32 + || header.pixel_format.size != size_of::() as u32 + { + return Err(LoadError::BadHeader); + } + + let header_dxt10 = if header + .pixel_format + .flags + .contains(DdsPixelFormatFlags::FOURCC) + && header.pixel_format.four_cc == DX10_FOURCC + { + if buf.len() < 4 + size_of::() + size_of::() { + return Err(LoadError::TooSmall); + } + + Some(DdsHeaderDxt10::from_bytes( + buf[128..128 + size_of::()] + .try_into() + .unwrap(), + )) + } else { + None + }; + + let offset = if header_dxt10.is_some() { + 4 + size_of::() + size_of::() + } else { + 4 + size_of::() + }; + + Ok(Dds { + header, + header_dxt10, + data: &buf[offset..], + }) + } +} + +#[repr(C)] +#[derive(Clone, Copy, Default, Debug)] +pub struct DdsHeader { + size: u32, + pub flags: u32, + pub height: u32, + pub width: u32, + pub pitch_or_linear_size: u32, + pub depth: u32, + pub mip_map_count: u32, + _reserved: [u32; 11], + pub pixel_format: DdsPixelFormat, + pub caps: u32, + pub caps2: u32, + pub caps3: u32, + pub caps4: u32, + _reserved2: u32, +} + +impl DdsHeader { + fn from_bytes(buf: [u8; size_of::()]) -> Self { + // SAFETY: + // * Array is the exact size of DdsHeader. + // * All bit patterns are valid for DdsHeader. + // * We use transmute_copy to resolve alignment concerns. + unsafe { std::mem::transmute_copy::<[u8; size_of::()], DdsHeader>(&buf) } + } +} + +#[repr(C)] +#[derive(Clone, Copy, Default, Debug)] +pub struct DdsHeaderDxt10 { + pub dxgi_format: DxgiFormat, + pub resource_dimension: D3D10ResourceDimension, + pub misc_flags: u32, + pub array_size: u32, + pub misc_flags2: u32, +} + +impl DdsHeaderDxt10 { + fn from_bytes(buf: [u8; size_of::()]) -> Self { + // SAFETY: + // * Array is the exact size of DdsHeaderDxt10. + // * All bit patterns are valid for DdsHeaderDxt10. + // * We use transmute_copy to resolve alignment concerns. + unsafe { + std::mem::transmute_copy::<[u8; size_of::()], DdsHeaderDxt10>(&buf) + } + } +} + +flags_def!(DdsPixelFormatFlags); + +impl DdsPixelFormatFlags { + pub const ALPHA_PIXELS: Self = Self(0x1); + pub const ALPHA: Self = Self(0x2); + pub const FOURCC: Self = Self(0x4); + pub const RGB: Self = Self(0x40); + pub const YUV: Self = Self(0x200); + pub const LUMINANCE: Self = Self(0x20000); +} + +#[repr(C)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Debug)] +pub struct DdsPixelFormat { + size: u32, + pub flags: DdsPixelFormatFlags, + pub four_cc: FourCC, + pub rgb_bit_count: u32, + pub r_bit_mask: u32, + pub g_bit_mask: u32, + pub b_bit_mask: u32, + pub a_bit_mask: u32, +} + +#[repr(u32)] +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Debug)] +pub enum DxgiFormat { + #[default] + UNKNOWN = 0, + R32G32B32A32_TYPELESS = 1, + R32G32B32A32_FLOAT = 2, + R32G32B32A32_UINT = 3, + R32G32B32A32_SINT = 4, + R32G32B32_TYPELESS = 5, + R32G32B32_FLOAT = 6, + R32G32B32_UINT = 7, + R32G32B32_SINT = 8, + R16G16B16A16_TYPELESS = 9, + R16G16B16A16_FLOAT = 10, + R16G16B16A16_UNORM = 11, + R16G16B16A16_UINT = 12, + R16G16B16A16_SNORM = 13, + R16G16B16A16_SINT = 14, + R32G32_TYPELESS = 15, + R32G32_FLOAT = 16, + R32G32_UINT = 17, + R32G32_SINT = 18, + R32G8X24_TYPELESS = 19, + D32_FLOAT_S8X24_UINT = 20, + R32_FLOAT_X8X24_TYPELESS = 21, + X32_TYPELESS_G8X24_UINT = 22, + R10G10B10A2_TYPELESS = 23, + R10G10B10A2_UNORM = 24, + R10G10B10A2_UINT = 25, + R11G11B10_FLOAT = 26, + R8G8B8A8_TYPELESS = 27, + R8G8B8A8_UNORM = 28, + R8G8B8A8_UNORM_SRGB = 29, + R8G8B8A8_UINT = 30, + R8G8B8A8_SNORM = 31, + R8G8B8A8_SINT = 32, + R16G16_TYPELESS = 33, + R16G16_FLOAT = 34, + R16G16_UNORM = 35, + R16G16_UINT = 36, + R16G16_SNORM = 37, + R16G16_SINT = 38, + R32_TYPELESS = 39, + D32_FLOAT = 40, + R32_FLOAT = 41, + R32_UINT = 42, + R32_SINT = 43, + R24G8_TYPELESS = 44, + D24_UNORM_S8_UINT = 45, + R24_UNORM_X8_TYPELESS = 46, + X24_TYPELESS_G8_UINT = 47, + R8G8_TYPELESS = 48, + R8G8_UNORM = 49, + R8G8_UINT = 50, + R8G8_SNORM = 51, + R8G8_SINT = 52, + R16_TYPELESS = 53, + R16_FLOAT = 54, + D16_UNORM = 55, + R16_UNORM = 56, + R16_UINT = 57, + R16_SNORM = 58, + R16_SINT = 59, + R8_TYPELESS = 60, + R8_UNORM = 61, + R8_UINT = 62, + R8_SNORM = 63, + R8_SINT = 64, + A8_UNORM = 65, + R1_UNORM = 66, + R9G9B9E5_SHAREDEXP = 67, + R8G8_B8G8_UNORM = 68, + G8R8_G8B8_UNORM = 69, + BC1_TYPELESS = 70, + BC1_UNORM = 71, + BC1_UNORM_SRGB = 72, + BC2_TYPELESS = 73, + BC2_UNORM = 74, + BC2_UNORM_SRGB = 75, + BC3_TYPELESS = 76, + BC3_UNORM = 77, + BC3_UNORM_SRGB = 78, + BC4_TYPELESS = 79, + BC4_UNORM = 80, + BC4_SNORM = 81, + BC5_TYPELESS = 82, + BC5_UNORM = 83, + BC5_SNORM = 84, + B5G6R5_UNORM = 85, + B5G5R5A1_UNORM = 86, + B8G8R8A8_UNORM = 87, + B8G8R8X8_UNORM = 88, + R10G10B10_XR_BIAS_A2_UNORM = 89, + B8G8R8A8_TYPELESS = 90, + B8G8R8A8_UNORM_SRGB = 91, + B8G8R8X8_TYPELESS = 92, + B8G8R8X8_UNORM_SRGB = 93, + BC6H_TYPELESS = 94, + BC6H_UF16 = 95, + BC6H_SF16 = 96, + BC7_TYPELESS = 97, + BC7_UNORM = 98, + BC7_UNORM_SRGB = 99, + AYUV = 100, + Y410 = 101, + Y416 = 102, + NV12 = 103, + P010 = 104, + P016 = 105, + YUV_420_OPAQUE = 106, + YUY2 = 107, + Y210 = 108, + Y216 = 109, + NV11 = 110, + AI44 = 111, + IA44 = 112, + P8 = 113, + A8P8 = 114, + B4G4R4A4_UNORM = 115, + P208 = 130, + V208 = 131, + V408 = 132, + SAMPLER_FEEDBACK_MIN_MIP_OPAQUE, + SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE, + FORCE_UINT = 0xffffffff, +} + +#[repr(u32)] +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Debug)] +pub enum D3D10ResourceDimension { + #[default] + Unknown = 0, + Buffer = 1, + Texture1d = 2, + Texture2d = 3, + Texture3d = 4, +} diff --git a/engine/narcissus-core/src/lib.rs b/engine/narcissus-core/src/lib.rs index d351b9d..ab822d3 100644 --- a/engine/narcissus-core/src/lib.rs +++ b/engine/narcissus-core/src/lib.rs @@ -1,5 +1,6 @@ mod arena; mod bitset; +pub mod dds; mod directory; mod finite; mod fixed_vec; -- 2.49.0