<commit_before>//!  Decoding of DDS images
//!
//!  DDS (DirectDraw Surface) is a container format for storing DXT (S3TC) compressed images.
//!
//!  # Related Links
//!  * <https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide> - Description of the DDS format.

use std::io::Read;
use std::{error, fmt};

use byteorder::{LittleEndian, ReadBytesExt};

#[allow(deprecated)]
use crate::codecs::dxt::{DxtDecoder, DxtReader, DxtVariant};
use crate::color::ColorType;
use crate::error::{
    DecodingError, ImageError, ImageFormatHint, ImageResult, UnsupportedError, UnsupportedErrorKind,
};
use crate::image::{ImageDecoder, ImageFormat};

/// Errors that can occur during decoding and parsing a DDS image
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum DecoderError {
    /// Wrong DDS channel width
    PixelFormatSizeInvalid(u32),
    /// Wrong DDS header size
    HeaderSizeInvalid(u32),
    /// Wrong DDS header flags
    HeaderFlagsInvalid(u32),

    /// Invalid DXGI format in DX10 header
    DxgiFormatInvalid(u32),
    /// Invalid flags in DX10 header
    Dx10FlagsInvalid(u32),
    /// Invalid array size in DX10 header
    Dx10ArraySizeInvalid(u32),

    /// DDS "DDS " signature invalid or missing
    DdsSignatureInvalid,
}

impl fmt::Display for DecoderError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            DecoderError::PixelFormatSizeInvalid(s) => {
                f.write_fmt(format_args!("Invalid DDS PixelFormat size: {}", s))
            }
            DecoderError::HeaderSizeInvalid(s) => {
                f.write_fmt(format_args!("Invalid DDS header size: {}", s))
            }
            DecoderError::HeaderFlagsInvalid(fs) => {
                f.write_fmt(format_args!("Invalid DDS header flags: {:#010X}", fs))
            }
            DecoderError::DxgiFormatInvalid(df) => {
                f.write_fmt(format_args!("Invalid DDS DXGI format: {}", df))
            }
            DecoderError::Dx10FlagsInvalid(fs) => {
                f.write_fmt(format_args!("Invalid DDS DX10 header flags: {:#010X}", fs))
            }
            DecoderError::Dx10ArraySizeInvalid(s) => {
                f.write_fmt(format_args!("Invalid DDS DX10 array size: {}", s))
            }
            DecoderError::DdsSignatureInvalid => f.write_str("DDS signature not found"),
        }
    }
}

impl From<DecoderError> for ImageError {
    fn from(e: DecoderError) -> ImageError {
        ImageError::Decoding(DecodingError::new(ImageFormat::Dds.into(), e))
    }
}

impl error::Error for DecoderError {}

/// Header used by DDS image files
#[derive(Debug)]
struct Header {
    flags: u32,
    height: u32,
    width: u32,
    pitch_or_linear_size: u32,
    depth: u32,
    mipmap_count: u32,
    pixel_format: PixelFormat,
    caps: u32,
    caps2: u32,
}

/// Extended DX10 header used by some DDS image files
#[derive(Debug)]
struct DX10Header {
    dxgi_format: u32,
    resource_dimension: u32,
    misc_flag: u32,
    array_size: u32,
    misc_flags_2: u32,
}

/// DDS pixel format
#[derive(Debug)]
struct PixelFormat {
    flags: u32,
    fourcc: [u8; 4],
    rgb_bit_count: u32,
    r_bit_mask: u32,
    g_bit_mask: u32,
    b_bit_mask: u32,
    a_bit_mask: u32,
}

impl PixelFormat {
    fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
        let size = r.read_u32::<LittleEndian>()?;
        if size != 32 {
            return Err(DecoderError::PixelFormatSizeInvalid(size).into());
        }

        Ok(Self {
            flags: r.read_u32::<LittleEndian>()?,
            fourcc: {
                let mut v = [0; 4];
                r.read_exact(&mut v)?;
                v
            },
            rgb_bit_count: r.read_u32::<LittleEndian>()?,
            r_bit_mask: r.read_u32::<LittleEndian>()?,
            g_bit_mask: r.read_u32::<LittleEndian>()?,
            b_bit_mask: r.read_u32::<LittleEndian>()?,
            a_bit_mask: r.read_u32::<LittleEndian>()?,
        })
    }
}

