use crate::datatypes::{read_u16, read_u32, read_i32};
use crate::record::Record;
use std::io::{Read, Cursor};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GroupType {
Normal,
WorldChildren,
InteriorCellBlock,
InteriorCellSubBlock,
ExteriorCellBlock,
ExteriorCellSubBlock,
CellChildren,
TopicChildren,
CellPersistentChildren,
CellTemporaryChildren,
Unknown(i32),
}
impl GroupType {
pub fn to_i32(&self) -> i32 {
match self {
GroupType::Normal => 0,
GroupType::WorldChildren => 1,
GroupType::InteriorCellBlock => 2,
GroupType::InteriorCellSubBlock => 3,
GroupType::ExteriorCellBlock => 4,
GroupType::ExteriorCellSubBlock => 5,
GroupType::CellChildren => 6,
GroupType::TopicChildren => 7,
GroupType::CellPersistentChildren => 8,
GroupType::CellTemporaryChildren => 9,
GroupType::Unknown(value) => *value,
}
}
}
impl From<i32> for GroupType {
fn from(value: i32) -> Self {
match value {
0 => GroupType::Normal,
1 => GroupType::WorldChildren,
2 => GroupType::InteriorCellBlock,
3 => GroupType::InteriorCellSubBlock,
4 => GroupType::ExteriorCellBlock,
5 => GroupType::ExteriorCellSubBlock,
6 => GroupType::CellChildren,
7 => GroupType::TopicChildren,
8 => GroupType::CellPersistentChildren,
9 => GroupType::CellTemporaryChildren,
_ => GroupType::Unknown(value),
}
}
}
#[derive(Debug)]
pub struct Group {
pub size: u32,
pub label: [u8; 4],
pub group_type: GroupType,
pub timestamp: u16,
pub version_control_info: u16,
pub unknown: u32,
pub children: Vec<GroupChild>,
}
#[derive(Debug)]
pub enum GroupChild {
Group(Box<Group>),
Record(Record),
}
impl Group {
pub fn parse(cursor: &mut Cursor<&[u8]>) -> Result<Self, Box<dyn std::error::Error>> {
if cursor.position() + 24 > cursor.get_ref().len() as u64 {
return Err("Insufficient data for group header".into());
}
let mut type_bytes = [0u8; 4];
cursor.read_exact(&mut type_bytes)?;
if &type_bytes != b"GRUP" {
return Err(format!("Expected GRUP, found {}", String::from_utf8_lossy(&type_bytes)).into());
}
let size = read_u32(cursor)?;
if size > 200_000_000 { return Err(format!("组大小异常: {} bytes (可能数据损坏)", size).into());
}
if size < 24 {
return Err(format!("组大小太小: {} bytes (最小应为24字节)", size).into());
}
let mut label = [0u8; 4];
cursor.read_exact(&mut label)?;
let group_type = GroupType::from(read_i32(cursor)?);
let timestamp = read_u16(cursor)?;
let version_control_info = read_u16(cursor)?;
let unknown = read_u32(cursor)?;
let data_size = size - 24;
if cursor.position() + data_size as u64 > cursor.get_ref().len() as u64 {
return Err(format!("Insufficient data for group data: expected {} bytes", data_size).into());
}
let data_start = cursor.position();
let data_end = data_start + data_size as u64;
let mut children = Vec::new();
while cursor.position() < data_end {
let peek_pos = cursor.position();
let mut peek_bytes = [0u8; 4];
cursor.read_exact(&mut peek_bytes)?;
cursor.set_position(peek_pos);
if &peek_bytes == b"GRUP" {
let child_group = Group::parse(cursor)?;
children.push(GroupChild::Group(Box::new(child_group)));
} else {
let record = Record::parse(cursor)?;
children.push(GroupChild::Record(record));
}
}
Ok(Group {
size,
label,
group_type,
timestamp,
version_control_info,
unknown,
children,
})
}
pub fn get_label(&self) -> &[u8; 4] {
&self.label
}
pub fn get_type(&self) -> &GroupType {
&self.group_type
}
pub fn get_records(&self) -> Vec<&Record> {
let mut records = Vec::new();
self.collect_records(&mut records);
records
}
fn collect_records<'a>(&'a self, records: &mut Vec<&'a Record>) {
for child in &self.children {
match child {
GroupChild::Group(group) => {
group.collect_records(records);
}
GroupChild::Record(record) => {
records.push(record);
}
}
}
}
pub fn get_label_string(&self) -> String {
String::from_utf8_lossy(&self.label).into_owned()
}
}