aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bin/gltf.rs57
-rw-r--r--src/bin/json.rs36
-rw-r--r--src/bin/meshes.rs56
-rw-r--r--src/bin/textures.rs56
-rw-r--r--src/bin/yaml.rs46
-rw-r--r--src/object/read.rs170
-rw-r--r--src/serialized_file.rs42
7 files changed, 202 insertions, 261 deletions
diff --git a/src/bin/gltf.rs b/src/bin/gltf.rs
index 07adec6..323b6d2 100644
--- a/src/bin/gltf.rs
+++ b/src/bin/gltf.rs
@@ -1,16 +1,7 @@
#![feature(array_chunks)]
use anyhow::anyhow;
-use std::{
- env::args,
- fs::{File, create_dir_all},
- io::{BufReader, BufWriter, Seek, SeekFrom, Write},
-};
-use unity_tools::{
- classes::mesh::{Mesh, VertexDataChannel},
- object::{parser::FromValue, read::read_value},
- serialized_file::SerializedFile,
- unityfs::UnityFS,
-};
+use std::{env::args, fs::File, io::BufReader};
+use unity_tools::{serialized_file::SerializedFile, unityfs::UnityFS};
fn main() -> anyhow::Result<()> {
env_logger::init_from_env("LOG");
@@ -18,9 +9,6 @@ fn main() -> anyhow::Result<()> {
let mut fs = UnityFS::open(file())?;
// let mut fs2 = UnityFS::open(file())?;
- let mut i = 0;
- create_dir_all("/tmp/a").unwrap();
-
let cabfile = fs
.find_main_file()
.ok_or(anyhow!("no CAB file found"))?
@@ -28,46 +16,7 @@ fn main() -> anyhow::Result<()> {
let mut cab = fs.read(&cabfile)?;
let file = SerializedFile::read(&mut cab)?;
- for ob in file.objects {
- cab.seek(SeekFrom::Start(ob.data_offset))?;
- let typetree = if ob.type_id < 0 {
- unimplemented!()
- } else {
- &file.types[ob.type_id as usize]
- };
- if let Some(typetree) = &typetree.type_tree {
- if typetree.type_string != "Mesh" {
- continue;
- }
- let value = read_value(typetree, file.endianness, &mut cab)?;
- let mesh = Mesh::from_value(value).unwrap();
- // if mesh.stream_data.len() == 0 {
- // let ress = ress.as_mut().unwrap();
- // ress.seek(SeekFrom::Start(mesh.stream_data.offset))?;
- // ress.by_ref()
- // .take(mesh.stream_data.size as u64)
- // .read_to_end(&mut mesh.)?;
- // }
- let mut obj = BufWriter::new(File::create(format!(
- "/tmp/a/{}_{i}.obj",
- mesh.name.replace("/", "-").replace(".", "-")
- ))?);
-
- let (pos_dims, positions) = mesh
- .vertex_data
- .read_channel(VertexDataChannel::Position)
- .unwrap();
- assert_eq!(pos_dims, 3);
-
- for [x, y, z] in positions.array_chunks() {
- writeln!(obj, "v {x} {y} {z}")?;
- }
- for [a, b, c] in mesh.read_indecies() {
- writeln!(obj, "f {} {} {}", a + 1, b + 1, c + 1)?;
- }
- i += 1;
- }
- }
+ for _ob in file.objects {}
Ok(())
}
diff --git a/src/bin/json.rs b/src/bin/json.rs
index 2d09306..dd83de6 100644
--- a/src/bin/json.rs
+++ b/src/bin/json.rs
@@ -1,9 +1,9 @@
use std::{
env::{args, var},
fs::File,
- io::{BufReader, Seek, SeekFrom, stdout},
+ io::{BufReader, stdout},
};
-use unity_tools::{object::read::read_value, serialized_file::SerializedFile, unityfs::UnityFS};
+use unity_tools::{serialized_file::SerializedFile, unityfs::UnityFS};
fn main() -> anyhow::Result<()> {
env_logger::init_from_env("LOG");
@@ -14,28 +14,20 @@ fn main() -> anyhow::Result<()> {
let node = fs.find_main_file().unwrap().to_owned();
let mut cab = fs.read(&node)?;
- let file = SerializedFile::read(&mut cab)?;
- for ob in file.objects {
- cab.seek(SeekFrom::Start(ob.data_offset))?;
- let typetree = if ob.type_id < 0 {
- unimplemented!()
- } else {
- &file.types[ob.type_id as usize]
- };
- if let Some(typetree) = &typetree.type_tree {
- if let Some(f) = &filter {
- if typetree.type_string != *f && ob.path_id.to_string() != *f {
- continue;
- }
- }
- let value = read_value(typetree, file.endianness, &mut cab)?;
- if pretty {
- serde_json::to_writer_pretty(stdout(), &value.to_json()).unwrap();
- } else {
- serde_json::to_writer(stdout(), &value.to_json()).unwrap();
+ let mut file = SerializedFile::read(&mut cab)?;
+ for ob in file.objects.clone() {
+ if let Some(f) = &filter {
+ if file.get_object_type_tree(&ob)?.type_string != *f && ob.path_id.to_string() != *f {
+ continue;
}
- println!()
}
+ let value = file.read_object(ob)?;
+ if pretty {
+ serde_json::to_writer_pretty(stdout(), &value.to_json()).unwrap();
+ } else {
+ serde_json::to_writer(stdout(), &value.to_json()).unwrap();
+ }
+ println!()
}
Ok(())
diff --git a/src/bin/meshes.rs b/src/bin/meshes.rs
index f2b4edf..d59b496 100644
--- a/src/bin/meshes.rs
+++ b/src/bin/meshes.rs
@@ -3,11 +3,11 @@ use anyhow::anyhow;
use std::{
env::args,
fs::{File, create_dir_all},
- io::{BufReader, BufWriter, Seek, SeekFrom, Write},
+ io::{BufReader, BufWriter, Write},
};
use unity_tools::{
classes::mesh::{Mesh, VertexDataChannel},
- object::{parser::FromValue, read::read_value},
+ object::parser::FromValue,
serialized_file::SerializedFile,
unityfs::UnityFS,
};
@@ -29,39 +29,31 @@ fn main() -> anyhow::Result<()> {
.to_owned();
let mut cab = fs.read(&cabfile)?;
- let file = SerializedFile::read(&mut cab)?;
- for ob in file.objects {
- cab.seek(SeekFrom::Start(ob.data_offset))?;
- let typetree = if ob.type_id < 0 {
- unimplemented!()
- } else {
- &file.types[ob.type_id as usize]
- };
- if let Some(typetree) = &typetree.type_tree {
- if typetree.type_string != "Mesh" {
- continue;
- }
- let value = read_value(typetree, file.endianness, &mut cab)?;
- let mesh = Mesh::from_value(value).unwrap();
- let mut obj = BufWriter::new(File::create(format!(
- "/tmp/a/{}_{i}.obj",
- mesh.name.replace("/", "-").replace(".", "-")
- ))?);
+ let mut file = SerializedFile::read(&mut cab)?;
+ for ob in file.objects.clone() {
+ if file.get_object_type_tree(&ob)?.type_string != "Mesh" {
+ continue;
+ }
+ let value = file.read_object(ob)?;
+ let mesh = Mesh::from_value(value).unwrap();
+ let mut obj = BufWriter::new(File::create(format!(
+ "/tmp/a/{}_{i}.obj",
+ mesh.name.replace("/", "-").replace(".", "-")
+ ))?);
- let (pos_dims, positions) = mesh
- .vertex_data
- .read_channel(VertexDataChannel::Position)
- .unwrap();
- assert_eq!(pos_dims, 3);
+ let (pos_dims, positions) = mesh
+ .vertex_data
+ .read_channel(VertexDataChannel::Position)
+ .unwrap();
+ assert_eq!(pos_dims, 3);
- for [x, y, z] in positions.array_chunks() {
- writeln!(obj, "v {x} {y} {z}")?;
- }
- for [a, b, c] in mesh.read_indecies() {
- writeln!(obj, "f {} {} {}", a + 1, b + 1, c + 1)?;
- }
- i += 1;
+ for [x, y, z] in positions.array_chunks() {
+ writeln!(obj, "v {x} {y} {z}")?;
+ }
+ for [a, b, c] in mesh.read_indecies() {
+ writeln!(obj, "f {} {} {}", a + 1, b + 1, c + 1)?;
}
+ i += 1;
}
Ok(())
diff --git a/src/bin/textures.rs b/src/bin/textures.rs
index 6ceabac..2e077fe 100644
--- a/src/bin/textures.rs
+++ b/src/bin/textures.rs
@@ -3,12 +3,10 @@ use log::warn;
use std::{
env::args,
fs::{File, create_dir_all},
- io::{BufReader, Seek, SeekFrom},
+ io::BufReader,
};
use unity_tools::{
- classes::texture2d::Texture2D,
- object::{parser::FromValue, read::read_value},
- serialized_file::SerializedFile,
+ classes::texture2d::Texture2D, object::parser::FromValue, serialized_file::SerializedFile,
unityfs::UnityFS,
};
@@ -27,38 +25,30 @@ fn main() -> anyhow::Result<()> {
.to_owned();
let mut cab = fs.read(&cabfile)?;
- let file = SerializedFile::read(&mut cab)?;
- for ob in file.objects {
- cab.seek(SeekFrom::Start(ob.data_offset))?;
- let typetree = if ob.type_id < 0 {
- unimplemented!()
- } else {
- &file.types[ob.type_id as usize]
- };
- if let Some(typetree) = &typetree.type_tree {
- if typetree.type_string != "Texture2D" {
- continue;
- }
- let value = read_value(typetree, file.endianness, &mut cab)?;
- let mut texture = Texture2D::from_value(value)?;
- if texture.image_data.is_empty() {
- texture.image_data = texture.stream_data.read(&mut fs2)?;
- }
- let path = format!(
- "/tmp/a/{}_{i}.png",
- texture.name.replace("/", "-").replace(".", "-")
- );
- match texture.to_image() {
- Ok(im) => {
- if !im.as_rgba32f().is_some() {
- im.save(&path).unwrap();
- println!("{path}");
- }
+ let mut file = SerializedFile::read(&mut cab)?;
+ for ob in file.objects.clone() {
+ if file.get_object_type_tree(&ob)?.type_string != "Texture2D" {
+ continue;
+ }
+ let value = file.read_object(ob)?;
+ let mut texture = Texture2D::from_value(value)?;
+ if texture.image_data.is_empty() {
+ texture.image_data = texture.stream_data.read(&mut fs2)?;
+ }
+ let path = format!(
+ "/tmp/a/{}_{i}.png",
+ texture.name.replace("/", "-").replace(".", "-")
+ );
+ match texture.to_image() {
+ Ok(im) => {
+ if !im.as_rgba32f().is_some() {
+ im.save(&path).unwrap();
+ println!("{path}");
}
- Err(e) => warn!("{e}"),
}
- i += 1;
+ Err(e) => warn!("{e}"),
}
+ i += 1;
}
Ok(())
diff --git a/src/bin/yaml.rs b/src/bin/yaml.rs
index fd3eec6..4ef8933 100644
--- a/src/bin/yaml.rs
+++ b/src/bin/yaml.rs
@@ -2,11 +2,9 @@ use serde_yml::Value;
use std::{
env::args,
fs::File,
- io::{BufReader, Seek, SeekFrom, stdout},
-};
-use unity_tools::{
- classes::HValue, object::read::read_value, serialized_file::SerializedFile, unityfs::UnityFS,
+ io::{BufReader, stdout},
};
+use unity_tools::{classes::HValue, serialized_file::SerializedFile, unityfs::UnityFS};
fn main() -> anyhow::Result<()> {
env_logger::init_from_env("LOG");
@@ -17,44 +15,36 @@ fn main() -> anyhow::Result<()> {
let node = fs.find_main_file().unwrap().to_owned();
let mut cab = fs.read(&node)?;
- let file = SerializedFile::read(&mut cab)?;
- for ob in file.objects {
- cab.seek(SeekFrom::Start(ob.data_offset))?;
- let typetree = if ob.type_id < 0 {
- unimplemented!()
- } else {
- &file.types[ob.type_id as usize]
- };
- if let Some(typetree) = &typetree.type_tree {
- if let Some(f) = &filter {
- if typetree.type_string != *f && ob.path_id.to_string() != *f {
- continue;
- }
+ let mut file = SerializedFile::read(&mut cab)?;
+ for ob in file.objects.clone() {
+ if let Some(f) = &filter {
+ if file.get_object_type_tree(&ob)?.type_string != *f && ob.path_id.to_string() != *f {
+ continue;
}
- let value = read_value(typetree, file.endianness, &mut cab)?;
- let hvalue = HValue::from_value(value)?;
+ }
+ let value = file.read_object(ob)?;
+ let hvalue = HValue::from_value(value)?;
- let mut hvalue = serde_yml::to_value(hvalue)?;
- reduce_large_arrays(&mut hvalue);
+ let mut hvalue = serde_yml::to_value(hvalue)?;
+ trim_large_arrays(&mut hvalue);
- serde_yml::to_writer(stdout(), &hvalue).unwrap();
- println!()
- }
+ serde_yml::to_writer(stdout(), &hvalue).unwrap();
+ println!()
}
Ok(())
}
-fn reduce_large_arrays(v: &mut Value) {
+fn trim_large_arrays(v: &mut Value) {
match v {
Value::Sequence(values) => {
- values.iter_mut().for_each(reduce_large_arrays);
+ values.iter_mut().for_each(trim_large_arrays);
while values.len() > 32 {
values.pop();
}
}
- Value::Mapping(mapping) => mapping.map.values_mut().for_each(reduce_large_arrays),
- Value::Tagged(tagged_value) => reduce_large_arrays(&mut tagged_value.value),
+ Value::Mapping(mapping) => mapping.map.values_mut().for_each(trim_large_arrays),
+ Value::Tagged(tagged_value) => trim_large_arrays(&mut tagged_value.value),
_ => (),
}
}
diff --git a/src/object/read.rs b/src/object/read.rs
index 5adbe3f..641bdcc 100644
--- a/src/object/read.rs
+++ b/src/object/read.rs
@@ -6,95 +6,93 @@ use log::trace;
use std::io::Seek;
use std::{collections::BTreeMap, io::Read};
-pub fn read_value(
- ty: &TypeTreeNode,
- e: Endianness,
- data: &mut (impl Read + Seek),
-) -> Result<Value> {
- 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::<Vec<_>>();
- 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)?);
+impl Value {
+ pub fn read(ty: &TypeTreeNode, e: Endianness, data: &mut (impl Read + Seek)) -> Result<Value> {
+ 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()?))
}
- 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);
+ "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)?))
}
- let mut fields = BTreeMap::new();
- for c in &ty.children {
- fields.insert(c.name_string.clone(), read_value(&c, e, data)?);
+ "double" => {
+ data.align(4)?;
+ Ok(Value::F64(data.read_f64(e)?))
}
- Ok(Value::Object {
- fields,
- class: ty.type_string.clone(),
- })
+ "string" => {
+ let Value::Array(arr) = Value::read(&ty.children[0], e, data)? else {
+ unreachable!()
+ };
+ let bytes = arr
+ .into_iter()
+ .map(|e| match e {
+ Value::U8(x) => x,
+ _ => unreachable!(),
+ })
+ .collect::<Vec<_>>();
+ Ok(Value::String(String::from_utf8(bytes)?))
+ }
+ "Array" => {
+ align |= ty.children[0].post_align();
+ assert_eq!(ty.byte_size, -1);
+ let Value::I32(size) = Value::read(&ty.children[0], e, data)? else {
+ unreachable!()
+ };
+ trace!("array of size {size}");
+ let mut elems = Vec::new();
+ for _ in 0..size {
+ elems.push(Value::read(&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(), Value::read(&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
+ );
}
- };
- 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)?;
+ if align || ty.post_align() {
+ trace!("post align");
+ data.align(4)?;
+ }
+ r
}
- r
}
diff --git a/src/serialized_file.rs b/src/serialized_file.rs
index b56fa3a..b3c1e3c 100644
--- a/src/serialized_file.rs
+++ b/src/serialized_file.rs
@@ -1,10 +1,11 @@
use crate::{
common_strings::COMMON_STRINGS,
helper::{AlignExt, Endianness, ReadExt},
+ object::Value,
};
-use anyhow::{Result, bail};
+use anyhow::{Result, anyhow, bail};
use log::{debug, info, trace, warn};
-use std::io::{Cursor, Read, Seek};
+use std::io::{Cursor, Read, Seek, SeekFrom};
#[derive(Debug, Clone)]
pub struct TypeTreeNode {
@@ -32,7 +33,7 @@ pub struct SeralizedType {
pub type_deps: Vec<u32>,
}
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct ObjectInfo {
pub path_id: i64,
pub data_offset: u64,
@@ -55,7 +56,8 @@ pub struct External {
}
#[derive(Debug)]
-pub struct SerializedFile {
+pub struct SerializedFile<T> {
+ pub file: T,
pub header: SerializedFileHeader,
pub types: Vec<SeralizedType>,
pub externals: Vec<External>,
@@ -120,8 +122,9 @@ impl SerializedFileHeader {
})
}
}
-impl SerializedFile {
- pub fn read(mut file: impl Read + Seek) -> Result<SerializedFile> {
+
+impl<T: Read + Seek> SerializedFile<T> {
+ pub fn read(mut file: T) -> Result<SerializedFile<T>> {
let h = SerializedFileHeader::read(&mut file)?;
let e = h.endianness;
@@ -293,6 +296,7 @@ impl SerializedFile {
let user_string = file.read_cstr()?;
Ok(SerializedFile {
+ file,
header: h,
types,
externals,
@@ -302,6 +306,32 @@ impl SerializedFile {
user_string,
})
}
+
+ pub fn get_object_type_tree(&self, ob: &ObjectInfo) -> Result<&'_ TypeTreeNode> {
+ let r#type = if ob.type_id < 0 {
+ unimplemented!()
+ } else {
+ &self.types[ob.type_id as usize]
+ };
+ r#type
+ .type_tree
+ .as_ref()
+ .ok_or(anyhow!("type tree missing"))
+ }
+ pub fn read_object(&mut self, ob: ObjectInfo) -> Result<Value> {
+ self.file.seek(SeekFrom::Start(ob.data_offset))?;
+ //? Duplicated impl from get_object_type_tree because borrow problem
+ let r#type = if ob.type_id < 0 {
+ unimplemented!()
+ } else {
+ &self.types[ob.type_id as usize]
+ };
+ let typetree = r#type
+ .type_tree
+ .as_ref()
+ .ok_or(anyhow!("type tree missing"))?;
+ Value::read(typetree, self.endianness, &mut self.file)
+ }
}
impl TypeTreeNode {