1mod class_index_map;
2mod enum_access;
3mod map;
4pub mod parser;
5mod seq;
6
7use crate::{lib::*, tri};
8
9use self::class_index_map::BytesClassIndexMapDeserializer;
10use self::enum_access::EnumDeserializer;
11use self::map::MapDeserializer;
12use self::parser::{
13 BytesStream,
14 classnames::{ClassNames, classnames_section},
15 fixups::Fixups,
16 type_kind::{
17 array_meta, boolean, matrix3, matrix4, qstransform, quaternion, real, rotation, string,
18 transform, vector4,
19 },
20};
21use self::seq::SeqDeserializer;
22use super::hexdump::{self, to_hexdump_pos};
23use super::serde::{hkx_header::HkxHeader, section_header::SectionHeader};
24use crate::errors::{
25 de::{Error, Result},
26 readable::ReadableError,
27};
28use havok_serde::de::{self, Deserialize, ReadEnumSize, Visitor};
29use havok_types::*;
30use winnow::binary::Endianness;
31use winnow::error::{StrContext, StrContextValue};
32use winnow::{Parser, binary};
33
34#[derive(Debug, educe::Educe)]
37#[educe(Default)]
38pub struct BytesDeserializer<'de> {
39 input: &'de [u8],
41
42 current_position: usize,
44
45 #[educe(Default = Endianness::Little)]
47 endian: Endianness,
48 is_x86: bool,
53
54 classnames: ClassNames<'de>,
59
60 classnames_header: SectionHeader,
62
63 data_header: SectionHeader,
65 data_fixups: Fixups,
67
68 class_index: usize,
74
75 takable_class_index: Option<Pointer>,
84}
85
86impl<'de> BytesDeserializer<'de> {
87 pub fn from_bytes(input: &'de [u8]) -> Self {
89 Self {
90 input,
91 ..Default::default()
92 }
93 }
94}
95
96pub fn from_partial_bytes<'a, T>(bytes: &'a [u8]) -> Result<T>
106where
107 T: Deserialize<'a>,
108{
109 from_partial_bytes_with_opt(BytesDeserializer::from_bytes(bytes))
110}
111
112pub fn from_partial_bytes_with_opt<'a, T>(de: BytesDeserializer<'a>) -> Result<T>
122where
123 T: Deserialize<'a>,
124{
125 let mut de = de;
126 let t = tri!(T::deserialize(&mut de).map_err(|err| de.to_readable_err(err)));
127
128 if de.input[de.current_position..].is_empty() {
129 Ok(t)
130 } else {
131 Err(de.to_readable_err(Error::TrailingBytes))
132 }
133}
134
135pub fn from_bytes<'a, T>(bytes: &'a [u8]) -> Result<T>
140where
141 T: Deserialize<'a>,
142{
143 from_bytes_with_opt(BytesDeserializer::from_bytes(bytes))
144}
145
146pub fn from_bytes_with_opt<'a, T>(de: BytesDeserializer<'a>) -> Result<T>
151where
152 T: Deserialize<'a>,
153{
154 let mut de = de;
155
156 let header = tri!(
158 de.parse_peek(HkxHeader::parser())
159 .map_err(|err| de.to_readable_err(err))
160 );
161 de.current_position += 64; de.is_x86 = header.pointer_size == 4;
163 de.endian = header.endian();
164
165 tri!(
167 de.set_section_header_and_fixups(
168 header.contents_class_name_section_index,
169 header.contents_section_index,
170 header.section_count,
171 )
172 .map_err(|err| de.to_readable_err(err))
173 );
174
175 let classnames_abs = de.classnames_header.absolute_data_start as usize;
177 let data_abs = de.data_header.absolute_data_start as usize;
178 let classnames_section_range = classnames_abs..data_abs; de.classnames = tri!(
180 de.parse_range(classnames_section(de.endian, 0), classnames_section_range)
181 .map_err(|err| de.to_readable_err(err))
182 );
183
184 de.current_position = data_abs; T::deserialize(&mut de).map_err(|err| de.to_readable_err(err))
187}
188
189impl<'de> BytesDeserializer<'de> {
193 fn parse_peek<O, P>(&self, mut parser: P) -> Result<O>
197 where
198 P: Parser<BytesStream<'de>, O, winnow::error::ContextError>,
199 {
200 let (_, res) = parser
201 .parse_peek(&self.input[self.current_position..])
202 .map_err(|err| Error::ContextError { err })?;
203 Ok(res)
204 }
205
206 fn parse_range<O, P>(&self, mut parser: P, range: Range<usize>) -> Result<O>
210 where
211 P: Parser<BytesStream<'de>, O, winnow::error::ContextError>,
212 {
213 let (_, res) = parser
214 .parse_peek(&self.input[range])
215 .map_err(|err| Error::ContextError { err })?;
216 Ok(res)
217 }
218
219 #[cold]
224 fn to_readable_err(&self, err: Error) -> Error {
225 let input = hexdump::to_string(self.input);
226 let err_pos = to_hexdump_pos(self.current_position);
227 let readable = match err {
228 Error::ContextError { err } => ReadableError::from_context(err, input, err_pos),
229 Error::ReadableError { source } => source,
230 err => ReadableError::from_display(err, input, err_pos),
231 };
232 Error::ReadableError { source: readable }
233 }
234
235 fn set_section_header_and_fixups(
239 &mut self,
240 classnames_section_index: i32,
241 data_section_index: i32,
242 section_len: i32,
243 ) -> Result<()> {
244 for i in 0..section_len {
245 match i {
246 i if classnames_section_index == i => {
247 self.classnames_header =
248 tri!(self.parse_peek(SectionHeader::from_bytes(self.endian)));
249 #[cfg(feature = "tracing")]
250 tracing::debug!("classnames_header: {}", self.classnames_header);
251
252 }
255
256 i if data_section_index == i => {
257 let backup_pos = self.current_position;
259
260 let header = tri!(self.parse_peek(SectionHeader::from_bytes(self.endian)));
262
263 let fixups_start = header.absolute_data_start + header.local_fixups_offset;
265 self.current_position = fixups_start as usize;
266 self.data_fixups =
267 tri!(self.parse_peek(Fixups::from_section_header(&header, self.endian)));
268
269 #[cfg(feature = "tracing")]
270 tracing::debug!(
271 "data_header: {header},\ndata_fixups: {:#?}",
272 self.data_fixups
273 );
274 self.data_header = header; self.current_position = backup_pos;
278 }
279 _ => {} };
281 self.current_position += 48; }
283 Ok(())
284 }
285
286 fn get_class_index_ptr(&mut self) -> Result<Pointer> {
288 let global_fixup_src = self.relative_position();
289
290 if let Some((_section_index, global_dst)) =
291 self.data_fixups.global_fixups.get(&global_fixup_src)
292 {
293 if let Some(class_index) = self.data_fixups.virtual_fixups.get_index_of(global_dst) {
294 #[cfg(feature = "tracing")]
295 tracing::debug!(
296 "global_fixup_src: {global_fixup_src}, class_index(from global_dst): {class_index}"
297 );
298
299 self.current_position += if self.is_x86 { 4 } else { 8 };
300 Ok(Pointer::new(class_index + 1))
301 } else {
302 #[cfg(feature = "tracing")]
303 tracing::debug!(
304 "Missing unique index of class for `global_fixup.dst(virtual_src)`({global_dst}) -> Not found `virtual_fixup.name_offset`. `NullPtr` is entered instead."
305 );
306 self.current_position += if self.is_x86 { 4 } else { 8 };
307 Ok(Pointer::new(0))
308 }
309 } else {
310 #[cfg(feature = "tracing")]
311 tracing::debug!(
312 "Not found `global_fixup.src({global_fixup_src})` -> `global_fixup.dst`. `NullPtr` is entered instead."
313 );
314 self.current_position += if self.is_x86 { 4 } else { 8 };
315 Ok(Pointer::new(0))
316 }
317 }
318
319 fn get_local_fixup_dst(&self) -> Result<usize> {
324 let local_src = self.relative_position();
325
326 #[cfg(feature = "tracing")]
327 {
328 let local_src_abs = self.current_position;
329 tracing::debug!("local_src: {local_src}/abs({local_src_abs:#x})");
330 }
331
332 #[allow(clippy::unnecessary_lazy_evaluations)]
333 let local_dst = *tri!({
334 self.data_fixups
335 .local_fixups
336 .get(&local_src)
337 .ok_or_else(|| {
338 #[cfg(feature = "tracing")]
339 tracing::debug!("Not found `local_fixup.src({local_src})` -> `local_dst`.");
340 Error::NotFoundDataLocalFixupsValue { key: local_src }
341 })
342 });
343
344 let local_dst_abs = (local_dst + self.data_header.absolute_data_start) as usize;
346 #[cfg(feature = "tracing")]
347 tracing::debug!("local_dst: {local_dst}/abs({local_dst_abs:#x})");
348 Ok(local_dst_abs)
349 }
350
351 fn parse_local_fixup<O, P>(&mut self, parser: P) -> Result<Option<O>>
353 where
354 P: Parser<BytesStream<'de>, O, winnow::error::ContextError>,
355 {
356 let backup_position = self.current_position();
357 self.current_position = match self.get_local_fixup_dst().ok() {
358 Some(dst) => dst,
359 None => return Ok(None),
360 };
361
362 let res = tri!(self.parse_peek(parser));
363
364 self.current_position = backup_position as usize;
365 Ok(Some(res))
366 }
367
368 fn skip_ptr_size(&mut self) -> Result<()> {
373 if self.is_x86 {
374 tri!(
375 self.parse_peek(binary::u32(self.endian).verify(|uint| *uint == 0).context(
376 StrContext::Expected(StrContextValue::Description(
377 "Skip x86 ptr size(0 fill 4bytes)"
378 ))
379 ))
380 );
381 self.current_position += 4;
382 } else {
383 tri!(
384 self.parse_peek(
385 binary::u64(self.endian)
386 .verify(|ulong| *ulong == 0)
387 .context(StrContext::Expected(StrContextValue::Description(
388 "Skip x64 ptr size(0 fill 8bytes)"
389 )))
390 )
391 );
392 self.current_position += 8;
393 };
394 Ok(())
395 }
396
397 #[inline]
403 const fn current_position(&self) -> u32 {
404 self.current_position as u32
405 }
406
407 #[inline]
412 const fn relative_position(&self) -> u32 {
413 self.current_position() - self.data_header.absolute_data_start
414 }
415}
416
417impl<'de> de::Deserializer<'de> for &mut BytesDeserializer<'de> {
421 type Error = Error;
422
423 #[inline]
424 fn deserialize_identifier<V>(
425 self,
426 size: ReadEnumSize,
427 visitor: V,
428 ) -> Result<V::Value, Self::Error>
429 where
430 V: Visitor<'de>,
431 {
432 self.deserialize_flags(size, visitor)
433 }
434
435 #[cold]
437 fn deserialize_key<V>(self, visitor: V) -> Result<V::Value, Self::Error>
438 where
439 V: Visitor<'de>,
440 {
441 visitor.visit_void(())
442 }
443
444 fn deserialize_class_index<V>(self, visitor: V) -> Result<V::Value, Self::Error>
446 where
447 V: Visitor<'de>,
448 {
449 visitor.visit_class_index(BytesClassIndexMapDeserializer::new(self))
450 }
451
452 #[inline]
453 fn deserialize_void<V>(self, visitor: V) -> Result<V::Value, Self::Error>
454 where
455 V: Visitor<'de>,
456 {
457 visitor.visit_void(())
458 }
459
460 fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
461 where
462 V: Visitor<'de>,
463 {
464 let res = visitor.visit_bool(tri!(self.parse_peek(boolean)));
465 self.current_position += 1;
466 res
467 }
468
469 fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
470 where
471 V: Visitor<'de>,
472 {
473 let res = visitor.visit_char(tri!(self.parse_peek(
474 binary::le_u8.context(StrContext::Expected(StrContextValue::Description("char")))
475 )) as char);
476 self.current_position += 1;
477 res
478 }
479
480 fn deserialize_int8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
481 where
482 V: Visitor<'de>,
483 {
484 let res = visitor.visit_int8(tri!(self.parse_peek(
485 binary::le_i8.context(StrContext::Expected(StrContextValue::Description("i8")))
486 )));
487 self.current_position += 1;
488 res
489 }
490
491 fn deserialize_uint8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
492 where
493 V: Visitor<'de>,
494 {
495 let res = visitor.visit_uint8(tri!(self.parse_peek(
496 binary::le_u8.context(StrContext::Expected(StrContextValue::Description("u8")))
497 )));
498 self.current_position += 1;
499 res
500 }
501
502 fn deserialize_int16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
503 where
504 V: Visitor<'de>,
505 {
506 let res = visitor.visit_int16(tri!(
507 self.parse_peek(
508 binary::i16(self.endian)
509 .context(StrContext::Expected(StrContextValue::Description("i16")))
510 )
511 ));
512 self.current_position += 2;
513 res
514 }
515
516 fn deserialize_uint16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
517 where
518 V: Visitor<'de>,
519 {
520 let res = visitor.visit_uint16(tri!(
521 self.parse_peek(
522 binary::u16(self.endian)
523 .context(StrContext::Expected(StrContextValue::Description("u16")))
524 )
525 ));
526 self.current_position += 2;
527 res
528 }
529
530 fn deserialize_int32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
531 where
532 V: Visitor<'de>,
533 {
534 let res = visitor.visit_int32(tri!(
535 self.parse_peek(
536 binary::i32(self.endian)
537 .context(StrContext::Expected(StrContextValue::Description("i32")))
538 )
539 ));
540 self.current_position += 4;
541 res
542 }
543
544 fn deserialize_uint32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
545 where
546 V: Visitor<'de>,
547 {
548 let res = visitor.visit_uint32(tri!(
549 self.parse_peek(
550 binary::u32(self.endian)
551 .context(StrContext::Expected(StrContextValue::Description("u32")))
552 )
553 ));
554 self.current_position += 4;
555 res
556 }
557
558 fn deserialize_int64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
559 where
560 V: Visitor<'de>,
561 {
562 let res = visitor.visit_int64(tri!(
563 self.parse_peek(
564 binary::i64(self.endian)
565 .context(StrContext::Expected(StrContextValue::Description("i64")))
566 )
567 ));
568 self.current_position += 8;
569 res
570 }
571
572 fn deserialize_uint64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
573 where
574 V: Visitor<'de>,
575 {
576 let res = visitor.visit_uint64(tri!(
577 self.parse_peek(
578 binary::u64(self.endian)
579 .context(StrContext::Expected(StrContextValue::Description("u64")))
580 )
581 ));
582 self.current_position += 8;
583 res
584 }
585
586 fn deserialize_real<V>(self, visitor: V) -> Result<V::Value, Self::Error>
587 where
588 V: Visitor<'de>,
589 {
590 let res = visitor.visit_real(tri!(self.parse_peek(
591 real(self.endian).context(StrContext::Expected(StrContextValue::Description("f32")))
592 )));
593 self.current_position += 4;
594 res
595 }
596
597 fn deserialize_vector4<V>(self, visitor: V) -> Result<V::Value, Self::Error>
598 where
599 V: Visitor<'de>,
600 {
601 let res = visitor.visit_vector4(tri!(self.parse_peek(vector4(self.endian))));
602 self.current_position += 16;
603 res
604 }
605
606 fn deserialize_quaternion<V>(self, visitor: V) -> Result<V::Value, Self::Error>
607 where
608 V: Visitor<'de>,
609 {
610 let res = visitor.visit_quaternion(tri!(self.parse_peek(quaternion(self.endian))));
611 self.current_position += 16;
612 res
613 }
614
615 fn deserialize_matrix3<V>(self, visitor: V) -> Result<V::Value, Self::Error>
616 where
617 V: Visitor<'de>,
618 {
619 let res = visitor.visit_matrix3(tri!(self.parse_peek(matrix3(self.endian))));
620 self.current_position += 48;
621 res
622 }
623
624 fn deserialize_rotation<V>(self, visitor: V) -> Result<V::Value, Self::Error>
625 where
626 V: Visitor<'de>,
627 {
628 let res = visitor.visit_rotation(tri!(self.parse_peek(rotation(self.endian))));
629 self.current_position += 48;
630 res
631 }
632
633 fn deserialize_qstransform<V>(self, visitor: V) -> Result<V::Value, Self::Error>
634 where
635 V: Visitor<'de>,
636 {
637 let res = visitor.visit_qstransform(tri!(self.parse_peek(qstransform(self.endian))));
638 self.current_position += 48;
639 res
640 }
641
642 fn deserialize_matrix4<V>(self, visitor: V) -> Result<V::Value, Self::Error>
643 where
644 V: Visitor<'de>,
645 {
646 let res = visitor.visit_matrix4(tri!(self.parse_peek(matrix4(self.endian))));
647 self.current_position += 64;
648 res
649 }
650
651 fn deserialize_transform<V>(self, visitor: V) -> Result<V::Value, Self::Error>
652 where
653 V: Visitor<'de>,
654 {
655 let res = visitor.visit_transform(tri!(self.parse_peek(transform(self.endian))));
656 self.current_position += 64;
657 res
658 }
659
660 fn deserialize_pointer<V>(self, visitor: V) -> Result<V::Value, Self::Error>
661 where
662 V: Visitor<'de>,
663 {
664 visitor.visit_pointer(tri!(self.get_class_index_ptr()))
665 }
666
667 fn deserialize_array<V>(self, visitor: V) -> Result<V::Value, Self::Error>
668 where
669 V: Visitor<'de>,
670 {
671 #[cfg(feature = "tracing")]
672 tracing::debug!(
673 "current_position: relative({:#x})/abs({:#x})",
674 self.relative_position(),
675 self.current_position
676 );
677
678 let (size, _cap_and_flags) = tri!(self.parse_peek(array_meta(self.is_x86, self.endian)));
681 #[cfg(feature = "tracing")]
682 tracing::debug!("in_struct array_size: {size}");
683
684 if size == 0 {
685 self.current_position += if self.is_x86 { 12 } else { 16 };
686 visitor.visit_array(SeqDeserializer::new(self, 0))
687 } else {
688 let pointed_data_position = tri!(self.get_local_fixup_dst());
690 self.current_position += if self.is_x86 { 12 } else { 16 }; let backup_position = self.current_position;
692
693 self.current_position = pointed_data_position;
694 let res = visitor.visit_array(SeqDeserializer::new(self, size));
695 self.current_position = backup_position;
696 res
697 }
698 }
699
700 #[inline]
702 fn deserialize_fixed_array<V>(self, visitor: V) -> Result<V::Value, Self::Error>
703 where
704 V: Visitor<'de>,
705 {
706 visitor.visit_array(SeqDeserializer::new(self, usize::MAX))
709 }
710
711 fn deserialize_class_index_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
712 where
713 V: Visitor<'de>,
714 {
715 let size = self.data_fixups.virtual_fixups.len();
716
717 #[cfg(feature = "tracing")]
718 tracing::debug!("class_map_size: {size}");
719 visitor.visit_array(SeqDeserializer::new(self, size))
720 }
721
722 fn deserialize_enum<V>(
723 self,
724 _name: &'static str,
725 _variants: &'static [&'static str],
726 visitor: V,
727 ) -> Result<V::Value, Self::Error>
728 where
729 V: Visitor<'de>,
730 {
731 visitor.visit_enum(EnumDeserializer::new(self))
732 }
733
734 fn deserialize_struct<V>(
735 self,
736 _name: &'static str,
737 fields: &'static [&'static str],
738 visitor: V,
739 ) -> Result<V::Value, Self::Error>
740 where
741 V: Visitor<'de>,
742 {
743 visitor.visit_struct_for_bytes(MapDeserializer::new(self, fields))
744 }
745
746 fn deserialize_variant<V>(self, visitor: V) -> Result<V::Value, Self::Error>
748 where
749 V: Visitor<'de>,
750 {
751 let res = visitor.visit_variant(Variant::new(Pointer::new(0), Pointer::new(0)));
752 self.current_position += if self.is_x86 { 8 } else { 16 };
753 res
754 }
755
756 fn deserialize_cstring<V>(self, visitor: V) -> Result<V::Value, Self::Error>
757 where
758 V: Visitor<'de>,
759 {
760 let s = tri!(self.parse_local_fixup(string)).map_or_else(
761 || {
762 #[cfg(feature = "tracing")]
763 tracing::debug!("CString is NullPtr");
764 CString::from_option(None)
765 },
766 CString::from_str,
767 );
768 tri!(self.skip_ptr_size());
769 visitor.visit_cstring(s)
770 }
771
772 #[inline]
773 fn deserialize_ulong<V>(self, visitor: V) -> Result<V::Value, Self::Error>
774 where
775 V: Visitor<'de>,
776 {
777 if self.is_x86 {
778 self.deserialize_uint32(visitor)
779 } else {
780 self.deserialize_uint64(visitor)
781 }
782 }
783
784 fn deserialize_flags<V>(self, size: ReadEnumSize, visitor: V) -> Result<V::Value, Self::Error>
785 where
786 V: Visitor<'de>,
787 {
788 match size {
789 ReadEnumSize::Int8 => self.deserialize_int8(visitor),
790 ReadEnumSize::Int16 => self.deserialize_int16(visitor),
791 ReadEnumSize::Int32 => self.deserialize_int32(visitor),
792 ReadEnumSize::Int64 => self.deserialize_int64(visitor),
793 ReadEnumSize::Uint8 => self.deserialize_uint8(visitor),
794 ReadEnumSize::Uint16 => self.deserialize_uint16(visitor),
795 ReadEnumSize::Uint32 => self.deserialize_uint32(visitor),
796 ReadEnumSize::Uint64 => self.deserialize_uint64(visitor),
797 }
798 }
799
800 fn deserialize_half<V>(self, visitor: V) -> Result<V::Value, Self::Error>
801 where
802 V: Visitor<'de>,
803 {
804 let res = visitor.visit_half(tri!(self.parse_peek(parser::type_kind::half(self.endian))));
805 self.current_position += 2;
806 res
807 }
808
809 fn deserialize_stringptr<V>(self, visitor: V) -> Result<V::Value, Self::Error>
810 where
811 V: Visitor<'de>,
812 {
813 let s = tri!(self.parse_local_fixup(string)).map_or_else(
814 || {
815 #[cfg(feature = "tracing")]
816 tracing::debug!("StringPtr is NullPtr");
817 StringPtr::from_option(None)
818 },
819 StringPtr::from_str,
820 );
821 tri!(self.skip_ptr_size());
822 visitor.visit_stringptr(s)
823 }
824}
825
826#[cfg(test)]
827mod tests {
828 use super::*;
829 use crate::tests::ClassMap;
830 use havok_classes::{EventMode, hkBaseObject, hkClassMember_::FlagValues, hkReferencedObject};
831 use pretty_assertions::assert_eq;
832 use zerocopy::IntoBytes as _;
833
834 fn partial_parse_assert<'a, T>(s: BytesStream<'a>, expected: T)
835 where
836 T: Deserialize<'a> + PartialEq + fmt::Debug,
837 {
838 match from_partial_bytes::<T>(s) {
839 Ok(res) => assert_eq!(res, expected),
840 Err(err) => {
841 tracing::error!(?err);
842 panic!("{err}")
843 }
844 }
845 }
846
847 #[test]
848 fn test_deserialize_primitive() {
849 partial_parse_assert(&[128, 0, 0, 0], FlagValues::ALIGN_8);
850 partial_parse_assert(&[0], EventMode::EVENT_MODE_DEFAULT);
851 }
852
853 #[test]
854 fn test_deserialize_primitive_array() {
855 partial_parse_assert::<[char; 0]>(b"", []);
856 partial_parse_assert(&[1, 0], [true, false]);
857 partial_parse_assert(
858 [
859 0_u32, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
860 ]
861 .as_bytes(),
862 [
863 0_u32, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
864 ],
865 );
866 }
867
868 #[test]
869 fn test_deserialize_math_array() {
870 #[rustfmt::skip]
871 let expected = [
872 Vector4 { x: -0.0, y: 0.0, z: -0.0, w: 1.0, },
873 Vector4 { x: 0.0, y: 0.0, z: -0.0, w: 1.0, },
874 Vector4 { x: -0.0, y: 0.0, z: -0.0, w: 1.0, },
875 ];
876 partial_parse_assert(
877 [
878 -0.0_f32, 0.0, -0.0, 1.0, 0.0, 0.0, -0.0, 1.0, -0.0, 0.0, -0.0, 1.0, ]
882 .as_bytes(),
883 expected,
884 );
885 }
886
887 #[test]
888 fn test_deserialize_class() {
889 partial_parse_assert(
890 &[
891 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, ],
896 hkReferencedObject {
897 __ptr: None, parent: hkBaseObject { __ptr: None },
899 m_memSizeAndFlags: 2,
900 m_referenceCount: 0,
901 },
902 );
903 }
904
905 fn from_file<'a, T>(bytes: &'a [u8]) -> T
908 where
909 T: Deserialize<'a>,
910 {
911 match from_bytes::<T>(bytes) {
912 Ok(res) => res,
913 Err(err) => {
914 tracing::error!("{err}");
915 panic!("{err}")
916 }
917 }
918 }
919
920 #[test]
921 #[cfg_attr(
922 all(feature = "tracing", not(miri)),
923 quick_tracing::init(test = "deserialize_hkx_bytes", stdio = false)
924 )]
925 fn test_deserialize_class_index() {
926 let bytes = include_bytes!("../../../../docs/handson_hex_dump/defaultmale/defaultmale.hkx");
927 let actual = from_file::<ClassMap>(bytes);
928 assert!(actual.len() == 3);
929 }
930}