diff options
author | metamuffin <metamuffin@disroot.org> | 2025-03-10 20:45:18 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-03-10 20:45:18 +0100 |
commit | 30bf5e07e52142a154a5660574213e59e0363ada (patch) | |
tree | a6a619a033269ab76650a820826255acafaa9b6c | |
parent | 42a5916cf742779d016eeefd1c59efbdfab64feb (diff) | |
download | unity-tools-30bf5e07e52142a154a5660574213e59e0363ada.tar unity-tools-30bf5e07e52142a154a5660574213e59e0363ada.tar.bz2 unity-tools-30bf5e07e52142a154a5660574213e59e0363ada.tar.zst |
type graph
-rw-r--r-- | src/assets.rs | 35 | ||||
-rw-r--r-- | src/bin/debug.rs | 42 | ||||
-rw-r--r-- | src/bin/json.rs | 16 | ||||
-rw-r--r-- | src/bin/typegraph.rs | 123 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/serialized_file.rs | 2 |
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" + ) +} @@ -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, |