impl Header {
    fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
        let size = r.read_u32::<LittleEndian>()?;
        if size != 124 {
            return Err(DecoderError::HeaderSizeInvalid(size).into());
        }

        const REQUIRED_FLAGS: u32 = 0x1 | 0x2 | 0x4 | 0x1000;
        const VALID_FLAGS: u32 = 0x1 | 0x2 | 0x4 | 0x8 | 0x1000 | 0x20000 | 0x80000 | 0x800000;
        let flags = r.read_u32::<LittleEndian>()?;
        if flags & (REQUIRED_FLAGS | !VALID_FLAGS) != REQUIRED_FLAGS {
            return Err(DecoderError::HeaderFlagsInvalid(flags).into());
        }

        let height = r.read_u32::<LittleEndian>()?;
        let width = r.read_u32::<LittleEndian>()?;
        let pitch_or_linear_size = r.read_u32::<LittleEndian>()?;
        let depth = r.read_u32::<LittleEndian>()?;
        let mipmap_count = r.read_u32::<LittleEndian>()?;
        // Skip `dwReserved1`
        {
            let mut skipped = [0; 4 * 11];
            r.read_exact(&mut skipped)?;
        }
        let pixel_format = PixelFormat::from_reader(r)?;
        let caps = r.read_u32::<LittleEndian>()?;
        let caps2 = r.read_u32::<LittleEndian>()?;
        // Skip `dwCaps3`, `dwCaps4`, `dwReserved2` (unused)
        {
            let mut skipped = [0; 4 + 4 + 4];
            r.read_exact(&mut skipped)?;
        }

        Ok(Self {
            flags,
            height,
            width,
            pitch_or_linear_size,
            depth,
            mipmap_count,
            pixel_format,
            caps,
            caps2,
        })
    }
}

impl DX10Header {
    fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
        let dxgi_format = r.read_u32::<LittleEndian>()?;
        let resource_dimension = r.read_u32::<LittleEndian>()?;
        let misc_flag = r.read_u32::<LittleEndian>()?;
        let array_size = r.read_u32::<LittleEndian>()?;
        let misc_flags_2 = r.read_u32::<LittleEndian>()?;

        let dx10_header = Self {
            dxgi_format,
            resource_dimension,
            misc_flag,
            array_size,
            misc_flags_2,
        };
        dx10_header.validate()?;

