use crate::datatypes::{read_u16, read_u32};
use std::io::{Read, Cursor};
#[derive(Debug, Clone)]
pub struct Subrecord {
pub record_type_bytes: [u8; 4],
pub record_type: String,
pub size: u16,
pub data: Vec<u8>,
}
impl Subrecord {
pub fn parse(cursor: &mut Cursor<&[u8]>) -> Result<Self, Box<dyn std::error::Error>> {
if cursor.position() + 6 > cursor.get_ref().len() as u64 {
return Err("Insufficient data for subrecord header".into());
}
let current_pos = cursor.position();
let mut type_bytes = [0u8; 4];
cursor.read_exact(&mut type_bytes)?;
let record_type = String::from_utf8_lossy(&type_bytes).into_owned();
let size = read_u16(cursor)?;
if record_type == "XXXX" {
if size != 4 {
return Err(format!("XXXX subrecord size should be 4, got {}", size).into());
}
let field_size = read_u32(cursor)?;
#[cfg(debug_assertions)]
{
eprintln!("📦 检测到 XXXX 超大子记录");
eprintln!(" 真实数据大小: {} bytes (0x{:X})", field_size, field_size);
}
let mut next_type_bytes = [0u8; 4];
cursor.read_exact(&mut next_type_bytes)?;
let next_type = String::from_utf8_lossy(&next_type_bytes).into_owned();
let next_size = read_u16(cursor)?;
if next_size != 0 {
eprintln!(
" ⚠️ 警告:XXXX 后续子记录 {} 的 size 不为 0,实际值: {}",
next_type, next_size
);
}
#[cfg(debug_assertions)]
eprintln!(" 后续子记录类型: {}", next_type);
let mut data = vec![0u8; field_size as usize];
cursor.read_exact(&mut data)?;
#[cfg(debug_assertions)]
eprintln!(" ✓ XXXX 子记录解析成功");
Ok(Subrecord {
record_type_bytes: next_type_bytes,
record_type: next_type,
size: 0, data,
})
} else {
if cursor.position() + size as u64 > cursor.get_ref().len() as u64 {
let error_msg = format!(
"Insufficient data for subrecord data: type='{}' (0x{:02X?}), expected {} bytes, but only {} bytes remaining (pos=0x{:X}, total={})",
record_type,
type_bytes,
size,
cursor.get_ref().len() as u64 - cursor.position(),
current_pos,
cursor.get_ref().len()
);
return Err(error_msg.into());
}
let mut data = vec![0u8; size as usize];
cursor.read_exact(&mut data)?;
Ok(Subrecord {
record_type_bytes: type_bytes,
record_type,
size,
data,
})
}
}
pub fn get_type(&self) -> &str {
&self.record_type
}
pub fn get_data(&self) -> &[u8] {
&self.data
}
pub fn is_string_type(&self, string_types: &[String]) -> bool {
string_types.iter().any(|t| t == &self.record_type)
}
}