aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-03-10 20:45:18 +0100
committermetamuffin <metamuffin@disroot.org>2025-03-10 20:45:18 +0100
commit30bf5e07e52142a154a5660574213e59e0363ada (patch)
treea6a619a033269ab76650a820826255acafaa9b6c
parent42a5916cf742779d016eeefd1c59efbdfab64feb (diff)
downloadunity-tools-30bf5e07e52142a154a5660574213e59e0363ada.tar
unity-tools-30bf5e07e52142a154a5660574213e59e0363ada.tar.bz2
unity-tools-30bf5e07e52142a154a5660574213e59e0363ada.tar.zst
type graph
-rw-r--r--src/assets.rs35
-rw-r--r--src/bin/debug.rs42
-rw-r--r--src/bin/json.rs16
-rw-r--r--src/bin/typegraph.rs123
-rw-r--r--src/lib.rs1
-rw-r--r--src/serialized_file.rs2
6 files changed, 195 insertions, 24 deletions
diff --git a/src/assets.rs b/src/assets.rs
new file mode 100644
index 0000000..a272920
--- /dev/null
+++ b/src/assets.rs
@@ -0,0 +1,35 @@
+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 5da7c03..9004055 100644
--- a/src/bin/debug.rs
+++ b/src/bin/debug.rs
@@ -1,10 +1,12 @@
-use log::debug;
use std::{
env::args,
fs::File,
io::{BufReader, Seek, SeekFrom},
};
-use unity_tools::{object::read_value, serialized_file::read_serialized_file, unityfs::UnityFS};
+use unity_tools::{
+ serialized_file::{TypeTreeNode, read_serialized_file},
+ unityfs::UnityFS,
+};
fn main() -> anyhow::Result<()> {
env_logger::init_from_env("LOG");
@@ -16,12 +18,8 @@ fn main() -> anyhow::Result<()> {
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))?;
@@ -36,12 +34,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 {
@@ -52,23 +50,23 @@ fn main() -> anyhow::Result<()> {
// println!("{}", tt.type_string);
// }
// }
- // if let Some(tree) = &typetree.type_tree {
- // println!("{}", tree.type_string);
- // print_crit_types(tree);
- // print_types(tree);
- // }
+ if let Some(tree) = &typetree.type_tree {
+ // println!("{}", tree.type_string);
+ // print_crit_types(tree);
+ print_types(tree);
+ }
// eprintln!("{typetree:#?}");
- let value = read_value(typetree.type_tree.as_ref().unwrap(), e, &mut cab)?;
+ // let value = read_value(typetree.type_tree.as_ref().unwrap(), e, &mut cab)?;
// if let Value::Object { class, .. } = &value {
// println!("{class}")
// }
- debug!(
- "{}",
- serde_json::to_string_pretty(&value.to_json()).unwrap()
- )
+ // debug!(
+ // "{}",
+ // serde_json::to_string_pretty(&value.to_json()).unwrap()
+ // )
}
// eprintln!("{:#?}", file.types);
}
diff --git a/src/bin/json.rs b/src/bin/json.rs
index a9685ed..3155b02 100644
--- a/src/bin/json.rs
+++ b/src/bin/json.rs
@@ -3,12 +3,17 @@ use std::{
fs::File,
io::{BufReader, Seek, SeekFrom, stdout},
};
-use unity_tools::{object::read_value, serialized_file::read_serialized_file, unityfs::UnityFS};
+use unity_tools::{
+ object::{Value, 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") {
@@ -26,6 +31,15 @@ fn main() -> anyhow::Result<()> {
&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 {
+ continue;
+ }
+ } else {
+ continue;
+ }
+ }
serde_json::to_writer(stdout(), &value.to_json()).unwrap();
println!()
}
diff --git a/src/bin/typegraph.rs b/src/bin/typegraph.rs
new file mode 100644
index 0000000..79c096b
--- /dev/null
+++ b/src/bin/typegraph.rs
@@ -0,0 +1,123 @@
+#![feature(random)]
+use std::{
+ collections::{BTreeMap, BTreeSet},
+ env::args,
+ fs::File,
+ io::BufReader,
+};
+use unity_tools::{
+ serialized_file::{TypeTreeNode, 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_prims = args().any(|a| a == "no_primitives");
+
+ let mut edges = BTreeSet::new();
+ 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 {
+ let typetree = if ob.type_id < 0 {
+ unimplemented!()
+ } else {
+ &file.types[ob.type_id as usize]
+ };
+ fn print_types(
+ edges: &mut BTreeSet<(String, String)>,
+ filter_prims: bool,
+ tt: TypeTreeNode,
+ ) {
+ for c in tt.children {
+ let mut c = vec![c];
+ loop {
+ let mut nc = Vec::new();
+ let mut f = false;
+ for mut c in c {
+ if let Some(inner) = c.type_string.strip_prefix("PPtr<") {
+ c.type_string = inner.strip_suffix(">").unwrap().to_owned();
+ f = true;
+ } else if matches!(c.type_string.as_str(), "Array" | "pair" | "map") {
+ nc.extend(c.children);
+ f = true
+ } else {
+ nc.push(c);
+ };
+ }
+ c = nc;
+ if !f {
+ break;
+ }
+ }
+ for c in c {
+ if filter_prims && is_primitive(&c.type_string) {
+ continue;
+ }
+ edges.insert((tt.type_string.to_owned(), c.type_string.to_owned()));
+ print_types(edges, filter_prims, c);
+ }
+ }
+ }
+
+ if let Some(tree) = &typetree.type_tree {
+ print_types(&mut edges, filter_prims, tree.clone());
+ }
+ }
+ }
+ let nodes = edges
+ .iter()
+ .flat_map(|(k, v)| [k, v])
+ .collect::<BTreeSet<_>>();
+ let ids = nodes
+ .into_iter()
+ .enumerate()
+ .map(|(i, n)| (n, i))
+ .collect::<BTreeMap<_, _>>();
+
+ println!("digraph {{");
+ for (name, id) in &ids {
+ println!("t{id} [label={name:?}]");
+ }
+ for (a, b) in &edges {
+ println!("t{} -> t{}", ids[a], ids[b]);
+ }
+ println!("}}");
+
+ Ok(())
+}
+
+fn is_primitive(s: &str) -> bool {
+ matches!(
+ s,
+ "Type*"
+ | "int"
+ | "unsigned int"
+ | "UInt8"
+ | "UInt16"
+ | "UInt32"
+ | "UInt64"
+ | "SInt8"
+ | "SInt16"
+ | "SInt32"
+ | "SInt64"
+ | "bool"
+ | "float"
+ | "vector"
+ | "string"
+ | "float3"
+ | "float4"
+ | "float2"
+ | "Vector2f"
+ | "Vector3f"
+ | "Vector4f"
+ | "Quaternionf"
+ | "xform"
+ )
+}
diff --git a/src/lib.rs b/src/lib.rs
index c456e91..14bdc34 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,3 +3,4 @@ pub mod helper;
pub mod common_strings;
pub mod serialized_file;
pub mod object;
+pub mod assets;
diff --git a/src/serialized_file.rs b/src/serialized_file.rs
index d8238ea..9397d53 100644
--- a/src/serialized_file.rs
+++ b/src/serialized_file.rs
@@ -6,7 +6,7 @@ use anyhow::{Result, bail};
use log::{debug, info, trace};
use std::io::{Cursor, Read, Seek};
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct TypeTreeNode {
pub version: u16,
pub level: u8,