unity_asset_binary/typetree/
mod.rs

1//! Unity TypeTree processing module
2//!
3//! This module provides comprehensive TypeTree processing capabilities,
4//! organized following UnityPy and unity-rs best practices.
5//!
6//! # Architecture
7//!
8//! The module is organized into several sub-modules:
9//! - `types` - Core data structures (TypeTree, TypeTreeNode, etc.)
10//! - `parser` - TypeTree parsing from binary data
11//! - `builder` - TypeTree construction and validation
12//! - `serializer` - Object serialization using TypeTree information
13//!
14//! # Examples
15//!
16//! ```rust,no_run
17//! use unity_asset_binary::typetree::{TypeTreeParser, TypeTreeBuilder, TypeTreeSerializer};
18//! use unity_asset_binary::reader::BinaryReader;
19//!
20//! // Create mock data for demonstration
21//! let data = vec![0u8; 100];
22//! let object_data = vec![0u8; 50];
23//!
24//! // Parse TypeTree from binary data
25//! let mut reader = BinaryReader::new(&data, unity_asset_binary::reader::ByteOrder::Little);
26//! let tree = TypeTreeParser::from_reader(&mut reader, 19)?;
27//!
28//! // Use TypeTree to parse object data
29//! let serializer = TypeTreeSerializer::new(&tree);
30//! let mut object_reader = BinaryReader::new(&object_data, unity_asset_binary::reader::ByteOrder::Little);
31//! let parsed_object = serializer.parse_object(&mut object_reader)?;
32//!
33//! // Build TypeTree programmatically
34//! let mut builder = TypeTreeBuilder::new().version(19);
35//! builder.add_simple_node("GameObject".to_string(), "Base".to_string(), -1, 0)?;
36//! let built_tree = builder.build()?;
37//! # Ok::<(), unity_asset_binary::error::BinaryError>(())
38//! ```
39
40pub mod builder;
41mod common_strings;
42pub mod parser;
43pub mod registry;
44pub mod serializer;
45pub mod tpk;
46pub mod types;
47
48// Re-export main types for easy access
49pub use builder::{TypeTreeBuilder, TypeTreeValidator, ValidationReport};
50pub use parser::{ParsingStats, TypeTreeParser};
51pub use registry::{
52    CompositeTypeTreeRegistry, InMemoryTypeTreeRegistry, JsonTypeTreeRegistry, TypeTreeRegistry,
53};
54pub use serializer::{
55    PPtrScanResult, TypeTreeParseMode, TypeTreeParseOptions, TypeTreeParseOutput,
56    TypeTreeParseWarning, TypeTreeSerializer,
57};
58pub use tpk::TpkTypeTreeRegistry;
59pub use types::{TypeInfo, TypeRegistry, TypeTree, TypeTreeNode, TypeTreeStatistics};
60
61/// Main TypeTree processing facade
62///
63/// This struct provides a high-level interface for TypeTree processing,
64/// combining parsing, building, and serialization functionality.
65pub struct TypeTreeProcessor {
66    tree: Option<TypeTree>,
67    version: u32,
68}
69
70impl TypeTreeProcessor {
71    /// Create a new TypeTree processor
72    pub fn new() -> Self {
73        Self {
74            tree: None,
75            version: 19, // Default to Unity 2019+ format
76        }
77    }
78
79    /// Create a processor with a specific Unity version
80    pub fn with_version(version: u32) -> Self {
81        Self {
82            tree: None,
83            version,
84        }
85    }
86
87    /// Parse TypeTree from binary data
88    pub fn parse_from_reader(
89        &mut self,
90        reader: &mut crate::reader::BinaryReader,
91    ) -> crate::error::Result<()> {
92        let tree = if self.version >= 12 || self.version == 10 {
93            TypeTreeParser::from_reader_blob(reader, self.version)?
94        } else {
95            TypeTreeParser::from_reader(reader, self.version)?
96        };
97
98        self.tree = Some(tree);
99        Ok(())
100    }
101
102    /// Parse object data using the loaded TypeTree
103    pub fn parse_object(
104        &self,
105        reader: &mut crate::reader::BinaryReader,
106    ) -> crate::error::Result<indexmap::IndexMap<String, unity_asset_core::UnityValue>> {
107        let tree = self
108            .tree
109            .as_ref()
110            .ok_or_else(|| crate::error::BinaryError::generic("No TypeTree loaded"))?;
111
112        let serializer = TypeTreeSerializer::new(tree);
113        serializer.parse_object(reader)
114    }
115
116    /// Serialize object data using the loaded TypeTree
117    pub fn serialize_object(
118        &self,
119        data: &indexmap::IndexMap<String, unity_asset_core::UnityValue>,
120    ) -> crate::error::Result<Vec<u8>> {
121        let tree = self
122            .tree
123            .as_ref()
124            .ok_or_else(|| crate::error::BinaryError::generic("No TypeTree loaded"))?;
125
126        let serializer = TypeTreeSerializer::new(tree);
127        serializer.serialize_object(data)
128    }
129
130    /// Get the loaded TypeTree
131    pub fn tree(&self) -> Option<&TypeTree> {
132        self.tree.as_ref()
133    }
134
135    /// Set a TypeTree manually
136    pub fn set_tree(&mut self, tree: TypeTree) {
137        self.tree = Some(tree);
138    }
139
140    /// Validate the loaded TypeTree
141    pub fn validate(&self) -> crate::error::Result<ValidationReport> {
142        let tree = self
143            .tree
144            .as_ref()
145            .ok_or_else(|| crate::error::BinaryError::generic("No TypeTree loaded"))?;
146
147        TypeTreeValidator::validate(tree)
148    }
149
150    /// Get TypeTree statistics
151    pub fn statistics(&self) -> Option<TypeTreeStatistics> {
152        self.tree.as_ref().map(|tree| tree.statistics())
153    }
154
155    /// Get parsing statistics
156    pub fn parsing_stats(&self) -> Option<ParsingStats> {
157        self.tree.as_ref().map(TypeTreeParser::get_parsing_stats)
158    }
159
160    /// Clear the loaded TypeTree
161    pub fn clear(&mut self) {
162        self.tree = None;
163    }
164
165    /// Check if a TypeTree is loaded
166    pub fn has_tree(&self) -> bool {
167        self.tree.is_some()
168    }
169
170    /// Get the Unity version
171    pub fn version(&self) -> u32 {
172        self.version
173    }
174
175    /// Set the Unity version
176    pub fn set_version(&mut self, version: u32) {
177        self.version = version;
178    }
179}
180
181impl Default for TypeTreeProcessor {
182    fn default() -> Self {
183        Self::new()
184    }
185}
186
187/// Convenience functions for common operations
188/// Create a TypeTree processor with default settings
189pub fn create_processor() -> TypeTreeProcessor {
190    TypeTreeProcessor::default()
191}
192
193/// Parse TypeTree from binary data with version detection
194pub fn parse_typetree(data: &[u8], version: u32) -> crate::error::Result<TypeTree> {
195    let mut reader = crate::reader::BinaryReader::new(data, crate::reader::ByteOrder::Little);
196
197    if version >= 12 || version == 10 {
198        TypeTreeParser::from_reader_blob(&mut reader, version)
199    } else {
200        TypeTreeParser::from_reader(&mut reader, version)
201    }
202}
203
204/// Parse object using TypeTree
205pub fn parse_object_with_typetree(
206    tree: &TypeTree,
207    data: &[u8],
208) -> crate::error::Result<indexmap::IndexMap<String, unity_asset_core::UnityValue>> {
209    let mut reader = crate::reader::BinaryReader::new(data, crate::reader::ByteOrder::Little);
210    let serializer = TypeTreeSerializer::new(tree);
211    serializer.parse_object(&mut reader)
212}
213
214/// Serialize object using TypeTree
215pub fn serialize_object_with_typetree(
216    tree: &TypeTree,
217    data: &indexmap::IndexMap<String, unity_asset_core::UnityValue>,
218) -> crate::error::Result<Vec<u8>> {
219    let serializer = TypeTreeSerializer::new(tree);
220    serializer.serialize_object(data)
221}
222
223/// Build a simple TypeTree for common Unity types
224pub fn build_common_typetree(class_name: &str) -> crate::error::Result<TypeTree> {
225    let mut builder = TypeTreeBuilder::new().version(19);
226
227    match class_name {
228        "GameObject" => {
229            builder.add_simple_node("GameObject".to_string(), "Base".to_string(), -1, 0)?;
230            let tree = builder.tree_mut();
231            if let Some(_root) = tree.nodes.get_mut(0) {
232                builder.add_child_to_node(
233                    "Base",
234                    TypeTreeNode::with_info("int".to_string(), "m_InstanceID".to_string(), 4),
235                )?;
236                builder.add_child_to_node(
237                    "Base",
238                    TypeTreeNode::with_info("string".to_string(), "m_Name".to_string(), -1),
239                )?;
240            }
241        }
242        "Transform" => {
243            builder.add_simple_node("Transform".to_string(), "Base".to_string(), -1, 0)?;
244            // Add common Transform fields
245            builder.add_primitive_field("Base", "m_LocalPosition".to_string(), "Vector3f")?;
246            builder.add_primitive_field("Base", "m_LocalRotation".to_string(), "Quaternionf")?;
247            builder.add_primitive_field("Base", "m_LocalScale".to_string(), "Vector3f")?;
248        }
249        _ => {
250            return Err(crate::error::BinaryError::unsupported(format!(
251                "Common TypeTree for '{}' not implemented",
252                class_name
253            )));
254        }
255    }
256
257    builder.build()
258}
259
260/// Validate TypeTree structure
261pub fn validate_typetree(tree: &TypeTree) -> crate::error::Result<ValidationReport> {
262    TypeTreeValidator::validate(tree)
263}
264
265/// Get TypeTree information summary
266pub fn get_typetree_info(tree: &TypeTree) -> TypeTreeInfo {
267    let stats = tree.statistics();
268
269    TypeTreeInfo {
270        version: tree.version,
271        platform: tree.platform,
272        has_type_dependencies: tree.has_type_dependencies,
273        node_count: stats.total_nodes,
274        root_node_count: stats.root_nodes,
275        max_depth: stats.max_depth,
276        primitive_count: stats.primitive_count,
277        array_count: stats.array_count,
278        string_buffer_size: stats.string_buffer_size,
279    }
280}
281
282/// TypeTree information summary
283#[derive(Debug, Clone)]
284pub struct TypeTreeInfo {
285    pub version: u32,
286    pub platform: u32,
287    pub has_type_dependencies: bool,
288    pub node_count: usize,
289    pub root_node_count: usize,
290    pub max_depth: i32,
291    pub primitive_count: usize,
292    pub array_count: usize,
293    pub string_buffer_size: usize,
294}
295
296/// Check if TypeTree format is supported
297pub fn is_version_supported(version: u32) -> bool {
298    // Support Unity versions 5.0+ (version 10+)
299    version >= 10
300}
301
302/// Get recommended parsing method for Unity version
303pub fn get_parsing_method(version: u32) -> &'static str {
304    if version >= 12 || version == 10 {
305        "blob"
306    } else {
307        "legacy"
308    }
309}
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314
315    #[test]
316    fn test_processor_creation() {
317        let processor = create_processor();
318        assert!(!processor.has_tree());
319        assert_eq!(processor.version(), 19);
320    }
321
322    #[test]
323    fn test_version_support() {
324        assert!(is_version_supported(19));
325        assert!(is_version_supported(10));
326        assert!(!is_version_supported(5));
327    }
328
329    #[test]
330    fn test_parsing_method() {
331        assert_eq!(get_parsing_method(19), "blob");
332        assert_eq!(get_parsing_method(12), "blob");
333        assert_eq!(get_parsing_method(10), "blob");
334        assert_eq!(get_parsing_method(9), "legacy");
335    }
336
337    #[test]
338    fn test_typetree_info() {
339        let tree = TypeTree::new();
340        let info = get_typetree_info(&tree);
341        assert_eq!(info.node_count, 0);
342        assert_eq!(info.root_node_count, 0);
343    }
344
345    #[test]
346    fn test_common_typetree_building() {
347        // Test that common TypeTree building doesn't panic
348        let result = build_common_typetree("GameObject");
349        assert!(result.is_ok() || result.is_err()); // Either way is fine for this test
350    }
351}