1use super::types::{TypeTree, TypeTreeNode};
7use crate::asset::SerializedType;
8use crate::error::{BinaryError, Result};
9use crate::reader::{BinaryReader, ByteOrder};
10use indexmap::IndexMap;
11use unity_asset_core::UnityValue;
12
13pub struct TypeTreeSerializer<'a> {
18 tree: &'a TypeTree,
19}
20
21#[derive(Debug, Default, Clone, PartialEq, Eq)]
22pub struct PPtrScanResult {
23 pub internal: Vec<i64>,
24 pub external: Vec<(i32, i64)>,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
28pub enum TypeTreeParseMode {
29 Strict,
30 #[default]
31 Lenient,
32}
33
34#[derive(Debug, Clone, Copy, Default)]
35pub struct TypeTreeParseOptions {
36 pub mode: TypeTreeParseMode,
37}
38
39#[derive(Debug, Clone)]
40pub struct TypeTreeParseWarning {
41 pub field: String,
42 pub error: String,
43}
44
45#[derive(Debug, Default)]
46pub struct TypeTreeParseOutput {
47 pub properties: IndexMap<String, UnityValue>,
48 pub warnings: Vec<TypeTreeParseWarning>,
49}
50
51#[derive(Debug)]
52struct TypeTreeParseContext<'a> {
53 options: TypeTreeParseOptions,
54 ref_types: Option<&'a [SerializedType]>,
55 has_managed_registry: bool,
56}
57
58#[derive(Debug)]
59struct TypeTreeScanContext<'a> {
60 ref_types: Option<&'a [SerializedType]>,
61 has_managed_registry: bool,
62}
63
64impl<'a> TypeTreeSerializer<'a> {
65 const MAX_ARRAY_LEN: usize = 1_000_000;
66 const MAX_TYPELESSDATA_LEN: usize = Self::MAX_ARRAY_LEN;
67
68 pub fn new(tree: &'a TypeTree) -> Self {
70 Self { tree }
71 }
72
73 pub fn parse_object(&self, reader: &mut BinaryReader) -> Result<IndexMap<String, UnityValue>> {
75 Ok(self
76 .parse_object_detailed(reader, TypeTreeParseOptions::default())?
77 .properties)
78 }
79
80 pub fn parse_object_with_ref_types(
81 &self,
82 reader: &mut BinaryReader,
83 ref_types: &'a [SerializedType],
84 ) -> Result<IndexMap<String, UnityValue>> {
85 Ok(self
86 .parse_object_detailed_with_ref_types(
87 reader,
88 TypeTreeParseOptions::default(),
89 ref_types,
90 )?
91 .properties)
92 }
93
94 pub fn parse_object_detailed(
95 &self,
96 reader: &mut BinaryReader,
97 options: TypeTreeParseOptions,
98 ) -> Result<TypeTreeParseOutput> {
99 self.parse_object_prefix_detailed(reader, options, usize::MAX)
100 }
101
102 pub fn parse_object_detailed_with_ref_types(
103 &self,
104 reader: &mut BinaryReader,
105 options: TypeTreeParseOptions,
106 ref_types: &'a [SerializedType],
107 ) -> Result<TypeTreeParseOutput> {
108 self.parse_object_prefix_detailed_with_ref_types(reader, options, usize::MAX, ref_types)
109 }
110
111 pub fn parse_object_prefix_detailed(
116 &self,
117 reader: &mut BinaryReader,
118 options: TypeTreeParseOptions,
119 root_children: usize,
120 ) -> Result<TypeTreeParseOutput> {
121 self.parse_object_prefix_ctx(
122 reader,
123 TypeTreeParseContext {
124 options,
125 ref_types: None,
126 has_managed_registry: false,
127 },
128 root_children,
129 )
130 }
131
132 pub fn parse_object_prefix_detailed_with_ref_types(
133 &self,
134 reader: &mut BinaryReader,
135 options: TypeTreeParseOptions,
136 root_children: usize,
137 ref_types: &'a [SerializedType],
138 ) -> Result<TypeTreeParseOutput> {
139 self.parse_object_prefix_ctx(
140 reader,
141 TypeTreeParseContext {
142 options,
143 ref_types: Some(ref_types),
144 has_managed_registry: false,
145 },
146 root_children,
147 )
148 }
149
150 fn parse_object_prefix_ctx(
151 &self,
152 reader: &mut BinaryReader,
153 mut ctx: TypeTreeParseContext<'a>,
154 root_children: usize,
155 ) -> Result<TypeTreeParseOutput> {
156 let mut out = TypeTreeParseOutput::default();
157
158 if let Some(root) = self.tree.nodes.first() {
159 for child in root.children.iter().take(root_children) {
160 if child.name.is_empty() {
161 continue;
162 }
163 match self.parse_value_by_type_ctx(reader, child, &mut ctx) {
164 Ok(value) => {
165 out.properties.insert(child.name.clone(), value);
166 }
167 Err(e) => {
168 if reader.remaining() == 0 {
169 break;
170 }
171 match ctx.options.mode {
172 TypeTreeParseMode::Strict => return Err(e),
173 TypeTreeParseMode::Lenient => out.warnings.push(TypeTreeParseWarning {
174 field: child.name.clone(),
175 error: e.to_string(),
176 }),
177 }
178 }
179 }
180 }
181 }
182
183 Ok(out)
184 }
185
186 pub fn scan_pptrs(&self, reader: &mut BinaryReader) -> Result<PPtrScanResult> {
189 self.scan_pptrs_with_ref_types(reader, None)
190 }
191
192 fn scan_value(
193 &self,
194 reader: &mut BinaryReader,
195 node: &TypeTreeNode,
196 out: &mut PPtrScanResult,
197 ) -> Result<()> {
198 let mut ctx = TypeTreeScanContext {
199 ref_types: None,
200 has_managed_registry: false,
201 };
202 self.scan_value_ctx(reader, node, out, &mut ctx)
203 }
204
205 pub fn scan_pptrs_with_ref_types(
208 &self,
209 reader: &mut BinaryReader,
210 ref_types: Option<&[SerializedType]>,
211 ) -> Result<PPtrScanResult> {
212 let mut out = PPtrScanResult::default();
213 let mut ctx = TypeTreeScanContext {
214 ref_types,
215 has_managed_registry: false,
216 };
217 if let Some(root) = self.tree.nodes.first() {
218 for child in &root.children {
219 self.scan_value_ctx(reader, child, &mut out, &mut ctx)?;
220 }
221 }
222 Ok(out)
223 }
224
225 fn scan_value_ctx(
226 &self,
227 reader: &mut BinaryReader,
228 node: &TypeTreeNode,
229 out: &mut PPtrScanResult,
230 ctx: &mut TypeTreeScanContext<'_>,
231 ) -> Result<()> {
232 if !node.children.is_empty() && node.children.iter().any(|c| c.type_name == "Array") {
234 self.scan_array(reader, node, out, ctx)?;
235 if node.is_aligned() {
236 reader.align_to(4)?;
237 }
238 return Ok(());
239 }
240
241 if node.type_name == "ReferencedObject" && !node.children.is_empty() {
243 let mut class: Option<String> = None;
244 let mut ns: Option<String> = None;
245 let mut asm: Option<String> = None;
246
247 for child in &node.children {
248 if child.type_name == "ManagedReferencesRegistry" {
249 if ctx.has_managed_registry {
250 self.scan_value_ctx(reader, child, out, ctx)?;
251 } else {
252 ctx.has_managed_registry = true;
253 self.scan_value_ctx(reader, child, out, ctx)?;
254 }
255 continue;
256 }
257
258 if child.name == "type" && !child.children.is_empty() {
259 for field in &child.children {
260 if field.type_name == "string"
261 && (field.name == "class" || field.name == "m_ClassName")
262 {
263 class = Some(reader.read_aligned_string()?);
264 continue;
265 }
266 if field.type_name == "string"
267 && (field.name == "ns" || field.name == "m_NameSpace")
268 {
269 ns = Some(reader.read_aligned_string()?);
270 continue;
271 }
272 if field.type_name == "string"
273 && (field.name == "asm" || field.name == "m_AssemblyName")
274 {
275 asm = Some(reader.read_aligned_string()?);
276 continue;
277 }
278 self.scan_value_ctx(reader, field, out, ctx)?;
279 }
280 if child.is_aligned() {
281 reader.align_to(4)?;
282 }
283 continue;
284 }
285
286 if child.type_name == "ReferencedObjectData" {
287 if let (Some(class), Some(ns), Some(asm), Some(ref_types)) =
288 (class.as_ref(), ns.as_ref(), asm.as_ref(), ctx.ref_types)
289 && let Some(tree) = resolve_ref_type_tree_triplet(class, ns, asm, ref_types)
290 && let Some(root) = tree.nodes.first()
291 {
292 for field in &root.children {
293 self.scan_value_ctx(reader, field, out, ctx)?;
294 }
295 if child.is_aligned() {
296 reader.align_to(4)?;
297 }
298 continue;
299 }
300
301 self.scan_value_ctx(reader, child, out, ctx)?;
303 continue;
304 }
305
306 self.scan_value_ctx(reader, child, out, ctx)?;
307 }
308
309 if node.is_aligned() {
310 reader.align_to(4)?;
311 }
312 return Ok(());
313 }
314
315 let is_pptr = node.type_name == "PPtr" || node.type_name.starts_with("PPtr<");
317 if is_pptr && !node.children.is_empty() {
318 let mut file_id: Option<i32> = None;
319 let mut path_id: Option<i64> = None;
320
321 for child in &node.children {
322 if child.name.eq_ignore_ascii_case("fileID")
323 || child.name.eq_ignore_ascii_case("m_FileID")
324 {
325 let v = self.scan_read_i32_like(reader, child)?;
327 file_id = Some(v);
328 } else if child.name.eq_ignore_ascii_case("pathID")
329 || child.name.eq_ignore_ascii_case("m_PathID")
330 {
331 let v = self.scan_read_i64_like(reader, child)?;
333 path_id = Some(v);
334 } else {
335 self.scan_value_ctx(reader, child, out, ctx)?;
336 }
337 }
338
339 if let (Some(file_id), Some(path_id)) = (file_id, path_id)
340 && path_id != 0
341 {
342 if file_id == 0 {
343 out.internal.push(path_id);
344 } else {
345 out.external.push((file_id, path_id));
346 }
347 }
348
349 if node.is_aligned() {
350 reader.align_to(4)?;
351 }
352 return Ok(());
353 }
354
355 match node.type_name.as_str() {
356 "SInt8" | "char" | "UInt8" => {
357 let _ = reader.read_u8()?;
358 }
359 "bool" => {
360 let _ = reader.read_u8()?;
361 }
362 "SInt16" | "short" => {
363 let _ = reader.read_i16()?;
364 }
365 "UInt16" | "unsigned short" => {
366 let _ = reader.read_u16()?;
367 }
368 "SInt32" | "int" => {
369 let _ = reader.read_i32()?;
370 }
371 "UInt32" | "unsigned int" | "Type*" => {
372 let _ = reader.read_u32()?;
373 }
374 "SInt64" | "long long" => {
375 let _ = reader.read_i64()?;
376 }
377 "UInt64" | "unsigned long long" | "FileSize" => {
378 let _ = reader.read_u64()?;
379 }
380 "float" => {
381 let _ = reader.read_f32()?;
382 }
383 "double" => {
384 let _ = reader.read_f64()?;
385 }
386 "string" => {
387 let len = reader.read_i32()?;
388 if len < 0 {
389 return Err(BinaryError::invalid_data(format!(
390 "Negative string length: {}",
391 len
392 )));
393 }
394 let len: usize = len as usize;
395 if len > BinaryReader::DEFAULT_MAX_STRING_LEN {
396 return Err(BinaryError::invalid_data(format!(
397 "String length {} exceeds limit {}",
398 len,
399 BinaryReader::DEFAULT_MAX_STRING_LEN
400 )));
401 }
402 reader.skip_bytes(len)?;
403 reader.align_to(4)?;
404 }
405 "TypelessData" => {
406 let length = reader.read_i32()?;
407 if length < 0 {
408 return Err(BinaryError::invalid_data(format!(
409 "Negative TypelessData length: {}",
410 length
411 )));
412 }
413 let length: usize = length as usize;
414 if length > Self::MAX_TYPELESSDATA_LEN {
415 return Err(BinaryError::invalid_data(format!(
416 "TypelessData length {} exceeds limit {}",
417 length,
418 Self::MAX_TYPELESSDATA_LEN
419 )));
420 }
421 reader.skip_bytes(length)?;
422 }
423 _ => {
424 if !node.children.is_empty() {
425 for child in &node.children {
426 self.scan_value_ctx(reader, child, out, ctx)?;
427 }
428 } else if node.byte_size > 0 {
429 reader.skip_bytes(node.byte_size as usize)?;
430 }
431 }
432 }
433
434 if node.is_aligned() {
435 reader.align_to(4)?;
436 }
437 Ok(())
438 }
439
440 fn scan_read_i32_like(&self, reader: &mut BinaryReader, node: &TypeTreeNode) -> Result<i32> {
441 let v = match node.type_name.as_str() {
442 "SInt32" | "int" => reader.read_i32()?,
443 "UInt32" | "unsigned int" | "Type*" => reader.read_u32()? as i32,
444 "SInt16" | "short" => reader.read_i16()? as i32,
445 "UInt16" | "unsigned short" => reader.read_u16()? as i32,
446 "SInt8" => reader.read_i8()? as i32,
447 "char" => reader.read_u8()? as i32,
448 "UInt8" => reader.read_u8()? as i32,
449 other => {
450 return Err(BinaryError::invalid_data(format!(
451 "Unsupported fileID type: {}",
452 other
453 )));
454 }
455 };
456 if node.is_aligned() {
457 reader.align_to(4)?;
458 }
459 Ok(v)
460 }
461
462 fn scan_read_i64_like(&self, reader: &mut BinaryReader, node: &TypeTreeNode) -> Result<i64> {
463 let v = match node.type_name.as_str() {
464 "SInt64" | "long long" => reader.read_i64()?,
465 "UInt64" | "unsigned long long" | "FileSize" => reader.read_u64()? as i64,
466 "SInt32" | "int" => reader.read_i32()? as i64,
467 "UInt32" | "unsigned int" | "Type*" => reader.read_u32()? as i64,
468 other => {
469 return Err(BinaryError::invalid_data(format!(
470 "Unsupported pathID type: {}",
471 other
472 )));
473 }
474 };
475 if node.is_aligned() {
476 reader.align_to(4)?;
477 }
478 Ok(v)
479 }
480
481 fn scan_array(
482 &self,
483 reader: &mut BinaryReader,
484 node: &TypeTreeNode,
485 out: &mut PPtrScanResult,
486 ctx: &mut TypeTreeScanContext<'_>,
487 ) -> Result<()> {
488 let array_node = node
489 .children
490 .iter()
491 .find(|child| child.type_name == "Array")
492 .ok_or_else(|| BinaryError::invalid_data("Array node not found in array type"))?;
493
494 let size_i32 = reader.read_i32()?;
495 if size_i32 < 0 {
496 return Err(BinaryError::invalid_data(format!(
497 "Negative array size: {}",
498 size_i32
499 )));
500 }
501 if let Some(size_node) = array_node.children.first()
502 && size_node.is_aligned()
503 {
504 reader.align_to(4)?;
505 }
506 let size = size_i32 as usize;
507 if size > Self::MAX_ARRAY_LEN {
508 return Err(BinaryError::invalid_data(format!(
509 "Array size too large: {}",
510 size
511 )));
512 }
513
514 let element_node = array_node
515 .children
516 .get(1)
517 .ok_or_else(|| BinaryError::invalid_data("Array element type not found"))?;
518
519 if element_node.children.is_empty() {
521 match element_node.type_name.as_str() {
522 "UInt8" | "char" | "SInt8" | "bool" => {
523 reader.skip_bytes(size)?;
524 if array_node.is_aligned() {
525 reader.align_to(4)?;
526 }
527 return Ok(());
528 }
529 "SInt16" | "short" | "UInt16" | "unsigned short" => {
530 reader.skip_bytes(size.checked_mul(2).ok_or_else(|| {
531 BinaryError::invalid_data("Array byte length overflow")
532 })?)?;
533 if array_node.is_aligned() {
534 reader.align_to(4)?;
535 }
536 return Ok(());
537 }
538 "SInt32" | "int" | "UInt32" | "unsigned int" | "Type*" | "float" => {
539 reader.skip_bytes(size.checked_mul(4).ok_or_else(|| {
540 BinaryError::invalid_data("Array byte length overflow")
541 })?)?;
542 if array_node.is_aligned() {
543 reader.align_to(4)?;
544 }
545 return Ok(());
546 }
547 "SInt64" | "long long" | "UInt64" | "unsigned long long" | "FileSize"
548 | "double" => {
549 reader.skip_bytes(size.checked_mul(8).ok_or_else(|| {
550 BinaryError::invalid_data("Array byte length overflow")
551 })?)?;
552 if array_node.is_aligned() {
553 reader.align_to(4)?;
554 }
555 return Ok(());
556 }
557 _ => {}
558 }
559 }
560
561 for _ in 0..size {
562 self.scan_value_ctx(reader, element_node, out, ctx)?;
563 }
564 if array_node.is_aligned() {
565 reader.align_to(4)?;
566 }
567 Ok(())
568 }
569
570 fn parse_value_by_type_ctx(
572 &self,
573 reader: &mut BinaryReader,
574 node: &TypeTreeNode,
575 ctx: &mut TypeTreeParseContext<'a>,
576 ) -> Result<UnityValue> {
577 let value = match node.type_name.as_str() {
578 "SInt8" => {
580 let val = reader.read_i8()?;
581 UnityValue::Integer(val as i64)
582 }
583 "char" => {
584 let val = reader.read_u8()?;
585 UnityValue::Integer(val as i64)
586 }
587 "SInt16" | "short" => {
588 let val = reader.read_i16()?;
589 UnityValue::Integer(val as i64)
590 }
591 "SInt32" | "int" => {
592 let val = reader.read_i32()?;
593 UnityValue::Integer(val as i64)
594 }
595 "SInt64" | "long long" => {
596 let val = reader.read_i64()?;
597 UnityValue::Integer(val)
598 }
599
600 "UInt8" => {
602 let val = reader.read_u8()?;
603 UnityValue::Integer(val as i64)
604 }
605 "UInt16" | "unsigned short" => {
606 let val = reader.read_u16()?;
607 UnityValue::Integer(val as i64)
608 }
609 "UInt32" | "unsigned int" | "Type*" => {
610 let val = reader.read_u32()?;
611 UnityValue::Integer(val as i64)
612 }
613 "UInt64" | "unsigned long long" | "FileSize" => {
614 let val = reader.read_u64()?;
615 UnityValue::Integer(val as i64)
616 }
617
618 "float" => {
620 let val = reader.read_f32()?;
621 UnityValue::Float(val as f64)
622 }
623 "double" => {
624 let val = reader.read_f64()?;
625 UnityValue::Float(val)
626 }
627
628 "bool" => {
630 let val = reader.read_u8()? != 0;
631 UnityValue::Bool(val)
632 }
633
634 "string" => UnityValue::String(reader.read_aligned_string()?),
636
637 "TypelessData" => {
639 let length = reader.read_i32()?;
640 if length < 0 {
641 return Err(BinaryError::invalid_data(format!(
642 "Negative TypelessData length: {}",
643 length
644 )));
645 }
646 let length: usize = length as usize;
647 if length > Self::MAX_TYPELESSDATA_LEN {
648 return Err(BinaryError::invalid_data(format!(
649 "TypelessData length {} exceeds limit {}",
650 length,
651 Self::MAX_TYPELESSDATA_LEN
652 )));
653 }
654 let bytes = reader.read_bytes(length)?;
655 UnityValue::Bytes(bytes)
656 }
657
658 _ if !node.children.is_empty()
660 && node.children.iter().any(|c| c.type_name == "Array") =>
661 {
662 self.parse_array(reader, node, ctx)?
663 }
664
665 "pair" if node.children.len() == 2 => {
667 let first = self.parse_value_by_type_ctx(reader, &node.children[0], ctx)?;
668 let second = self.parse_value_by_type_ctx(reader, &node.children[1], ctx)?;
669 UnityValue::Array(vec![first, second])
670 }
671
672 "ReferencedObject" => {
674 let mut nested = IndexMap::new();
675 for child in &node.children {
676 if child.type_name == "ManagedReferencesRegistry" {
677 ctx.has_managed_registry = true;
680 let mut dummy = PPtrScanResult::default();
681 self.scan_value(reader, child, &mut dummy)?;
682 continue;
683 }
684
685 if child.type_name == "ReferencedObjectData" {
686 let resolved = ctx
687 .ref_types
688 .and_then(|r| resolve_ref_type_tree(&nested, r));
689 if let Some(tree) = resolved
690 && let Some(root) = tree.nodes.first()
691 {
692 let mut props = IndexMap::new();
693 for field in &root.children {
694 if field.name.is_empty() {
695 continue;
696 }
697 let v = self.parse_value_by_type_ctx(reader, field, ctx)?;
698 props.insert(field.name.clone(), v);
699 }
700 if !child.name.is_empty() {
701 nested.insert(child.name.clone(), UnityValue::Object(props));
702 }
703 continue;
704 }
705
706 if let Some((class, ns, asm)) = referenced_type_triplet(&nested) {
709 nested.insert(
710 "_referenced_type_unresolved".to_string(),
711 UnityValue::Bool(true),
712 );
713 nested.insert(
714 "_referenced_type_key".to_string(),
715 UnityValue::String(format!("{}|{}|{}", class, ns, asm)),
716 );
717 }
718
719 let v = self.parse_value_by_type_ctx(reader, child, ctx)?;
721 if !child.name.is_empty() {
722 nested.insert(child.name.clone(), v);
723 }
724 continue;
725 }
726
727 if !child.name.is_empty() {
728 let v = self.parse_value_by_type_ctx(reader, child, ctx)?;
729 nested.insert(child.name.clone(), v);
730 } else {
731 let _ = self.parse_value_by_type_ctx(reader, child, ctx)?;
732 }
733 }
734 UnityValue::Object(nested)
735 }
736
737 "ManagedReferencesRegistry" => {
739 ctx.has_managed_registry = true;
743 let mut dummy = PPtrScanResult::default();
744 self.scan_value(reader, node, &mut dummy)?;
745 UnityValue::Null
746 }
747 _ => {
748 if !node.children.is_empty() {
749 let mut nested_props = IndexMap::new();
750 for child in &node.children {
751 if child.type_name == "ManagedReferencesRegistry" {
752 if ctx.has_managed_registry {
755 let mut dummy = PPtrScanResult::default();
756 self.scan_value(reader, child, &mut dummy)?;
757 continue;
758 }
759 ctx.has_managed_registry = true;
760 let mut dummy = PPtrScanResult::default();
761 self.scan_value(reader, child, &mut dummy)?;
762 continue;
763 }
764 if !child.name.is_empty() {
765 let child_value = self.parse_value_by_type_ctx(reader, child, ctx)?;
766 nested_props.insert(child.name.clone(), child_value);
767 } else {
768 let _ = self.parse_value_by_type_ctx(reader, child, ctx)?;
769 }
770 }
771 UnityValue::Object(nested_props)
772 } else {
773 if node.byte_size > 0 {
775 let _data = reader.read_bytes(node.byte_size as usize)?;
776 UnityValue::Null
777 } else {
778 UnityValue::Null
779 }
780 }
781 }
782 };
783
784 if node.is_aligned() {
788 reader.align_to(4)?;
789 }
790
791 Ok(value)
792 }
793
794 fn parse_array(
796 &self,
797 reader: &mut BinaryReader,
798 node: &TypeTreeNode,
799 ctx: &mut TypeTreeParseContext<'a>,
800 ) -> Result<UnityValue> {
801 let array_node = node
803 .children
804 .iter()
805 .find(|child| child.type_name == "Array")
806 .ok_or_else(|| BinaryError::invalid_data("Array node not found in array type"))?;
807
808 let size_i32 = reader.read_i32()?;
810 if size_i32 < 0 {
811 return Err(BinaryError::invalid_data(format!(
812 "Negative array size: {}",
813 size_i32
814 )));
815 }
816 if let Some(size_node) = array_node.children.first()
817 && size_node.is_aligned()
818 {
819 reader.align_to(4)?;
820 }
821 let size = size_i32 as usize;
822 if size > Self::MAX_ARRAY_LEN {
823 return Err(BinaryError::invalid_data(format!(
824 "Array size too large: {}",
825 size
826 )));
827 }
828
829 let mut elements = Vec::with_capacity(size);
830
831 let element_node = array_node
833 .children
834 .get(1)
835 .ok_or_else(|| BinaryError::invalid_data("Array element type not found"))?;
836
837 if element_node.children.is_empty() {
839 let byte_order = reader.byte_order();
840 match element_node.type_name.as_str() {
841 "UInt8" | "char" => {
842 let bytes = reader.read_bytes(size)?;
843 if array_node.is_aligned() {
844 reader.align_to(4)?;
845 }
846 return Ok(UnityValue::Bytes(bytes));
847 }
848 "SInt8" => {
849 let bytes = reader.read_bytes(size)?;
850 if array_node.is_aligned() {
852 reader.align_to(4)?;
853 }
854 return Ok(UnityValue::Bytes(bytes));
855 }
856 "bool" => {
857 let bytes = reader.read_bytes(size)?;
858 let out = UnityValue::Array(
859 bytes
860 .into_iter()
861 .map(|b| UnityValue::Bool(b != 0))
862 .collect(),
863 );
864 if array_node.is_aligned() {
865 reader.align_to(4)?;
866 }
867 return Ok(out);
868 }
869 "SInt16" | "short" => {
870 let byte_len = size
871 .checked_mul(2)
872 .ok_or_else(|| BinaryError::invalid_data("Array byte length overflow"))?;
873 let bytes = reader.read_bytes(byte_len)?;
874 let mut out = Vec::with_capacity(size);
875 for chunk in bytes.chunks_exact(2) {
876 let raw: [u8; 2] = chunk.try_into().expect("chunks_exact size");
877 let v = match byte_order {
878 ByteOrder::Big => i16::from_be_bytes(raw),
879 ByteOrder::Little => i16::from_le_bytes(raw),
880 };
881 out.push(UnityValue::Integer(v as i64));
882 }
883 if array_node.is_aligned() {
884 reader.align_to(4)?;
885 }
886 return Ok(UnityValue::Array(out));
887 }
888 "UInt16" | "unsigned short" => {
889 let byte_len = size
890 .checked_mul(2)
891 .ok_or_else(|| BinaryError::invalid_data("Array byte length overflow"))?;
892 let bytes = reader.read_bytes(byte_len)?;
893 let mut out = Vec::with_capacity(size);
894 for chunk in bytes.chunks_exact(2) {
895 let raw: [u8; 2] = chunk.try_into().expect("chunks_exact size");
896 let v = match byte_order {
897 ByteOrder::Big => u16::from_be_bytes(raw),
898 ByteOrder::Little => u16::from_le_bytes(raw),
899 };
900 out.push(UnityValue::Integer(v as i64));
901 }
902 if array_node.is_aligned() {
903 reader.align_to(4)?;
904 }
905 return Ok(UnityValue::Array(out));
906 }
907 "SInt32" | "int" => {
908 let byte_len = size
909 .checked_mul(4)
910 .ok_or_else(|| BinaryError::invalid_data("Array byte length overflow"))?;
911 let bytes = reader.read_bytes(byte_len)?;
912 let mut out = Vec::with_capacity(size);
913 for chunk in bytes.chunks_exact(4) {
914 let raw: [u8; 4] = chunk.try_into().expect("chunks_exact size");
915 let v = match byte_order {
916 ByteOrder::Big => i32::from_be_bytes(raw),
917 ByteOrder::Little => i32::from_le_bytes(raw),
918 };
919 out.push(UnityValue::Integer(v as i64));
920 }
921 if array_node.is_aligned() {
922 reader.align_to(4)?;
923 }
924 return Ok(UnityValue::Array(out));
925 }
926 "UInt32" | "unsigned int" | "Type*" => {
927 let byte_len = size
928 .checked_mul(4)
929 .ok_or_else(|| BinaryError::invalid_data("Array byte length overflow"))?;
930 let bytes = reader.read_bytes(byte_len)?;
931 let mut out = Vec::with_capacity(size);
932 for chunk in bytes.chunks_exact(4) {
933 let raw: [u8; 4] = chunk.try_into().expect("chunks_exact size");
934 let v = match byte_order {
935 ByteOrder::Big => u32::from_be_bytes(raw),
936 ByteOrder::Little => u32::from_le_bytes(raw),
937 };
938 out.push(UnityValue::Integer(v as i64));
939 }
940 if array_node.is_aligned() {
941 reader.align_to(4)?;
942 }
943 return Ok(UnityValue::Array(out));
944 }
945 "SInt64" | "long long" => {
946 let byte_len = size
947 .checked_mul(8)
948 .ok_or_else(|| BinaryError::invalid_data("Array byte length overflow"))?;
949 let bytes = reader.read_bytes(byte_len)?;
950 let mut out = Vec::with_capacity(size);
951 for chunk in bytes.chunks_exact(8) {
952 let raw: [u8; 8] = chunk.try_into().expect("chunks_exact size");
953 let v = match byte_order {
954 ByteOrder::Big => i64::from_be_bytes(raw),
955 ByteOrder::Little => i64::from_le_bytes(raw),
956 };
957 out.push(UnityValue::Integer(v));
958 }
959 if array_node.is_aligned() {
960 reader.align_to(4)?;
961 }
962 return Ok(UnityValue::Array(out));
963 }
964 "UInt64" | "unsigned long long" | "FileSize" => {
965 let byte_len = size
966 .checked_mul(8)
967 .ok_or_else(|| BinaryError::invalid_data("Array byte length overflow"))?;
968 let bytes = reader.read_bytes(byte_len)?;
969 let mut out = Vec::with_capacity(size);
970 for chunk in bytes.chunks_exact(8) {
971 let raw: [u8; 8] = chunk.try_into().expect("chunks_exact size");
972 let v = match byte_order {
973 ByteOrder::Big => u64::from_be_bytes(raw),
974 ByteOrder::Little => u64::from_le_bytes(raw),
975 };
976 out.push(UnityValue::Integer(v as i64));
977 }
978 if array_node.is_aligned() {
979 reader.align_to(4)?;
980 }
981 return Ok(UnityValue::Array(out));
982 }
983 "float" => {
984 let byte_len = size
985 .checked_mul(4)
986 .ok_or_else(|| BinaryError::invalid_data("Array byte length overflow"))?;
987 let bytes = reader.read_bytes(byte_len)?;
988 let mut out = Vec::with_capacity(size);
989 for chunk in bytes.chunks_exact(4) {
990 let raw: [u8; 4] = chunk.try_into().expect("chunks_exact size");
991 let bits = match byte_order {
992 ByteOrder::Big => u32::from_be_bytes(raw),
993 ByteOrder::Little => u32::from_le_bytes(raw),
994 };
995 out.push(UnityValue::Float(f32::from_bits(bits) as f64));
996 }
997 if array_node.is_aligned() {
998 reader.align_to(4)?;
999 }
1000 return Ok(UnityValue::Array(out));
1001 }
1002 "double" => {
1003 let byte_len = size
1004 .checked_mul(8)
1005 .ok_or_else(|| BinaryError::invalid_data("Array byte length overflow"))?;
1006 let bytes = reader.read_bytes(byte_len)?;
1007 let mut out = Vec::with_capacity(size);
1008 for chunk in bytes.chunks_exact(8) {
1009 let raw: [u8; 8] = chunk.try_into().expect("chunks_exact size");
1010 let bits = match byte_order {
1011 ByteOrder::Big => u64::from_be_bytes(raw),
1012 ByteOrder::Little => u64::from_le_bytes(raw),
1013 };
1014 out.push(UnityValue::Float(f64::from_bits(bits)));
1015 }
1016 if array_node.is_aligned() {
1017 reader.align_to(4)?;
1018 }
1019 return Ok(UnityValue::Array(out));
1020 }
1021 _ => {}
1022 }
1023 }
1024
1025 for _ in 0..size {
1026 let element = self.parse_value_by_type_ctx(reader, element_node, ctx)?;
1027 elements.push(element);
1028 }
1029 if array_node.is_aligned() {
1030 reader.align_to(4)?;
1031 }
1032
1033 Ok(UnityValue::Array(elements))
1034 }
1035
1036 pub fn serialize_object(&self, data: &IndexMap<String, UnityValue>) -> Result<Vec<u8>> {
1038 let mut buffer = Vec::new();
1039
1040 if let Some(root) = self.tree.nodes.first() {
1041 for child in &root.children {
1042 if child.name.is_empty() {
1043 continue;
1044 }
1045 if let Some(value) = data.get(&child.name) {
1046 self.serialize_value(&mut buffer, value, child)?;
1047 }
1048 }
1049 }
1050
1051 Ok(buffer)
1052 }
1053
1054 fn serialize_value(
1056 &self,
1057 buffer: &mut Vec<u8>,
1058 value: &UnityValue,
1059 node: &TypeTreeNode,
1060 ) -> Result<()> {
1061 match node.type_name.as_str() {
1062 "SInt8" | "char" => {
1063 if let UnityValue::Integer(val) = value {
1064 buffer.push(*val as u8);
1065 self.align_buffer(buffer, 4);
1066 }
1067 }
1068 "SInt16" | "short" => {
1069 if let UnityValue::Integer(val) = value {
1070 buffer.extend_from_slice(&(*val as i16).to_le_bytes());
1071 self.align_buffer(buffer, 4);
1072 }
1073 }
1074 "SInt32" | "int" => {
1075 if let UnityValue::Integer(val) = value {
1076 buffer.extend_from_slice(&(*val as i32).to_le_bytes());
1077 }
1078 }
1079 "SInt64" | "long long" => {
1080 if let UnityValue::Integer(val) = value {
1081 buffer.extend_from_slice(&val.to_le_bytes());
1082 }
1083 }
1084 "UInt8" => {
1085 if let UnityValue::Integer(val) = value {
1086 buffer.push(*val as u8);
1087 self.align_buffer(buffer, 4);
1088 }
1089 }
1090 "UInt16" | "unsigned short" => {
1091 if let UnityValue::Integer(val) = value {
1092 buffer.extend_from_slice(&(*val as u16).to_le_bytes());
1093 self.align_buffer(buffer, 4);
1094 }
1095 }
1096 "UInt32" | "unsigned int" | "Type*" => {
1097 if let UnityValue::Integer(val) = value {
1098 buffer.extend_from_slice(&(*val as u32).to_le_bytes());
1099 }
1100 }
1101 "UInt64" | "unsigned long long" | "FileSize" => {
1102 if let UnityValue::Integer(val) = value {
1103 buffer.extend_from_slice(&(*val as u64).to_le_bytes());
1104 }
1105 }
1106 "float" => {
1107 if let UnityValue::Float(val) = value {
1108 buffer.extend_from_slice(&(*val as f32).to_le_bytes());
1109 }
1110 }
1111 "double" => {
1112 if let UnityValue::Float(val) = value {
1113 buffer.extend_from_slice(&val.to_le_bytes());
1114 }
1115 }
1116 "bool" => {
1117 if let UnityValue::Bool(val) = value {
1118 buffer.push(if *val { 1 } else { 0 });
1119 self.align_buffer(buffer, 4);
1120 }
1121 }
1122 "string" => {
1123 if let UnityValue::String(val) = value {
1124 buffer.extend_from_slice(&(val.len() as u32).to_le_bytes());
1126 buffer.extend_from_slice(val.as_bytes());
1128 self.align_buffer(buffer, 4);
1129 }
1130 }
1131 _ if node.is_array() => {
1132 if let UnityValue::Array(elements) = value {
1133 buffer.extend_from_slice(&(elements.len() as i32).to_le_bytes());
1135
1136 if let Some(array_node) = node.children.iter().find(|c| c.type_name == "Array")
1138 && let Some(element_node) = array_node.children.get(1)
1139 {
1140 for element in elements {
1141 self.serialize_value(buffer, element, element_node)?;
1142 }
1143 }
1144 }
1145 }
1146 _ => {
1147 if let UnityValue::Object(obj) = value {
1149 for child in &node.children {
1150 if child.name.is_empty() {
1151 continue;
1152 }
1153 if let Some(child_value) = obj.get(&child.name) {
1154 self.serialize_value(buffer, child_value, child)?;
1155 }
1156 }
1157 }
1158 }
1159 }
1160
1161 Ok(())
1162 }
1163
1164 fn align_buffer(&self, buffer: &mut Vec<u8>, alignment: usize) {
1166 let remainder = buffer.len() % alignment;
1167 if remainder != 0 {
1168 let padding = alignment - remainder;
1169 buffer.resize(buffer.len() + padding, 0);
1170 }
1171 }
1172
1173 pub fn tree(&self) -> &TypeTree {
1175 self.tree
1176 }
1177
1178 pub fn estimate_size(&self, data: &IndexMap<String, UnityValue>) -> usize {
1180 let mut size = 0;
1181
1182 if let Some(root) = self.tree.nodes.first() {
1183 for child in &root.children {
1184 if child.name.is_empty() {
1185 continue;
1186 }
1187 if let Some(value) = data.get(&child.name) {
1188 size += Self::estimate_value_size(value, child);
1189 }
1190 }
1191 }
1192
1193 size
1194 }
1195
1196 fn estimate_value_size(value: &UnityValue, node: &TypeTreeNode) -> usize {
1198 match node.type_name.as_str() {
1199 "SInt8" | "UInt8" | "char" | "bool" => 4, "SInt16" | "UInt16" | "short" | "unsigned short" => 4, "SInt32" | "UInt32" | "int" | "unsigned int" | "float" | "Type*" => 4,
1202 "SInt64" | "UInt64" | "long long" | "unsigned long long" | "double" | "FileSize" => 8,
1203 "string" => {
1204 if let UnityValue::String(s) = value {
1205 4 + s.len() + (4 - (s.len() % 4)) % 4 } else {
1207 4
1208 }
1209 }
1210 _ if node.is_array() => {
1211 if let UnityValue::Array(elements) = value {
1212 let mut size = 4; if let Some(array_node) = node.children.iter().find(|c| c.type_name == "Array")
1214 && let Some(element_node) = array_node.children.get(1)
1215 {
1216 for element in elements {
1217 size += Self::estimate_value_size(element, element_node);
1218 }
1219 }
1220 size
1221 } else {
1222 4
1223 }
1224 }
1225 _ => {
1226 if let UnityValue::Object(obj) = value {
1228 let mut size = 0;
1229 for child in &node.children {
1230 if child.name.is_empty() {
1231 continue;
1232 }
1233 if let Some(child_value) = obj.get(&child.name) {
1234 size += Self::estimate_value_size(child_value, child);
1235 }
1236 }
1237 size
1238 } else {
1239 node.byte_size.max(0) as usize
1240 }
1241 }
1242 }
1243 }
1244}
1245
1246fn resolve_ref_type_tree<'a>(
1247 referenced_object: &IndexMap<String, UnityValue>,
1248 ref_types: &'a [SerializedType],
1249) -> Option<&'a TypeTree> {
1250 let type_v = referenced_object.get("type")?;
1251 let type_obj = type_v.as_object()?;
1252
1253 fn get_str_ci(map: &IndexMap<String, UnityValue>, keys: &[&str]) -> Option<String> {
1254 for key in keys {
1255 for (k, v) in map.iter() {
1256 if k.eq_ignore_ascii_case(key)
1257 && let UnityValue::String(s) = v
1258 {
1259 return Some(s.clone());
1260 }
1261 }
1262 }
1263 None
1264 }
1265
1266 let class = get_str_ci(type_obj, &["class", "m_ClassName"])?;
1267 if class.is_empty() {
1268 return None;
1269 }
1270 let ns = get_str_ci(type_obj, &["ns", "m_NameSpace"]).unwrap_or_default();
1271 let asm = get_str_ci(type_obj, &["asm", "m_AssemblyName"]).unwrap_or_default();
1272
1273 ref_types.iter().find_map(|t| {
1274 if !t.class_name.is_empty()
1275 && t.class_name == class
1276 && t.namespace == ns
1277 && t.assembly_name == asm
1278 && !t.type_tree.is_empty()
1279 {
1280 Some(&t.type_tree)
1281 } else {
1282 None
1283 }
1284 })
1285}
1286
1287fn resolve_ref_type_tree_triplet<'a>(
1288 class: &str,
1289 ns: &str,
1290 asm: &str,
1291 ref_types: &'a [SerializedType],
1292) -> Option<&'a TypeTree> {
1293 if class.is_empty() {
1294 return None;
1295 }
1296 ref_types.iter().find_map(|t| {
1297 if !t.class_name.is_empty()
1298 && t.class_name == class
1299 && t.namespace == ns
1300 && t.assembly_name == asm
1301 && !t.type_tree.is_empty()
1302 {
1303 Some(&t.type_tree)
1304 } else {
1305 None
1306 }
1307 })
1308}
1309
1310fn referenced_type_triplet(
1311 referenced_object: &IndexMap<String, UnityValue>,
1312) -> Option<(String, String, String)> {
1313 let type_v = referenced_object.get("type")?;
1314 let type_obj = type_v.as_object()?;
1315 let class = type_obj.get("class")?.as_str()?.to_string();
1316 let ns = type_obj
1317 .get("ns")
1318 .and_then(|v| v.as_str())
1319 .unwrap_or_default()
1320 .to_string();
1321 let asm = type_obj
1322 .get("asm")
1323 .and_then(|v| v.as_str())
1324 .unwrap_or_default()
1325 .to_string();
1326 Some((class, ns, asm))
1327}
1328
1329#[cfg(test)]
1330mod tests {
1331 use super::*;
1332
1333 fn node(type_name: &str, name: &str) -> TypeTreeNode {
1334 let mut n = TypeTreeNode::new();
1335 n.type_name = type_name.to_string();
1336 n.name = name.to_string();
1337 n
1338 }
1339
1340 #[test]
1341 fn test_serializer_creation() {
1342 let tree = TypeTree::new();
1343 let serializer = TypeTreeSerializer::new(&tree);
1344 assert!(serializer.tree().is_empty());
1345 }
1346
1347 #[test]
1348 fn test_buffer_alignment() {
1349 let tree = TypeTree::new();
1350 let serializer = TypeTreeSerializer::new(&tree);
1351
1352 let mut buffer = vec![1, 2, 3]; serializer.align_buffer(&mut buffer, 4);
1354 assert_eq!(buffer.len(), 4); assert_eq!(buffer[3], 0); }
1357
1358 #[test]
1359 fn typetree_array_alignment_honors_array_node_flag() {
1360 let mut tree = TypeTree::new();
1361
1362 let mut root = node("TestObject", "TestObject");
1363
1364 let mut vec_node = node("vector", "m_Data");
1365 let mut array_node = node("Array", "Array");
1366 array_node.meta_flags = 0x4000; array_node.children = vec![node("int", "size"), node("UInt8", "data")];
1368 vec_node.children = vec![array_node];
1369
1370 let next_node = node("int", "m_Next");
1371
1372 root.children = vec![vec_node, next_node];
1373 tree.nodes.push(root);
1374
1375 let serializer = TypeTreeSerializer::new(&tree);
1376
1377 let mut bytes = Vec::new();
1378 bytes.extend_from_slice(&1i32.to_le_bytes()); bytes.push(0xAA); bytes.extend_from_slice(&[0u8; 3]); bytes.extend_from_slice(&0x11223344i32.to_le_bytes()); let mut reader = BinaryReader::new(&bytes, ByteOrder::Little);
1384 let props = serializer.parse_object(&mut reader).unwrap();
1385 assert_eq!(
1386 props.get("m_Next").and_then(|v| v.as_i64()),
1387 Some(0x11223344)
1388 );
1389 }
1390
1391 #[test]
1392 fn typetree_scan_pptrs_honors_array_node_alignment() {
1393 let mut tree = TypeTree::new();
1394
1395 let mut root = node("TestObject", "TestObject");
1396
1397 let mut vec_node = node("vector", "m_Data");
1398 let mut array_node = node("Array", "Array");
1399 array_node.meta_flags = 0x4000; array_node.children = vec![node("int", "size"), node("UInt8", "data")];
1401 vec_node.children = vec![array_node];
1402
1403 let next_node = node("int", "m_Next");
1404 root.children = vec![vec_node, next_node];
1405 tree.nodes.push(root);
1406
1407 let serializer = TypeTreeSerializer::new(&tree);
1408
1409 let mut bytes = Vec::new();
1410 bytes.extend_from_slice(&1i32.to_le_bytes());
1411 bytes.push(0xAA);
1412 bytes.extend_from_slice(&[0u8; 3]);
1413 bytes.extend_from_slice(&0x11223344i32.to_le_bytes());
1414
1415 let mut reader = BinaryReader::new(&bytes, ByteOrder::Little);
1416 let _ = serializer.scan_pptrs(&mut reader).unwrap();
1417 assert_eq!(reader.remaining(), 0);
1418 }
1419
1420 #[test]
1421 fn typetree_char_reads_as_unsigned_byte() {
1422 let mut tree = TypeTree::new();
1423 let mut root = node("TestObject", "TestObject");
1424
1425 let mut char_node = node("char", "m_Char");
1426 char_node.meta_flags = 0x4000;
1427 let next_node = node("int", "m_Next");
1428
1429 root.children = vec![char_node, next_node];
1430 tree.nodes.push(root);
1431
1432 let serializer = TypeTreeSerializer::new(&tree);
1433
1434 let mut bytes = Vec::new();
1435 bytes.push(0xFF);
1436 bytes.extend_from_slice(&[0u8; 3]);
1437 bytes.extend_from_slice(&0x11223344i32.to_le_bytes());
1438
1439 let mut reader = BinaryReader::new(&bytes, ByteOrder::Little);
1440 let props = serializer.parse_object(&mut reader).unwrap();
1441 assert_eq!(props.get("m_Char").and_then(|v| v.as_i64()), Some(255));
1442 assert_eq!(
1443 props.get("m_Next").and_then(|v| v.as_i64()),
1444 Some(0x11223344)
1445 );
1446 }
1447
1448 #[test]
1449 fn typetree_byte_arrays_are_emitted_as_bytes() {
1450 let mut tree = TypeTree::new();
1451 let mut root = node("TestObject", "TestObject");
1452
1453 let mut vec_u8 = node("vector", "m_UInt8");
1454 let mut arr_u8 = node("Array", "Array");
1455 arr_u8.children = vec![node("int", "size"), node("UInt8", "data")];
1456 vec_u8.children = vec![arr_u8];
1457
1458 let mut vec_char = node("vector", "m_Char");
1459 let mut arr_char = node("Array", "Array");
1460 arr_char.children = vec![node("int", "size"), node("char", "data")];
1461 vec_char.children = vec![arr_char];
1462
1463 root.children = vec![vec_u8, vec_char];
1464 tree.nodes.push(root);
1465
1466 let serializer = TypeTreeSerializer::new(&tree);
1467
1468 let mut bytes = Vec::new();
1469 bytes.extend_from_slice(&2i32.to_le_bytes());
1470 bytes.extend_from_slice(&[0xAB, 0xCD]);
1471 bytes.extend_from_slice(&3i32.to_le_bytes());
1472 bytes.extend_from_slice(&[0x41, 0x80, 0xFF]);
1473
1474 let mut reader = BinaryReader::new(&bytes, ByteOrder::Little);
1475 let props = serializer.parse_object(&mut reader).unwrap();
1476
1477 assert_eq!(
1478 props.get("m_UInt8").and_then(|v| v.as_bytes()),
1479 Some(&[0xAB, 0xCD][..])
1480 );
1481 assert_eq!(
1482 props.get("m_Char").and_then(|v| v.as_bytes()),
1483 Some(&[0x41, 0x80, 0xFF][..])
1484 );
1485 }
1486}