aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/assets.rs35
-rw-r--r--src/bin/debug.rs21
-rw-r--r--src/bin/json.rs27
-rw-r--r--src/bin/yaml.rs44
-rw-r--r--src/classes.rs101
-rw-r--r--src/lib.rs2
-rw-r--r--src/object.rs31
-rw-r--r--src/serialized_file.rs13
8 files changed, 208 insertions, 66 deletions
diff --git a/src/assets.rs b/src/assets.rs
deleted file mode 100644
index a272920..0000000
--- a/src/assets.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-use crate::object::Value;
-use anyhow::Result;
-use std::collections::BTreeMap;
-
-pub enum AssetValue {
- Value(Value),
- Array(Vec<AssetValue>),
- Object {
- class: String,
- fields: BTreeMap<String, AssetValue>,
- },
-}
-
-impl AssetValue {
- pub fn from_value(value: Value) -> Result<Self> {
- Ok(match value {
- Value::Array(elems) => Self::Array(
- elems
- .into_iter()
- .map(|e| AssetValue::from_value(e))
- .collect::<Result<Vec<_>>>()?,
- ),
- Value::Object { class, fields } => match class.as_str() {
- _ => Self::Object {
- class,
- fields: fields
- .into_iter()
- .map(|(k, v)| Ok((k, AssetValue::from_value(v)?)))
- .collect::<Result<BTreeMap<_, _>>>()?,
- },
- },
- x => Self::Value(x),
- })
- }
-}
diff --git a/src/bin/debug.rs b/src/bin/debug.rs
index 9004055..5633387 100644
--- a/src/bin/debug.rs
+++ b/src/bin/debug.rs
@@ -3,10 +3,7 @@ use std::{
fs::File,
io::{BufReader, Seek, SeekFrom},
};
-use unity_tools::{
- serialized_file::{TypeTreeNode, read_serialized_file},
- unityfs::UnityFS,
-};
+use unity_tools::{serialized_file::read_serialized_file, unityfs::UnityFS};
fn main() -> anyhow::Result<()> {
env_logger::init_from_env("LOG");
@@ -34,12 +31,12 @@ fn main() -> anyhow::Result<()> {
// .expect("unknown type")
&file.types[ob.type_id as usize]
};
- fn print_types(tt: &TypeTreeNode) {
- println!("{}", tt.type_string);
- for c in &tt.children {
- print_types(&c);
- }
- }
+ // fn print_types(tt: &TypeTreeNode) {
+ // println!("{}", tt.type_string);
+ // for c in &tt.children {
+ // print_types(&c);
+ // }
+ // }
// fn print_crit_types(tt: &TypeTreeNode) {
// let mut crit = tt.byte_size == -1 || tt.children.is_empty();
// for c in &tt.children {
@@ -51,9 +48,9 @@ fn main() -> anyhow::Result<()> {
// }
// }
if let Some(tree) = &typetree.type_tree {
- // println!("{}", tree.type_string);
+ println!("{}", tree.type_string);
// print_crit_types(tree);
- print_types(tree);
+ // print_types(tree);
}
// eprintln!("{typetree:#?}");
diff --git a/src/bin/json.rs b/src/bin/json.rs
index 3155b02..4611530 100644
--- a/src/bin/json.rs
+++ b/src/bin/json.rs
@@ -1,19 +1,16 @@
use std::{
- env::args,
+ env::{args, var},
fs::File,
io::{BufReader, Seek, SeekFrom, stdout},
};
-use unity_tools::{
- object::{Value, read_value},
- serialized_file::read_serialized_file,
- unityfs::UnityFS,
-};
+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)?;
let filter = args().nth(2);
+ let pretty = var("PRETTY").is_ok();
for node in fs.nodes().to_vec() {
if node.name.ends_with(".resource") || node.name.ends_with(".resS") {
@@ -21,8 +18,6 @@ fn main() -> anyhow::Result<()> {
}
let mut cab = fs.read(&node)?;
let file = read_serialized_file(&mut cab)?;
- let e = file.endianness;
-
for ob in file.objects {
cab.seek(SeekFrom::Start(ob.data_offset))?;
let typetree = if ob.type_id < 0 {
@@ -30,18 +25,20 @@ fn main() -> anyhow::Result<()> {
} else {
&file.types[ob.type_id as usize]
};
- let value = read_value(typetree.type_tree.as_ref().unwrap(), e, &mut cab)?;
- if let Some(f) = &filter {
- if let Value::Object { class, .. } = &value {
- if class != f {
+ 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 {
- continue;
+ serde_json::to_writer(stdout(), &value.to_json()).unwrap();
}
+ println!()
}
- serde_json::to_writer(stdout(), &value.to_json()).unwrap();
- println!()
}
}
diff --git a/src/bin/yaml.rs b/src/bin/yaml.rs
new file mode 100644
index 0000000..e541c62
--- /dev/null
+++ b/src/bin/yaml.rs
@@ -0,0 +1,44 @@
+use std::{
+ env::args,
+ fs::File,
+ io::{BufReader, Seek, SeekFrom, stdout},
+};
+use unity_tools::{
+ classes::HValue, 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)?;
+ let filter = args().nth(2);
+
+ 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 file = read_serialized_file(&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)?;
+ let hvalue = HValue::from_value(value)?;
+ serde_yml::to_writer(stdout(), &hvalue).unwrap();
+ println!()
+ }
+ }
+ }
+
+ Ok(())
+}
diff --git a/src/classes.rs b/src/classes.rs
new file mode 100644
index 0000000..87bce8c
--- /dev/null
+++ b/src/classes.rs
@@ -0,0 +1,101 @@
+use crate::object::Value;
+use anyhow::Result;
+use serde::Serialize;
+use std::collections::BTreeMap;
+
+#[derive(Serialize)]
+pub enum HValue {
+ PPtr(PPtr),
+ Pair(Box<HValue>, Box<HValue>),
+ Value([Value; 1]),
+ Map(BTreeMap<String, HValue>),
+ AssetInfo(AssetInfo),
+
+ Array(Vec<HValue>),
+ Object {
+ class: String,
+ fields: BTreeMap<String, HValue>,
+ },
+}
+
+impl HValue {
+ pub fn from_value(v: Value) -> Result<HValue> {
+ Ok(match v {
+ Value::Array(a) => Self::Array(
+ a.into_iter()
+ .map(|e| HValue::from_value(e))
+ .collect::<Result<Vec<_>>>()?,
+ ),
+ Value::Object { class, fields } => {
+ let mut fields = fields
+ .into_iter()
+ .map(|(k, v)| Ok((k, HValue::from_value(v)?)))
+ .collect::<Result<BTreeMap<_, _>>>()?;
+
+ match class.as_str() {
+ x if x.starts_with("PPtr<") => {
+ let inner = x.strip_prefix("PPtr<").unwrap().strip_suffix(">").unwrap();
+ Self::PPtr(PPtr {
+ class: inner.to_owned(),
+ file_id: fields["m_FileID"].as_value().unwrap().as_i32().unwrap(),
+ path_id: fields["m_PathID"].as_value().unwrap().as_i64().unwrap(),
+ })
+ }
+ "AssetInfo" => Self::AssetInfo(AssetInfo {
+ preload_index: fields["preloadIndex"].as_value().unwrap().as_i32().unwrap(),
+ preload_size: fields["preloadSize"].as_value().unwrap().as_i32().unwrap(),
+ asset: fields.remove("asset").unwrap().as_pptr().unwrap(),
+ }),
+ "map" => {
+ let Self::Array(a) = fields.remove("Array").unwrap() else {
+ unreachable!()
+ };
+ Self::Map(
+ a.into_iter()
+ .map(|e| {
+ let Self::Pair(k, v) = e else { unreachable!() };
+ (k.as_value().unwrap().clone().as_string().unwrap(), *v)
+ })
+ .collect(),
+ )
+ }
+ "pair" => Self::Pair(
+ Box::new(fields.remove("first").unwrap()),
+ Box::new(fields.remove("second").unwrap()),
+ ),
+ "vector" => fields.remove("Array").unwrap(),
+ _ => Self::Object { class, fields },
+ }
+ }
+ x => Self::Value([x]),
+ })
+ }
+ pub fn as_value(&self) -> Option<&Value> {
+ if let HValue::Value(v) = self {
+ Some(&v[0])
+ } else {
+ None
+ }
+ }
+ pub fn as_pptr(self) -> Option<PPtr> {
+ if let HValue::PPtr(v) = self {
+ Some(v)
+ } else {
+ None
+ }
+ }
+}
+
+#[derive(Debug, Serialize)]
+pub struct PPtr {
+ class: String,
+ file_id: i32,
+ path_id: i64,
+}
+
+#[derive(Debug, Serialize)]
+pub struct AssetInfo {
+ preload_index: i32,
+ preload_size: i32,
+ asset: PPtr,
+}
diff --git a/src/lib.rs b/src/lib.rs
index 14bdc34..c2416c3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,4 +3,4 @@ pub mod helper;
pub mod common_strings;
pub mod serialized_file;
pub mod object;
-pub mod assets;
+pub mod classes;
diff --git a/src/object.rs b/src/object.rs
index be76c18..061f58e 100644
--- a/src/object.rs
+++ b/src/object.rs
@@ -2,10 +2,11 @@ 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)]
+#[derive(Debug, Clone, Serialize)]
pub enum Value {
Bool(bool),
U8(u8),
@@ -121,6 +122,34 @@ pub fn read_value(
}
impl Value {
+ pub fn as_class(self, name: &str) -> Option<BTreeMap<String, Value>> {
+ if let Value::Object { class, fields } = self {
+ if class == name { Some(fields) } else { None }
+ } else {
+ None
+ }
+ }
+ pub fn as_string(self) -> Option<String> {
+ if let Value::String(s) = self {
+ Some(s)
+ } else {
+ None
+ }
+ }
+ pub fn as_i64(&self) -> Option<i64> {
+ if let Value::I64(s) = self {
+ Some(*s)
+ } else {
+ None
+ }
+ }
+ pub fn as_i32(&self) -> Option<i32> {
+ if let Value::I32(s) = self {
+ Some(*s)
+ } else {
+ None
+ }
+ }
pub fn to_json(self) -> serde_json::Value {
match self {
Value::Bool(x) => serde_json::Value::Bool(x),
diff --git a/src/serialized_file.rs b/src/serialized_file.rs
index 9397d53..26f9391 100644
--- a/src/serialized_file.rs
+++ b/src/serialized_file.rs
@@ -3,7 +3,7 @@ use crate::{
helper::{AlignExt, Endianness, ReadExt},
};
use anyhow::{Result, bail};
-use log::{debug, info, trace};
+use log::{debug, info, trace, warn};
use std::io::{Cursor, Read, Seek};
#[derive(Debug, Clone)]
@@ -163,7 +163,12 @@ pub fn read_serialized_file(mut file: impl Read + Seek) -> Result<SerializedFile
let get_string = |off: u32| {
let data = if off & 0x80000000 != 0 {
let off = off & 0x7fffffff;
- &COMMON_STRINGS[off as usize..]
+ if off as usize > COMMON_STRINGS.len() {
+ warn!("common strings missing index {off:08x}");
+ b"<common string missing>"
+ } else {
+ &COMMON_STRINGS[off as usize..]
+ }
} else {
&string_data[off as usize..]
};
@@ -189,6 +194,10 @@ pub fn read_serialized_file(mut file: impl Read + Seek) -> Result<SerializedFile
ref_type_hash: node_data.read_u64(e)?,
children: vec![],
};
+ if node.level == 0 && !parents.is_empty() {
+ warn!("unexpected toplevel typetree node");
+ parents.clear();
+ }
while parents.len() > node.level as usize {
let n = parents.pop().unwrap();
parents.last_mut().unwrap().children.push(n)