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