use crate::helper::{AlignExt, Endianness, ReadExt}; use crate::serialized_file::TypeTreeNode; use anyhow::{Result, bail}; use log::trace; use serde::Serialize; use std::io::Seek; use std::{collections::BTreeMap, io::Read}; #[derive(Debug, Clone, Serialize)] pub enum Value { Bool(bool), U8(u8), I8(i8), U16(u16), I16(i16), U32(u32), I32(i32), F32(f32), U64(u64), I64(i64), F64(f64), Array(Vec), Object { class: String, fields: BTreeMap, }, Typeless(Vec), String(String), } pub fn read_value( ty: &TypeTreeNode, e: Endianness, data: &mut (impl Read + Seek), ) -> Result { let mut align = false; let pos_before = data.stream_position()?; let r = match ty.type_string.as_str() { "char" => { assert_eq!(ty.byte_size, 1); Ok(Value::U8(data.read_u8()?)) } "Type*" => Ok(Value::U32(data.read_u32(e)?)), "int" => Ok(Value::I32(data.read_i32(e)?)), "unsigned int" => Ok(Value::U32(data.read_u32(e)?)), "UInt8" => Ok(Value::U8(data.read_u8()?)), "UInt16" => Ok(Value::U16(data.read_u16(e)?)), "UInt32" => Ok(Value::U32(data.read_u32(e)?)), "UInt64" => Ok(Value::U64(data.read_u64(e)?)), "SInt8" => Ok(Value::I8(data.read_i8()?)), "SInt16" => Ok(Value::I16(data.read_i16(e)?)), "SInt32" => Ok(Value::I32(data.read_i32(e)?)), "SInt64" => Ok(Value::I64(data.read_i64(e)?)), "bool" => Ok(Value::Bool(data.read_u8()? != 0)), "float" => { data.align(4)?; Ok(Value::F32(data.read_f32(e)?)) } "double" => { data.align(4)?; Ok(Value::F64(data.read_f64(e)?)) } "string" => { let Value::Array(arr) = read_value(&ty.children[0], e, data)? else { unreachable!() }; let bytes = arr .into_iter() .map(|e| match e { Value::U8(x) => x, _ => unreachable!(), }) .collect::>(); Ok(Value::String(String::from_utf8(bytes)?)) } "Array" => { align |= ty.children[0].post_align(); assert_eq!(ty.byte_size, -1); let Value::I32(size) = read_value(&ty.children[0], e, data)? else { unreachable!() }; trace!("array of size {size}"); let mut elems = Vec::new(); for _ in 0..size { elems.push(read_value(&ty.children[1], e, data)?); } Ok(Value::Array(elems)) } "TypelessData" => { let len = data.read_u32(e)?; let mut buf = vec![0u8; len as usize]; data.read_exact(&mut buf)?; Ok(Value::Typeless(buf)) } _ => { if ty.children.is_empty() && ty.byte_size != 0 { todo!("need type {:?}", ty.type_string); } let mut fields = BTreeMap::new(); for c in &ty.children { fields.insert(c.name_string.clone(), read_value(&c, e, data)?); } Ok(Value::Object { fields, class: ty.type_string.clone(), }) } }; let pos_after = data.stream_position()?; if ty.byte_size != -1 && pos_after - pos_before < ty.byte_size as u64 { bail!( "did not read enough data ({} expected, {} actual)", ty.byte_size, pos_after - pos_before ); } if align || ty.post_align() { trace!("post align"); data.align(4)?; } r } impl Value { pub fn class_name(&self) -> Option<&String> { if let Value::Object { class, .. } = self { Some(class) } else { None } } pub fn as_class(self, name: &str) -> Option> { if let Value::Object { class, fields } = self { if class == name { Some(fields) } else { None } } else { None } } pub fn as_string(self) -> Option { if let Value::String(s) = self { Some(s) } else { None } } pub fn as_i64(&self) -> Option { if let Value::I64(s) = self { Some(*s) } else { None } } pub fn as_i32(&self) -> Option { if let Value::I32(s) = self { Some(*s) } else { None } } pub fn as_u32(&self) -> Option { if let Value::U32(s) = self { Some(*s) } else { None } } pub fn as_u16(&self) -> Option { if let Value::U16(s) = self { Some(*s) } else { None } } pub fn as_bool(&self) -> Option { if let Value::Bool(s) = self { Some(*s) } else { None } } pub fn as_array(self) -> Option> { if let Value::Array(s) = self { Some(s) } else { None } } pub fn to_json(self) -> serde_json::Value { match self { Value::Bool(x) => serde_json::Value::Bool(x), Value::U8(x) => serde_json::Value::Number(x.into()), Value::I8(x) => serde_json::Value::Number(x.into()), Value::U16(x) => serde_json::Value::Number(x.into()), Value::I16(x) => serde_json::Value::Number(x.into()), Value::U32(x) => serde_json::Value::Number(x.into()), Value::U64(x) => serde_json::Value::Number(x.into()), Value::I32(x) => serde_json::Value::Number(x.into()), Value::F32(x) => serde_json::Value::Number( serde_json::Number::from_f64(x as f64).unwrap_or(0.into()), ), Value::I64(x) => serde_json::Value::Number(x.into()), Value::F64(x) => serde_json::Value::Number(serde_json::Number::from_f64(x).unwrap()), Value::String(x) => serde_json::Value::String(x), Value::Typeless(values) => serde_json::Value::Array( values .into_iter() .map(|e| serde_json::Value::Number(e.into())) .collect(), ), Value::Array(values) => { serde_json::Value::Array(values.into_iter().map(Value::to_json).collect()) } Value::Object { class, fields } => serde_json::Value::Object( fields .into_iter() .map(|(k, v)| (k, v.to_json())) .chain(Some(( "@class".to_string(), serde_json::Value::String(class), ))) .collect(), ), } } }