unity_asset_binary/typetree/
builder.rs1use super::types::{TypeTree, TypeTreeNode};
6use crate::error::{BinaryError, Result};
7use std::collections::HashMap;
8
9pub struct TypeTreeBuilder {
14 tree: TypeTree,
15 node_map: HashMap<String, usize>, }
17
18impl TypeTreeBuilder {
19 pub fn new() -> Self {
21 Self {
22 tree: TypeTree::new(),
23 node_map: HashMap::new(),
24 }
25 }
26
27 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 pub fn version(mut self, version: u32) -> Self {
37 self.tree.version = version;
38 self
39 }
40
41 pub fn platform(mut self, platform: u32) -> Self {
43 self.tree.platform = platform;
44 self
45 }
46
47 pub fn has_type_dependencies(mut self, has_deps: bool) -> Self {
49 self.tree.has_type_dependencies = has_deps;
50 self
51 }
52
53 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 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 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 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 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 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 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, _ => {
164 return Err(BinaryError::invalid_data(format!(
165 "Unknown primitive type: {}",
166 type_name
167 )));
168 }
169 };
170 Ok(size)
171 }
172
173 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 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; 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 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 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; data_node.level = parent_level + 2;
209 data_node.index = array_node.index + 2;
210
211 let mut element_node = TypeTreeNode::new();
213 element_node.type_name = element_type.to_string();
214 element_node.name = String::new(); 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 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 pub fn validate(&self) -> Result<()> {
230 self.tree.validate().map_err(BinaryError::generic)
231 }
232
233 pub fn build(mut self) -> Result<TypeTree> {
235 self.validate()?;
237
238 self.update_string_buffer();
240
241 self.update_node_indices();
243
244 Ok(self.tree)
245 }
246
247 fn update_string_buffer(&mut self) {
249 self.tree.string_buffer.clear();
250
251 let mut strings = std::collections::HashSet::new();
253 Self::collect_strings(&self.tree.nodes, &mut strings);
254
255 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); }
263
264 Self::update_string_offsets(&mut self.tree.nodes, &offset_map);
266 }
267
268 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 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 fn update_node_indices(&mut self) {
296 let mut index = 0;
297 Self::update_indices(&mut self.tree.nodes, &mut index);
298 }
299
300 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 pub fn tree(&self) -> &TypeTree {
311 &self.tree
312 }
313
314 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
326pub struct TypeTreeValidator;
328
329impl TypeTreeValidator {
330 pub fn validate(tree: &TypeTree) -> Result<ValidationReport> {
332 let mut report = ValidationReport::new();
333
334 if tree.nodes.is_empty() {
336 report.add_error("TypeTree has no nodes".to_string());
337 return Ok(report);
338 }
339
340 for (i, node) in tree.nodes.iter().enumerate() {
342 Self::validate_node(node, 0, &mut report, &format!("root[{}]", i));
343 }
344
345 Self::validate_string_buffer(tree, &mut report);
347
348 Ok(report)
349 }
350
351 fn validate_node(
353 node: &TypeTreeNode,
354 expected_level: i32,
355 report: &mut ValidationReport,
356 path: &str,
357 ) {
358 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 if node.type_name.is_empty() {
368 report.add_error(format!("{}: Empty type name", path));
369 }
370
371 if node.byte_size < -1 {
373 report.add_error(format!("{}: Invalid byte size ({})", path, node.byte_size));
374 }
375
376 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 fn validate_string_buffer(tree: &TypeTree, report: &mut ValidationReport) {
389 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 Self::validate_string_offsets(&tree.nodes, &tree.string_buffer, report, "root");
396 }
397
398 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 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 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 Self::validate_string_offsets(&node.children, string_buffer, report, &node_path);
426 }
427 }
428}
429
430#[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}