image-blp 1.2.0

Library to encode/decode Blizzard BLP texture format
Documentation
use super::super::error::Error;
use super::super::types::Parser;
use crate::types::*;
use log::*;
use nom::{
    error::context,
    multi::count,
    number::complete::{le_u32, le_u8},
    Err,
};

pub fn parse_raw3<'a>(
    blp_header: &BlpHeader,
    original_input: &'a [u8],
    offsets: &[u32],
    sizes: &[u32],
    images: &mut Vec<Raw3Image>,
    input: &'a [u8],
) -> Parser<'a, ()> {
    let mut read_image = |i: usize| {
        let offset = offsets[i];
        let size = sizes[i];
        if offset as usize >= original_input.len() {
            error!(
                "Offset of mipmap {} is out of bounds! {} >= {}",
                i,
                offset,
                original_input.len()
            );
            return Err(Err::Failure(Error::<&[u8]>::OutOfBounds(0)));
        }
        if (offset + size) as usize > original_input.len() {
            error!(
                "Offset+size of mipmap {} is out of bounds! {} > {}",
                i,
                offset + size,
                original_input.len()
            );
            return Err(Err::Failure(Error::OutOfBounds(0)));
        }

        trace!("Expecting size of image: {}", size);
        let image_bytes = &original_input[offset as usize..(offset + size) as usize];
        trace!("We have {} bytes", image_bytes.len());
        let n = blp_header.mipmap_pixels(i);
        trace!(
            "For mipmap size {:?} we should fetch {} bytes",
            blp_header.mipmap_size(i),
            n * 4
        );
        let (_, pixels) = count(le_u32, n as usize)(image_bytes)?;

        images.push(Raw3Image { pixels });
        Ok(())
    };

    trace!("Mipmaps count: {}", blp_header.mipmaps_count());
    read_image(0)?;
    if blp_header.has_mipmaps() {
        for (i, &size) in sizes
            .iter()
            .enumerate()
            .take((blp_header.mipmaps_count() + 1).min(16))
            .skip(1)
        {
            if size == 0 {
                trace!("Size of mipmap {} is 0 bytes, I stop reading of images", i);
                break;
            }
            read_image(i)?;
        }
    }
    Ok((input, ()))
}

pub fn parse_dxtn<'a>(
    blp_header: &BlpHeader,
    dxtn: DxtnFormat,
    original_input: &'a [u8],
    offsets: &[u32],
    sizes: &[u32],
    images: &mut Vec<DxtnImage>,
    input: &'a [u8],
) -> Parser<'a, ()> {
    trace!("{:?}", blp_header);

    let mut read_image = |i: usize| {
        let offset = offsets[i];
        let size = sizes[i];
        if offset as usize >= original_input.len() {
            error!(
                "Offset of mipmap {} is out of bounds! {} >= {}",
                i,
                offset,
                original_input.len()
            );
            return Err(Err::Failure(Error::<&[u8]>::OutOfBounds(0)));
        }
        if (offset + size) as usize > original_input.len() {
            error!(
                "Offset+size of mipmap {} is out of bounds! {} > {}",
                i,
                offset + size,
                original_input.len()
            );
            return Err(Err::Failure(Error::OutOfBounds(0)));
        }

        let image_bytes = &original_input[offset as usize..(offset + size) as usize];
        let n = blp_header.mipmap_pixels(i);
        let blocks_n = ((n as f32) / 16.0).ceil() as usize;
        let mut blocks_size = blocks_n * dxtn.block_size();
        trace!("Dxtn blocks count: {blocks_n}");
        trace!("Dxtn format: {dxtn:?}, block size: {}", dxtn.block_size());
        trace!(
            "Left size: {}, expected size: {}",
            image_bytes.len(),
            blocks_size
        );
        if blocks_size > image_bytes.len() {
            warn!("Data is smaller than expected! Trying to read only whole number of blocks");
            let new_blocks_n = image_bytes.len() / dxtn.block_size();
            warn!("Reading {new_blocks_n} blocks");
            blocks_size = new_blocks_n * dxtn.block_size();
        }
        let (_, content) = context("dxtn blocks", count(le_u8, blocks_size))(image_bytes)?;
        images.push(DxtnImage { content });
        Ok(())
    };

    read_image(0)?;
    if blp_header.has_mipmaps() {
        trace!("Mipmaps count: {}", blp_header.mipmaps_count());
        for i in 1..(blp_header.mipmaps_count() + 1).min(16) {
            read_image(i)?;
        }
    }
    Ok((input, ()))
}