use serde::{Deserialize, Serialize};
use super::error::{WzError, WzResult};
const MCV_MIN_HEADER_SIZE: usize = 36;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McvHeader {
pub header_length: u16,
pub fourcc: u32,
pub width: u16,
pub height: u16,
pub frame_count: i32,
pub data_flags: u8,
pub frame_delay_unit_ns: i64,
pub default_delay: i32,
}
pub fn parse_mcv_header(data: &[u8]) -> WzResult<McvHeader> {
if data.len() < MCV_MIN_HEADER_SIZE {
return Err(WzError::Custom(format!(
"MCV data too short for header: {} < {}",
data.len(),
MCV_MIN_HEADER_SIZE
)));
}
if &data[0..4] != b"MCV0" {
return Err(WzError::Custom(format!(
"Invalid MCV signature: {:02X} {:02X} {:02X} {:02X}",
data[0], data[1], data[2], data[3]
)));
}
let header_length = u16::from_le_bytes([data[6], data[7]]);
let fourcc = u32::from_le_bytes([data[8], data[9], data[10], data[11]]) ^ 0xA5A5A5A5;
let width = u16::from_le_bytes([data[12], data[13]]);
let height = u16::from_le_bytes([data[14], data[15]]);
let frame_count = i32::from_le_bytes([data[16], data[17], data[18], data[19]]);
let data_flags = data[20];
let frame_delay_unit_ns = i64::from_le_bytes(data[24..32].try_into().unwrap());
let default_delay = i32::from_le_bytes([data[32], data[33], data[34], data[35]]);
Ok(McvHeader {
header_length,
fourcc,
width,
height,
frame_count,
data_flags,
frame_delay_unit_ns,
default_delay,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn build_mcv_header(
fourcc: u32,
width: u16,
height: u16,
frame_count: i32,
flags: u8,
) -> Vec<u8> {
let mut data = Vec::with_capacity(MCV_MIN_HEADER_SIZE);
data.extend_from_slice(b"MCV0"); data.extend_from_slice(&[0x00, 0x00]); data.extend_from_slice(&36u16.to_le_bytes()); data.extend_from_slice(&(fourcc ^ 0xA5A5A5A5).to_le_bytes()); data.extend_from_slice(&width.to_le_bytes()); data.extend_from_slice(&height.to_le_bytes()); data.extend_from_slice(&frame_count.to_le_bytes()); data.push(flags); data.extend_from_slice(&[0x00, 0x00, 0x00]); data.extend_from_slice(&1_000_000i64.to_le_bytes()); data.extend_from_slice(&100i32.to_le_bytes()); data
}
#[test]
fn test_parse_valid_header() {
let data = build_mcv_header(0x48323634, 1920, 1080, 240, 0x03);
let header = parse_mcv_header(&data).unwrap();
assert_eq!(header.fourcc, 0x48323634);
assert_eq!(header.width, 1920);
assert_eq!(header.height, 1080);
assert_eq!(header.frame_count, 240);
assert_eq!(header.data_flags, 0x03);
assert_eq!(header.frame_delay_unit_ns, 1_000_000);
assert_eq!(header.default_delay, 100);
assert_eq!(header.header_length, 36);
}
#[test]
fn test_parse_too_short() {
let data = vec![0u8; 10];
assert!(parse_mcv_header(&data).is_err());
}
#[test]
fn test_parse_invalid_signature() {
let mut data = build_mcv_header(0, 0, 0, 0, 0);
data[0] = b'X';
assert!(parse_mcv_header(&data).is_err());
}
#[test]
fn test_fourcc_xor_decode() {
let original_fourcc: u32 = 0x34363248; let data = build_mcv_header(original_fourcc, 100, 100, 1, 0);
let header = parse_mcv_header(&data).unwrap();
assert_eq!(header.fourcc, original_fourcc);
}
#[test]
fn test_extra_trailing_data_ok() {
let mut data = build_mcv_header(0, 640, 480, 30, 0);
data.extend_from_slice(&[0xFF; 100]); let header = parse_mcv_header(&data).unwrap();
assert_eq!(header.width, 640);
assert_eq!(header.height, 480);
}
}