use nom::bytes::complete::take;
use strum::FromRepr;
use crate::binary::{
errors::ParseResult,
image::Image,
scalars::{byte, dword, short, word, Byte, Dword, Short, Word},
};
#[derive(Debug)]
pub struct CelChunk<'a> {
pub layer_index: Word,
pub x: Short,
pub y: Short,
pub opacity: Byte,
pub z_index: Short,
pub content: CelContent<'a>,
}
#[derive(Debug, FromRepr)]
enum CelType {
RawImageData,
LinkedCel,
CompressedImage,
CompressedTilemap,
Unknown(Word),
}
impl From<Word> for CelType {
fn from(word: Word) -> Self {
CelType::from_repr(word.into()).unwrap_or(Self::Unknown(word))
}
}
#[derive(Debug)]
pub enum CelContent<'a> {
Image(Image<'a>),
LinkedCel {
frame_position: Word,
},
CompressedTilemap {
width: Word,
height: Word,
bits_per_tile: Word,
bitmask_tile_id: Dword,
bitmask_x_flip: Dword,
bitmask_y_flip: Dword,
bitmask_diagonal_flip: Dword,
data: &'a [u8],
},
Unknown(&'a [u8]),
}
pub fn parse_cel_chunk(input: &[u8]) -> ParseResult<'_, CelChunk<'_>> {
let (input, layer_index) = word(input)?;
let (input, x) = short(input)?;
let (input, y) = short(input)?;
let (input, opacity) = byte(input)?;
let (input, cel_type) = word(input)?;
let cel_type = CelType::from(cel_type);
let (input, z_index) = short(input)?;
let (input, _) = take(5usize)(input)?;
let content = match cel_type {
CelType::RawImageData => {
let (input, width) = word(input)?;
let (input, height) = word(input)?;
CelContent::Image(Image {
width,
height,
data: input,
compressed: false,
})
}
CelType::LinkedCel => {
let (_, frame_position) = word(input)?;
CelContent::LinkedCel { frame_position }
}
CelType::CompressedImage => {
let (input, width) = word(input)?;
let (input, height) = word(input)?;
CelContent::Image(Image {
width,
height,
data: input,
compressed: true,
})
}
CelType::CompressedTilemap => {
let (input, width) = word(input)?;
let (input, height) = word(input)?;
let (input, bits_per_tile) = word(input)?;
let (input, bitmask_tile_id) = dword(input)?;
let (input, bitmask_y_flip) = dword(input)?;
let (input, bitmask_x_flip) = dword(input)?;
let (input, bitmask_diagonal_flip) = dword(input)?;
let (input, _) = take(10usize)(input)?;
CelContent::CompressedTilemap {
width,
height,
bits_per_tile,
bitmask_tile_id,
bitmask_x_flip,
bitmask_y_flip,
bitmask_diagonal_flip,
data: input,
}
}
CelType::Unknown(_) => CelContent::Unknown(input),
};
Ok((
&input[input.len()..],
CelChunk {
layer_index,
x,
y,
opacity,
z_index,
content,
},
))
}