        Ok(dx10_header)
    }

    fn validate(&self) -> Result<(), ImageError> {
        // Note: see https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header-dxt10 for info on valid values
        if self.dxgi_format < 0 || self.dxgi_format > 132 {
            // Invalid format
            return Err(DecoderError::DxgiFormatInvalid(self.dxgi_format).into());
        }

        if self.misc_flag != 0x4 && self.misc_flag != 0x4 {
            // Invalid flag
            // Only no (0x0) and DDS_RESOURCE_MISC_TEXTURECUBE (0x4) flags are allowed
            return Err(DecoderError::Dx10FlagsInvalid(self.misc_flag).into());
        }

        if self.resource_dimension == 4 && self.array_size != 1 {
            // Invalid array size
            // 3D textures (resource dimension == 4) must have an array size of 1
            return Err(DecoderError::Dx10ArraySizeInvalid(self.array_size).into());
        }

        if self.misc_flags_2 < 0x0 || self.misc_flags_2 > 0x4 {<commit_msg>Add missing DX10 resource dimension validation<commit_after>//!  Decoding of DDS images
//!
//!  DDS (DirectDraw Surface) is a container format for storing DXT (S3TC) compressed images.
//!
//!  # Related Links
//!  * <https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide> - Description of the DDS format.

use std::io::Read;
use std::{error, fmt};

use byteorder::{LittleEndian, ReadBytesExt};

#[allow(deprecated)]
use crate::codecs::dxt::{DxtDecoder, DxtReader, DxtVariant};
use crate::color::ColorType;
use crate::error::{
    DecodingError, ImageError, ImageFormatHint, ImageResult, UnsupportedError, UnsupportedErrorKind,
};
use crate::image::{ImageDecoder, ImageFormat};

/// Errors that can occur during decoding and parsing a DDS image
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum DecoderError {
    /// Wrong DDS channel width
    PixelFormatSizeInvalid(u32),
    /// Wrong DDS header size
    HeaderSizeInvalid(u32),
    /// Wrong DDS header flags
    HeaderFlagsInvalid(u32),

    /// Invalid DXGI format in DX10 header
    DxgiFormatInvalid(u32),
    /// Invalid resource dimension
    ResourceDimensionInvalid(u32),
    /// Invalid flags in DX10 header
    Dx10FlagsInvalid(u32),
    /// Invalid array size in DX10 header
    Dx10ArraySizeInvalid(u32),

    /// DDS "DDS " signature invalid or missing
    DdsSignatureInvalid,
}

impl fmt::Display for DecoderError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            DecoderError::PixelFormatSizeInvalid(s) => {
                f.write_fmt(format_args!("Invalid DDS PixelFormat size: {}", s))
            }
            DecoderError::HeaderSizeInvalid(s) => {
                f.write_fmt(format_args!("Invalid DDS header size: {}", s))
            }
            DecoderError::HeaderFlagsInvalid(fs) => {
                f.write_fmt(format_args!("Invalid DDS header flags: {:#010X}", fs))
            }
            DecoderError::DxgiFormatInvalid(df) => {
                f.write_fmt(format_args!("Invalid DDS DXGI format: {}", df))
            }
            DecoderError::ResourceDimensionInvalid(d) => {
                f.write_fmt(format_args!("Invalid DDS resource dimension: {}", d))
            }
            DecoderError::Dx10FlagsInvalid(fs) => {
                f.write_fmt(format_args!("Invalid DDS DX10 header flags: {:#010X}", fs))
            }
            DecoderError::Dx10ArraySizeInvalid(s) => {
                f.write_fmt(format_args!("Invalid DDS DX10 array size: {}", s))
            }
            DecoderError::DdsSignatureInvalid => f.write_str("DDS signature not found"),
        }
    }
}

impl From<DecoderError> for ImageError {
    fn from(e: DecoderError) -> ImageError {
        ImageError::Decoding(DecodingError::new(ImageFormat::Dds.into(), e))
    }
}

impl error::Error for DecoderError {}

/// Header used by DDS image files
#[derive(Debug)]
struct Header {
    flags: u32,
    height: u32,
    width: u32,
    pitch_or_linear_size: u32,
    depth: u32,
    mipmap_count: u32,
    pixel_format: PixelFormat,
    caps: u32,
    caps2: u32,
}

/// Extended DX10 header used by some DDS image files
#[derive(Debug)]
struct DX10Header {
    dxgi_format: u32,
    resource_dimension: u32,
    misc_flag: u32,
    array_size: u32,
    misc_flags_2: u32,
}

/// DDS pixel format
#[derive(Debug)]
struct PixelFormat {
    flags: u32,
    fourcc: [u8; 4],
    rgb_bit_count: u32,
    r_bit_mask: u32,
    g_bit_mask: u32,
    b_bit_mask: u32,
    a_bit_mask: u32,
}

impl PixelFormat {
    fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
        let size = r.read_u32::<LittleEndian>()?;
        if size != 32 {
            return Err(DecoderError::PixelFormatSizeInvalid(size).into());
        }

        Ok(Self {
            flags: r.read_u32::<LittleEndian>()?,
            fourcc: {
                let mut v = [0; 4];
                r.read_exact(&mut v)?;
                v
            },
            rgb_bit_count: r.read_u32::<LittleEndian>()?,
            r_bit_mask: r.read_u32::<LittleEndian>()?,
            g_bit_mask: r.read_u32::<LittleEndian>()?,
            b_bit_mask: r.read_u32::<LittleEndian>()?,
            a_bit_mask: r.read_u32::<LittleEndian>()?,
        })
    }
}

