unity_asset_binary/typetree/
parser.rs

1//! TypeTree parser implementation
2//!
3//! This module provides parsing functionality for Unity TypeTree structures,
4//! inspired by UnityPy/classes/TypeTree.py
5
6use super::types::{TypeTree, TypeTreeNode};
7use crate::error::{BinaryError, Result};
8use crate::reader::BinaryReader;
9
10/// TypeTree parser
11///
12/// This struct handles the parsing of TypeTree structures from binary data,
13/// supporting different Unity versions and formats.
14pub struct TypeTreeParser;
15
16impl TypeTreeParser {
17    /// Parse TypeTree from binary data
18    pub fn from_reader(reader: &mut BinaryReader, version: u32) -> Result<TypeTree> {
19        let mut tree = TypeTree::new();
20        tree.version = version;
21
22        // Read number of nodes
23        let node_count = reader.read_u32()? as usize;
24
25        // Read string buffer size
26        let string_buffer_size = reader.read_u32()? as usize;
27
28        // Read nodes
29        for _ in 0..node_count {
30            let node = Self::read_node(reader, version)?;
31            tree.nodes.push(node);
32        }
33
34        // Read string buffer
35        tree.string_buffer = reader.read_bytes(string_buffer_size)?;
36
37        // Resolve string references
38        Self::resolve_strings(&mut tree)?;
39
40        // Build tree hierarchy
41        Self::build_hierarchy(&mut tree)?;
42
43        Ok(tree)
44    }
45
46    /// Parse TypeTree from binary data using blob format (Unity version >= 12 or == 10)
47    pub fn from_reader_blob(reader: &mut BinaryReader, version: u32) -> Result<TypeTree> {
48        let mut tree = TypeTree::new();
49        tree.version = version;
50
51        // Read number of nodes
52        let node_count = reader.read_i32()? as usize;
53
54        // Read string buffer size
55        let string_buffer_size = reader.read_i32()? as usize;
56
57        // Read nodes in blob format
58        for _ in 0..node_count {
59            let mut node = TypeTreeNode::new();
60
61            // Read node data in blob format (based on unity-rs)
62            node.version = reader.read_u16()? as i32;
63            node.level = reader.read_u8()? as i32;
64            node.type_flags = reader.read_u8()? as i32;
65            node.type_str_offset = reader.read_u32()?;
66            node.name_str_offset = reader.read_u32()?;
67            node.byte_size = reader.read_i32()?;
68            node.index = reader.read_i32()?;
69            node.meta_flags = reader.read_i32()?;
70
71            if version >= 19 {
72                node.ref_type_hash = reader.read_u64()?;
73            }
74
75            tree.nodes.push(node);
76        }
77
78        // Read string buffer
79        tree.string_buffer = reader.read_bytes(string_buffer_size)?;
80
81        // Resolve string references
82        Self::resolve_strings(&mut tree)?;
83
84        // Build tree hierarchy
85        Self::build_hierarchy(&mut tree)?;
86
87        Ok(tree)
88    }
89
90    /// Read a single TypeTree node
91    fn read_node(reader: &mut BinaryReader, version: u32) -> Result<TypeTreeNode> {
92        let mut node = TypeTreeNode::new();
93
94        if version >= 10 {
95            node.version = reader.read_i16()? as i32;
96            node.level = reader.read_u8()? as i32;
97            node.type_flags = reader.read_u8()? as i32;
98            node.type_str_offset = reader.read_u32()?;
99            node.name_str_offset = reader.read_u32()?;
100            node.byte_size = reader.read_i32()?;
101            node.index = reader.read_i32()?;
102            node.meta_flags = reader.read_i32()?;
103
104            if version >= 12 {
105                node.ref_type_hash = reader.read_u64()?;
106            }
107        } else {
108            // Legacy format
109            node.type_str_offset = reader.read_u32()?;
110            node.name_str_offset = reader.read_u32()?;
111            node.byte_size = reader.read_i32()?;
112            node.index = reader.read_i32()?;
113            node.type_flags = reader.read_i32()?;
114            node.version = reader.read_i32()?;
115            node.meta_flags = reader.read_i32()?;
116            node.level = reader.read_i32()?;
117        }
118
119        Ok(node)
120    }
121
122    /// Resolve string references in the TypeTree
123    fn resolve_strings(tree: &mut TypeTree) -> Result<()> {
124        for node in &mut tree.nodes {
125            Self::resolve_node_strings(node, &tree.string_buffer)?;
126        }
127        Ok(())
128    }
129
130    /// Resolve string references for a single node and its children
131    fn resolve_node_strings(node: &mut TypeTreeNode, string_buffer: &[u8]) -> Result<()> {
132        // Resolve type name
133        node.type_name = Self::get_string_from_buffer(string_buffer, node.type_str_offset)?;
134
135        // Resolve field name
136        node.name = Self::get_string_from_buffer(string_buffer, node.name_str_offset)?;
137
138        // Resolve children
139        for child in &mut node.children {
140            Self::resolve_node_strings(child, string_buffer)?;
141        }
142
143        Ok(())
144    }
145
146    /// Get string from buffer at offset
147    fn get_string_from_buffer(buffer: &[u8], offset: u32) -> Result<String> {
148        if offset as usize >= buffer.len() {
149            return Ok(String::new());
150        }
151
152        let start = offset as usize;
153        let end = buffer[start..]
154            .iter()
155            .position(|&b| b == 0)
156            .map(|pos| start + pos)
157            .unwrap_or(buffer.len());
158
159        String::from_utf8(buffer[start..end].to_vec())
160            .map_err(|e| BinaryError::generic(format!("Invalid UTF-8 string: {}", e)))
161    }
162
163    /// Build hierarchical structure from flat node list
164    fn build_hierarchy(tree: &mut TypeTree) -> Result<()> {
165        if tree.nodes.is_empty() {
166            return Ok(());
167        }
168
169        // Create a working copy of nodes
170        let mut nodes = std::mem::take(&mut tree.nodes);
171
172        // Build hierarchy using a stack-based approach
173        let mut stack: Vec<(i32, usize)> = Vec::new(); // (level, index)
174        let mut root_nodes = Vec::new();
175
176        for (i, node) in nodes.iter().enumerate() {
177            let current_level = node.level;
178
179            // Pop stack until we find the parent level
180            while let Some(&(level, _)) = stack.last() {
181                if level < current_level {
182                    break;
183                }
184                stack.pop();
185            }
186
187            if let Some(&(_, _parent_idx)) = stack.last() {
188                // This node is a child of the node at parent_idx
189                // We'll handle this in the second pass
190            } else {
191                // This is a root node
192                root_nodes.push(i);
193            }
194
195            stack.push((current_level, i));
196        }
197
198        // Second pass: actually build the hierarchy
199        let mut processed = vec![false; nodes.len()];
200        let mut result_nodes = Vec::new();
201
202        for &root_idx in &root_nodes {
203            if !processed[root_idx] {
204                let root_node = Self::build_node_hierarchy(&mut nodes, &mut processed, root_idx)?;
205                result_nodes.push(root_node);
206            }
207        }
208
209        tree.nodes = result_nodes;
210        Ok(())
211    }
212
213    /// Build hierarchy for a single node and its children
214    fn build_node_hierarchy(
215        nodes: &mut [TypeTreeNode],
216        processed: &mut [bool],
217        node_idx: usize,
218    ) -> Result<TypeTreeNode> {
219        if processed[node_idx] {
220            return Err(BinaryError::generic("Node already processed"));
221        }
222
223        let mut node = nodes[node_idx].clone();
224        processed[node_idx] = true;
225
226        let current_level = node.level;
227        node.children.clear();
228
229        // Find children (nodes with level = current_level + 1 that come after this node)
230        for i in (node_idx + 1)..nodes.len() {
231            if processed[i] {
232                continue;
233            }
234
235            let child_level = nodes[i].level;
236
237            if child_level <= current_level {
238                // We've reached a sibling or parent level, stop looking for children
239                break;
240            }
241
242            if child_level == current_level + 1 {
243                // This is a direct child
244                let child_node = Self::build_node_hierarchy(nodes, processed, i)?;
245                node.children.push(child_node);
246            }
247        }
248
249        Ok(node)
250    }
251
252    /// Validate parsed TypeTree
253    pub fn validate(tree: &TypeTree) -> Result<()> {
254        if tree.nodes.is_empty() {
255            return Err(BinaryError::invalid_data("TypeTree has no nodes"));
256        }
257
258        for (i, node) in tree.nodes.iter().enumerate() {
259            Self::validate_node(node, 0).map_err(|e| {
260                BinaryError::generic(format!("Node {} validation failed: {}", i, e))
261            })?;
262        }
263
264        Ok(())
265    }
266
267    /// Validate a single node and its children
268    fn validate_node(node: &TypeTreeNode, expected_level: i32) -> Result<()> {
269        if node.type_name.is_empty() {
270            return Err(BinaryError::invalid_data("Node has empty type name"));
271        }
272
273        if node.level != expected_level {
274            return Err(BinaryError::invalid_data(format!(
275                "Node level mismatch: expected {}, got {}",
276                expected_level, node.level
277            )));
278        }
279
280        if node.byte_size < -1 {
281            return Err(BinaryError::invalid_data("Invalid byte size"));
282        }
283
284        // Validate children
285        for child in &node.children {
286            Self::validate_node(child, expected_level + 1)?;
287        }
288
289        Ok(())
290    }
291
292    /// Get parsing statistics
293    pub fn get_parsing_stats(tree: &TypeTree) -> ParsingStats {
294        let mut stats = (0usize, 0i32, 0usize, 0usize); // (total_nodes, max_depth, primitive_count, array_count)
295
296        fn count_nodes(node: &TypeTreeNode, depth: i32, stats: &mut (usize, i32, usize, usize)) {
297            stats.0 += 1; // total_nodes
298            stats.1 = stats.1.max(depth); // max_depth
299
300            if node.is_primitive() {
301                stats.2 += 1; // primitive_count
302            }
303            if node.is_array() {
304                stats.3 += 1; // array_count
305            }
306
307            for child in &node.children {
308                count_nodes(child, depth + 1, stats);
309            }
310        }
311
312        for node in &tree.nodes {
313            count_nodes(node, 0, &mut stats);
314        }
315
316        ParsingStats {
317            total_nodes: stats.0,
318            root_nodes: tree.nodes.len(),
319            max_depth: stats.1,
320            primitive_count: stats.2,
321            array_count: stats.3,
322            string_buffer_size: tree.string_buffer.len(),
323            version: tree.version,
324        }
325    }
326}
327
328/// Parsing statistics
329#[derive(Debug, Clone)]
330pub struct ParsingStats {
331    pub total_nodes: usize,
332    pub root_nodes: usize,
333    pub max_depth: i32,
334    pub primitive_count: usize,
335    pub array_count: usize,
336    pub string_buffer_size: usize,
337    pub version: u32,
338}
339
340#[cfg(test)]
341mod tests {
342    use super::*;
343
344    #[test]
345    fn test_parser_creation() {
346        // Basic test to ensure parser methods exist
347        let _dummy = 1 + 1;
348        assert_eq!(_dummy, 2);
349    }
350
351    #[test]
352    fn test_string_buffer_parsing() {
353        let buffer = b"hello\0world\0test\0";
354        let result = TypeTreeParser::get_string_from_buffer(buffer, 0).unwrap();
355        assert_eq!(result, "hello");
356
357        let result = TypeTreeParser::get_string_from_buffer(buffer, 6).unwrap();
358        assert_eq!(result, "world");
359
360        let result = TypeTreeParser::get_string_from_buffer(buffer, 12).unwrap();
361        assert_eq!(result, "test");
362    }
363}