From 2f4a11ddda04604d5d756231d258ef60fa9f7bd8 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sat, 15 Feb 2025 13:21:25 +0100 Subject: can read objects --- src/bin/parse.rs | 48 ++++++++++++++++++++++++++++ src/bin/probe.rs | 25 +++++++++++++++ src/helper.rs | 38 ++++++++++++++++++++++ src/main.rs | 42 ------------------------ src/object.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/serialized_file.rs | 45 +++++++++++++++++++++++--- src/unityfs.rs | 23 +++++++++---- 7 files changed, 252 insertions(+), 56 deletions(-) create mode 100644 src/bin/parse.rs create mode 100644 src/bin/probe.rs delete mode 100644 src/main.rs (limited to 'src') diff --git a/src/bin/parse.rs b/src/bin/parse.rs new file mode 100644 index 0000000..d436aad --- /dev/null +++ b/src/bin/parse.rs @@ -0,0 +1,48 @@ +use std::{ + env::args, + fs::File, + io::{BufReader, Read, Seek, SeekFrom}, +}; +use unity_tools::{object::read_value, serialized_file::read_serialized_file, unityfs::UnityFS}; + +fn main() -> anyhow::Result<()> { + env_logger::init_from_env("LOG"); + let file = BufReader::new(File::open(args().nth(1).unwrap())?); + let mut fs = UnityFS::open(file)?; + + for node in fs.nodes().to_vec() { + if node.name.ends_with(".resource") || node.name.ends_with(".resS") { + continue; + } + let mut cab = fs.read(&node)?; + // let mut writer = File::create(format!("/tmp/{}", node.name))?; + // std::io::copy(&mut cab, &mut writer)?; + // continue; + + let file = read_serialized_file(&mut cab)?; + let e = file.endianness; + + for ob in file.objects { + cab.seek(SeekFrom::Start(ob.data_offset))?; + let mut ob_data = cab.by_ref(); //.take(ob.data_size as u64); + + eprintln!("{:#?}", ob); + let typetree = if ob.type_id < 0 { + unimplemented!() + } else { + file.types + .iter() + .find(|t| t.class_id == ob.type_id) + .expect("unknown type") + }; + eprintln!("{typetree:#?}"); + + let value = read_value(typetree.type_tree.as_ref().unwrap(), e, &mut ob_data)?; + + eprintln!("{value:#?}") + } + // eprintln!("{:#?}", file.types); + } + + Ok(()) +} diff --git a/src/bin/probe.rs b/src/bin/probe.rs new file mode 100644 index 0000000..85b2a7a --- /dev/null +++ b/src/bin/probe.rs @@ -0,0 +1,25 @@ +use anyhow::Result; +use std::{env::args, fs::File, io::BufReader}; +use unity_tools::{serialized_file::read_serialized_file_header, unityfs::UnityFS}; + +fn main() -> Result<()> { + let file = BufReader::new(File::open(args().nth(1).unwrap())?); + let mut fs = UnityFS::open(file)?; + + for node in fs.nodes().to_vec() { + if node.name.ends_with(".resource") || node.name.ends_with(".resS") { + continue; + } + let mut cab = fs.read(&node)?; + let ch = read_serialized_file_header(&mut cab)?; + + if fs.unity_version.is_ascii() && ch.generator_version.is_ascii() && ch.format < 100 { + println!( + "{}\t{}\t{}\t{}", + fs.file_version, fs.unity_version, ch.format, ch.generator_version + ); + } + } + + Ok(()) +} diff --git a/src/helper.rs b/src/helper.rs index cb52b8c..a65d3e8 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -28,6 +28,12 @@ pub trait ReadExt { fn read_i64_le(&mut self) -> Result; fn read_u128_be(&mut self) -> Result; fn read_cstr(&mut self) -> Result; + fn read_f32(&mut self, e: Endianness) -> Result; + fn read_f32_be(&mut self) -> Result; + fn read_f32_le(&mut self) -> Result; + fn read_f64(&mut self, e: Endianness) -> Result; + fn read_f64_be(&mut self) -> Result; + fn read_f64_le(&mut self) -> Result; } impl ReadExt for T { @@ -132,6 +138,38 @@ impl ReadExt for T { self.read_exact(&mut buf)?; Ok(i64::from_le_bytes(buf)) } + fn read_f32(&mut self, e: Endianness) -> Result { + match e { + Endianness::Big => self.read_f32_be(), + Endianness::Little => self.read_f32_le(), + } + } + fn read_f32_be(&mut self) -> Result { + let mut buf = [0; 4]; + self.read_exact(&mut buf)?; + Ok(f32::from_be_bytes(buf)) + } + fn read_f32_le(&mut self) -> Result { + let mut buf = [0; 4]; + self.read_exact(&mut buf)?; + Ok(f32::from_le_bytes(buf)) + } + fn read_f64(&mut self, e: Endianness) -> Result { + match e { + Endianness::Big => self.read_f64_be(), + Endianness::Little => self.read_f64_le(), + } + } + fn read_f64_be(&mut self) -> Result { + let mut buf = [0; 8]; + self.read_exact(&mut buf)?; + Ok(f64::from_be_bytes(buf)) + } + fn read_f64_le(&mut self) -> Result { + let mut buf = [0; 8]; + self.read_exact(&mut buf)?; + Ok(f64::from_le_bytes(buf)) + } fn read_u128_be(&mut self) -> Result { let mut buf = [0; 16]; self.read_exact(&mut buf)?; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 450f4a7..0000000 --- a/src/main.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::{ - env::args, - fs::File, - io::{BufReader, Read, Seek, SeekFrom}, -}; -use unity_tools::{serialized_file::read_serialized_file, unityfs::UnityFS}; - -fn main() -> anyhow::Result<()> { - env_logger::init_from_env("LOG"); - let file = BufReader::new(File::open(args().nth(1).unwrap())?); - let mut fs = UnityFS::open(file)?; - - for node in fs.nodes().to_vec() { - if node.name.ends_with(".resource") || node.name.ends_with(".resS") { - continue; - } - let mut cab = fs.read(&node)?; - // let mut writer = File::create(format!("/tmp/{}", node.name))?; - // std::io::copy(&mut cab, &mut writer)?; - // continue; - - let file = read_serialized_file(&mut cab)?; - - for ob in file.objects { - cab.seek(SeekFrom::Start(ob.data_offset))?; - let ob_data = cab.by_ref().take(ob.data_size as u64); - - eprintln!("{:#?}", ob); - let typetree = if ob.type_id < 0 { - unimplemented!() - } else { - file.types - .iter() - .find(|t| t.class_id == ob.type_id) - .expect("unknown type") - }; - } - // eprintln!("{:#?}", file.types); - } - - Ok(()) -} diff --git a/src/object.rs b/src/object.rs index 9875517..5ad1441 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,4 +1,87 @@ +use crate::helper::{AlignExt, Endianness, ReadExt}; use crate::serialized_file::TypeTreeNode; -use std::io::Read; +use anyhow::Result; +use log::debug; +use std::io::Seek; +use std::{collections::BTreeMap, io::Read}; -pub fn read_value(ty: TypeTreeNode, data: &mut impl Read) {} +#[derive(Debug)] +pub enum Value { + Bool(bool), + U8(u8), + U16(u16), + U32(u32), + I32(i32), + F32(f32), + I64(i64), + F64(f64), + Array(Vec), + Object { + class: String, + fields: BTreeMap, + }, + String(String), +} + +pub fn read_value( + ty: &TypeTreeNode, + e: Endianness, + data: &mut (impl Read + Seek), +) -> Result { + match ty.type_string.as_str() { + "char" => Ok(Value::U8(data.read_u8()?)), + "int" => Ok(Value::I32(data.read_i32(e)?)), + "unsigned int" => Ok(Value::U32(data.read_u32(e)?)), + "UInt16" => Ok(Value::U16(data.read_u16(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| { + if let Value::U8(x) = e { + x + } else { + unreachable!() + } + }) + .collect::>(); + Ok(Value::String(String::from_utf8(bytes)?)) + } + "Array" => { + let Value::I32(size) = read_value(&ty.children[0], e, data)? else { + unreachable!() + }; + debug!("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)) + } + _ => { + if ty.children.is_empty() && ty.byte_size != -1 { + 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(), + }) + } + } +} diff --git a/src/serialized_file.rs b/src/serialized_file.rs index 4b348e0..cdf6125 100644 --- a/src/serialized_file.rs +++ b/src/serialized_file.rs @@ -56,14 +56,27 @@ pub struct External { #[derive(Debug)] pub struct SerializedFile { + pub header: SerializedFileHeader, pub types: Vec, pub externals: Vec, pub scripts: Vec