impl Header {
    fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
        let size = r.read_u32::<LittleEndian>()?;
        if size != 124 {
            return Err(DecoderError::HeaderSizeInvalid(size).into());
        }

        const REQUIRED_FLAGS: u32 = 0x1 | 0x2 | 0x4 | 0x1000;
        const VALID_FLAGS: u32 = 0x1 | 0x2 | 0x4 | 0x8 | 0x1000 | 0x20000 | 0x80000 | 0x800000;
        let flags = r.read_u32::<LittleEndian>()?;
        if flags & (REQUIRED_FLAGS | !VALID_FLAGS) != REQUIRED_FLAGS {
            return Err(DecoderError::HeaderFlagsInvalid(flags).into());
        }

        let height = r.read_u32::<LittleEndian>()?;
        let width = r.read_u32::<LittleEndian>()?;
        let pitch_or_linear_size = r.read_u32::<LittleEndian>()?;
        let depth = r.read_u32::<LittleEndian>()?;
        let mipmap_count = r.read_u32::<LittleEndian>()?;
        // Skip `dwReserved1`
        {
            let mut skipped = [0; 4 * 11];
            r.read_exact(&mut skipped)?;
        }
        let pixel_format = PixelFormat::from_reader(r)?;
        let caps = r.read_u32::<LittleEndian>()?;
        let caps2 = r.read_u32::<LittleEndian>()?;
        // Skip `dwCaps3`, `dwCaps4`, `dwReserved2` (unused)
        {
            let mut skipped = [0; 4 + 4 + 4];
            r.read_exact(&mut skipped)?;
        }

        Ok(Self {
            flags,
            height,
            width,
            pitch_or_linear_size,
            depth,
            mipmap_count,
            pixel_format,
            caps,
            caps2,
        })
    }
}

impl DX10Header {
    fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
        let dxgi_format = r.read_u32::<LittleEndian>()?;
        let resource_dimension = r.read_u32::<LittleEndian>()?;
        let misc_flag = r.read_u32::<LittleEndian>()?;
        let array_size = r.read_u32::<LittleEndian>()?;
        let misc_flags_2 = r.read_u32::<LittleEndian>()?;

        let dx10_header = Self {
            dxgi_format,
            resource_dimension,
            misc_flag,
            array_size,
            misc_flags_2,
        };
        dx10_header.validate()?;

        Ok(dx10_header)
    }

    fn validate(&self) -> Result<(), ImageError> {
        // Note: see https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header-dxt10 for info on valid values
        if self.dxgi_format < 0 || self.dxgi_format > 132 {
            // Invalid format
            return Err(DecoderError::DxgiFormatInvalid(self.dxgi_format).into());
        }

        if self.resource_dimension < 2 || self.resource_dimension > 4 {
            // Invalid dimension
            // Only 1D (2), 2D (3) and 3D (4) resource dimensions are allowed
            return Err(DecoderError::ResourceDimensionInvalid(self.resource_dimension).into());
        }

        if self.misc_flag != 0x0 && self.misc_flag != 0x4 {
            // Invalid flag
            // Only no (0x0) and DDS_RESOURCE_MISC_TEXTURECUBE (0x4) flags are allowed
            return Err(DecoderError::Dx10FlagsInvalid(self.misc_flag).into());
        }

        if self.resource_dimension == 4 && self.array_size != 1 {
            // Invalid array size
            // 3D textures (resource dimension == 4) must have an array size of 1
            return Err(DecoderError::Dx10ArraySizeInvalid(self.array_size).into());
        }

        if self.misc_flags_2 < 0x0 || self.misc_flags_2 > 0x4 {
{'fraction_non_alphanumeric': 0.1307645834731825, 'fraction_numerical': 0.032758273477881454, 'mean_word_length': 2.3254464285714285, 'pattern_counts': {'":': 0, '<': 64, '<?xml version=': 0, '>': 91, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2310604', 'n_tokens_mistral': 5162, 'n_tokens_neox': 4804, 'n_words': 1026}