unity_asset_binary/typetree/
builder.rs

1//! TypeTree builder and validation
2//!
3//! This module provides functionality for building and validating TypeTree structures.
4
5use super::types::{TypeTree, TypeTreeNode};
6use crate::error::{BinaryError, Result};
7use std::collections::HashMap;
8
9/// TypeTree builder
10///
11/// This struct provides methods for building TypeTree structures programmatically,
12/// including validation and optimization.
13pub struct TypeTreeBuilder {
14    tree: TypeTree,
15    node_map: HashMap<String, usize>, // name -> node index for quick lookup
16}
17
18impl TypeTreeBuilder {
19    /// Create a new TypeTree builder
20    pub fn new() -> Self {
21        Self {
22            tree: TypeTree::new(),
23            node_map: HashMap::new(),
24        }
25    }
26
27    /// Create a builder with initial capacity
28    pub fn with_capacity(capacity: usize) -> Self {
29        Self {
30            tree: TypeTree::with_capacity(capacity),
31            node_map: HashMap::with_capacity(capacity),
32        }
33    }
34
35    /// Set TypeTree version
36    pub fn version(mut self, version: u32) -> Self {
37        self.tree.version = version;
38        self
39    }
40
41    /// Set platform
42    pub fn platform(mut self, platform: u32) -> Self {
43        self.tree.platform = platform;
44        self
45    }
46
47    /// Set type dependencies flag
48    pub fn has_type_dependencies(mut self, has_deps: bool) -> Self {
49        self.tree.has_type_dependencies = has_deps;
50        self
51    }
52
53    /// Add a root node
54    pub fn add_root_node(&mut self, node: TypeTreeNode) -> Result<&mut Self> {
55        if node.level != 0 {
56            return Err(BinaryError::invalid_data("Root node must have level 0"));
57        }
58
59        let node_name = node.name.clone();
60        let index = self.tree.nodes.len();
61
62        self.tree.nodes.push(node);
63
64        if !node_name.is_empty() {
65            self.node_map.insert(node_name, index);
66        }
67
68        Ok(self)
69    }
70
71    /// Create and add a simple node
72    pub fn add_simple_node(
73        &mut self,
74        type_name: String,
75        name: String,
76        byte_size: i32,
77        level: i32,
78    ) -> Result<&mut Self> {
79        let mut node = TypeTreeNode::new();
80        node.type_name = type_name;
81        node.name = name.clone();
82        node.byte_size = byte_size;
83        node.level = level;
84        node.index = self.tree.nodes.len() as i32;
85
86        if level == 0 {
87            self.add_root_node(node)?;
88        } else {
89            return Err(BinaryError::invalid_data(
90                "Use add_child_to_node for non-root nodes",
91            ));
92        }
93
94        Ok(self)
95    }
96
97    /// Add a child node to an existing node
98    pub fn add_child_to_node(
99        &mut self,
100        parent_name: &str,
101        child: TypeTreeNode,
102    ) -> Result<&mut Self> {
103        let parent_index = self.node_map.get(parent_name).copied().ok_or_else(|| {
104            BinaryError::generic(format!("Parent node '{}' not found", parent_name))
105        })?;
106
107        // Validate child level
108        let parent_level = self.tree.nodes[parent_index].level;
109        if child.level != parent_level + 1 {
110            return Err(BinaryError::invalid_data(format!(
111                "Child level must be parent level + 1 (expected {}, got {})",
112                parent_level + 1,
113                child.level
114            )));
115        }
116
117        let child_name = child.name.clone();
118        self.tree.nodes[parent_index].children.push(child);
119
120        // Update node map if child has a name
121        if !child_name.is_empty() {
122            let child_index = self.tree.nodes[parent_index].children.len() - 1;
123            self.node_map
124                .insert(format!("{}.{}", parent_name, child_name), child_index);
125        }
126
127        Ok(self)
128    }
129
130    /// Build common primitive types
131    pub fn add_primitive_field(
132        &mut self,
133        parent_name: &str,
134        field_name: String,
135        type_name: &str,
136    ) -> Result<&mut Self> {
137        let parent_index = self.node_map.get(parent_name).copied().ok_or_else(|| {
138            BinaryError::generic(format!("Parent node '{}' not found", parent_name))
139        })?;
140
141        let parent_level = self.tree.nodes[parent_index].level;
142        let byte_size = Self::get_primitive_size(type_name)?;
143
144        let mut child = TypeTreeNode::new();
145        child.type_name = type_name.to_string();
146        child.name = field_name;
147        child.byte_size = byte_size;
148        child.level = parent_level + 1;
149        child.index = (self.tree.nodes.len() + self.tree.nodes[parent_index].children.len()) as i32;
150
151        self.add_child_to_node(parent_name, child)?;
152        Ok(self)
153    }
154
155    /// Get the size of primitive types
156    fn get_primitive_size(type_name: &str) -> Result<i32> {
157        let size = match type_name {
158            "bool" | "SInt8" | "UInt8" | "char" => 1,
159            "SInt16" | "UInt16" | "short" | "unsigned short" => 2,
160            "SInt32" | "UInt32" | "int" | "unsigned int" | "float" => 4,
161            "SInt64" | "UInt64" | "long long" | "unsigned long long" | "double" => 8,
162            "string" => -1, // Variable size
163            _ => {
164                return Err(BinaryError::invalid_data(format!(
165                    "Unknown primitive type: {}",
166                    type_name
167                )));
168            }
169        };
170        Ok(size)
171    }
172
173    /// Add an array field
174    pub fn add_array_field(
175        &mut self,
176        parent_name: &str,
177        field_name: String,
178        element_type: &str,
179    ) -> Result<&mut Self> {
180        let parent_index = self.node_map.get(parent_name).copied().ok_or_else(|| {
181            BinaryError::generic(format!("Parent node '{}' not found", parent_name))
182        })?;
183
184        let parent_level = self.tree.nodes[parent_index].level;
185
186        // Create array container node
187        let mut array_node = TypeTreeNode::new();
188        array_node.type_name = "Array".to_string();
189        array_node.name = field_name.clone();
190        array_node.byte_size = -1; // Variable size
191        array_node.level = parent_level + 1;
192        array_node.index =
193            (self.tree.nodes.len() + self.tree.nodes[parent_index].children.len()) as i32;
194
195        // Create size node
196        let mut size_node = TypeTreeNode::new();
197        size_node.type_name = "int".to_string();
198        size_node.name = "size".to_string();
199        size_node.byte_size = 4;
200        size_node.level = parent_level + 2;
201        size_node.index = array_node.index + 1;
202
203        // Create data array node
204        let mut data_node = TypeTreeNode::new();
205        data_node.type_name = format!("Array<{}>", element_type);
206        data_node.name = "data".to_string();
207        data_node.byte_size = -1; // Variable size
208        data_node.level = parent_level + 2;
209        data_node.index = array_node.index + 2;
210
211        // Create element node
212        let mut element_node = TypeTreeNode::new();
213        element_node.type_name = element_type.to_string();
214        element_node.name = String::new(); // Array elements don't have names
215        element_node.byte_size = Self::get_primitive_size(element_type).unwrap_or(-1);
216        element_node.level = parent_level + 3;
217        element_node.index = array_node.index + 3;
218
219        // Build hierarchy
220        data_node.children.push(element_node);
221        array_node.children.push(size_node);
222        array_node.children.push(data_node);
223
224        self.add_child_to_node(parent_name, array_node)?;
225        Ok(self)
226    }
227
228    /// Validate the built TypeTree
229    pub fn validate(&self) -> Result<()> {
230        self.tree.validate().map_err(BinaryError::generic)
231    }
232
233    /// Build and return the TypeTree
234    pub fn build(mut self) -> Result<TypeTree> {
235        // Final validation
236        self.validate()?;
237
238        // Update string buffer
239        self.update_string_buffer();
240
241        // Update node indices
242        self.update_node_indices();
243
244        Ok(self.tree)
245    }
246
247    /// Update the string buffer with all type and field names
248    fn update_string_buffer(&mut self) {
249        self.tree.string_buffer.clear();
250
251        // Collect all unique strings
252        let mut strings = std::collections::HashSet::new();
253        Self::collect_strings(&self.tree.nodes, &mut strings);
254
255        // Build string buffer and update offsets
256        let mut offset_map = HashMap::new();
257        for string in &strings {
258            let offset = self.tree.string_buffer.len() as u32;
259            offset_map.insert(string.clone(), offset);
260            self.tree.string_buffer.extend_from_slice(string.as_bytes());
261            self.tree.string_buffer.push(0); // Null terminator
262        }
263
264        // Update node offsets
265        Self::update_string_offsets(&mut self.tree.nodes, &offset_map);
266    }
267
268    /// Collect all strings from nodes
269    fn collect_strings(nodes: &[TypeTreeNode], strings: &mut std::collections::HashSet<String>) {
270        for node in nodes {
271            if !node.type_name.is_empty() {
272                strings.insert(node.type_name.clone());
273            }
274            if !node.name.is_empty() {
275                strings.insert(node.name.clone());
276            }
277            Self::collect_strings(&node.children, strings);
278        }
279    }
280
281    /// Update string offsets in nodes
282    fn update_string_offsets(nodes: &mut [TypeTreeNode], offset_map: &HashMap<String, u32>) {
283        for node in nodes {
284            if let Some(&offset) = offset_map.get(&node.type_name) {
285                node.type_str_offset = offset;
286            }
287            if let Some(&offset) = offset_map.get(&node.name) {
288                node.name_str_offset = offset;
289            }
290            Self::update_string_offsets(&mut node.children, offset_map);
291        }
292    }
293
294    /// Update node indices
295    fn update_node_indices(&mut self) {
296        let mut index = 0;
297        Self::update_indices(&mut self.tree.nodes, &mut index);
298    }
299
300    /// Update indices recursively
301    fn update_indices(nodes: &mut [TypeTreeNode], index: &mut i32) {
302        for node in nodes {
303            node.index = *index;
304            *index += 1;
305            Self::update_indices(&mut node.children, index);
306        }
307    }
308
309    /// Get the current tree (for inspection during building)
310    pub fn tree(&self) -> &TypeTree {
311        &self.tree
312    }
313
314    /// Get mutable access to the current tree
315    pub fn tree_mut(&mut self) -> &mut TypeTree {
316        &mut self.tree
317    }
318}
319
320impl Default for TypeTreeBuilder {
321    fn default() -> Self {
322        Self::new()
323    }
324}
325
326/// TypeTree validator
327pub struct TypeTreeValidator;
328
329impl TypeTreeValidator {
330    /// Validate a complete TypeTree
331    pub fn validate(tree: &TypeTree) -> Result<ValidationReport> {
332        let mut report = ValidationReport::new();
333
334        // Basic structure validation
335        if tree.nodes.is_empty() {
336            report.add_error("TypeTree has no nodes".to_string());
337            return Ok(report);
338        }
339
340        // Validate each root node
341        for (i, node) in tree.nodes.iter().enumerate() {
342            Self::validate_node(node, 0, &mut report, &format!("root[{}]", i));
343        }
344
345        // Validate string buffer
346        Self::validate_string_buffer(tree, &mut report);
347
348        Ok(report)
349    }
350
351    /// Validate a single node
352    fn validate_node(
353        node: &TypeTreeNode,
354        expected_level: i32,
355        report: &mut ValidationReport,
356        path: &str,
357    ) {
358        // Check level
359        if node.level != expected_level {
360            report.add_error(format!(
361                "{}: Level mismatch (expected {}, got {})",
362                path, expected_level, node.level
363            ));
364        }
365
366        // Check type name
367        if node.type_name.is_empty() {
368            report.add_error(format!("{}: Empty type name", path));
369        }
370
371        // Check byte size
372        if node.byte_size < -1 {
373            report.add_error(format!("{}: Invalid byte size ({})", path, node.byte_size));
374        }
375
376        // Validate children
377        for (i, child) in node.children.iter().enumerate() {
378            let child_path = if node.name.is_empty() {
379                format!("{}[{}]", path, i)
380            } else {
381                format!("{}.{}", path, child.name)
382            };
383            Self::validate_node(child, expected_level + 1, report, &child_path);
384        }
385    }
386
387    /// Validate string buffer
388    fn validate_string_buffer(tree: &TypeTree, report: &mut ValidationReport) {
389        // Check if string buffer is properly null-terminated
390        if !tree.string_buffer.is_empty() && tree.string_buffer[tree.string_buffer.len() - 1] != 0 {
391            report.add_warning("String buffer is not null-terminated".to_string());
392        }
393
394        // Validate string offsets
395        Self::validate_string_offsets(&tree.nodes, &tree.string_buffer, report, "root");
396    }
397
398    /// Validate string offsets in nodes
399    fn validate_string_offsets(
400        nodes: &[TypeTreeNode],
401        string_buffer: &[u8],
402        report: &mut ValidationReport,
403        path: &str,
404    ) {
405        for (i, node) in nodes.iter().enumerate() {
406            let node_path = format!("{}[{}]", path, i);
407
408            // Check type string offset
409            if node.type_str_offset as usize >= string_buffer.len() {
410                report.add_error(format!(
411                    "{}: Type string offset out of bounds ({})",
412                    node_path, node.type_str_offset
413                ));
414            }
415
416            // Check name string offset
417            if node.name_str_offset as usize >= string_buffer.len() {
418                report.add_error(format!(
419                    "{}: Name string offset out of bounds ({})",
420                    node_path, node.name_str_offset
421                ));
422            }
423
424            // Validate children
425            Self::validate_string_offsets(&node.children, string_buffer, report, &node_path);
426        }
427    }
428}
429
430/// Validation report
431#[derive(Debug, Clone)]
432pub struct ValidationReport {
433    pub errors: Vec<String>,
434    pub warnings: Vec<String>,
435}
436
437impl ValidationReport {
438    pub fn new() -> Self {
439        Self {
440            errors: Vec::new(),
441            warnings: Vec::new(),
442        }
443    }
444
445    pub fn add_error(&mut self, error: String) {
446        self.errors.push(error);
447    }
448
449    pub fn add_warning(&mut self, warning: String) {
450        self.warnings.push(warning);
451    }
452
453    pub fn is_valid(&self) -> bool {
454        self.errors.is_empty()
455    }
456
457    pub fn has_warnings(&self) -> bool {
458        !self.warnings.is_empty()
459    }
460}
461
462impl Default for ValidationReport {
463    fn default() -> Self {
464        Self::new()
465    }
466}
467
468#[cfg(test)]
469mod tests {
470    use super::*;
471
472    #[test]
473    fn test_builder_creation() {
474        let builder = TypeTreeBuilder::new();
475        assert!(builder.tree().is_empty());
476    }
477
478    #[test]
479    fn test_primitive_sizes() {
480        assert_eq!(TypeTreeBuilder::get_primitive_size("int").unwrap(), 4);
481        assert_eq!(TypeTreeBuilder::get_primitive_size("bool").unwrap(), 1);
482        assert_eq!(TypeTreeBuilder::get_primitive_size("double").unwrap(), 8);
483        assert_eq!(TypeTreeBuilder::get_primitive_size("string").unwrap(), -1);
484    }
485}