serde_hkx/xml/de/
mod.rs

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::ClassIndexMapDeserializer;
10use self::enum_access::EnumDeserializer;
11use self::map::MapDeserializer;
12use self::parser::{
13    comment_multispace0,
14    tag::{attr_string, end_tag},
15    type_kind::{
16        boolean, matrix3, matrix4, pointer, qstransform, quaternion, real, rotation, string,
17        transform, vector4,
18    },
19};
20use self::seq::SeqDeserializer;
21use crate::errors::{
22    de::{Error, Result},
23    readable::ReadableError,
24};
25use havok_serde::de::{self, Deserialize, ReadEnumSize, Visitor};
26use havok_types::*;
27use parser::tag::{class_start_tag, start_tag};
28use winnow::Parser;
29use winnow::ascii::{dec_int, dec_uint};
30use winnow::combinator::opt;
31
32////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
33
34#[derive(Debug)]
35pub struct XmlDeserializer<'de> {
36    /// This string starts with the input data and characters are truncated off
37    /// the beginning as data is parsed.
38    input: &'de str,
39
40    /// This is readonly for error report. Not move position.
41    original: &'de str,
42
43    /// Unique Class index & XML name attribute(e.g. `#0050`).
44    ///
45    /// Incremented each time deserialize_struct is called.
46    ///
47    /// And this is present in `SeqAccess::class_ptr` to refer to class_ptr as a key in [`HashMap`].
48    class_index: Option<usize>,
49
50    ///  In `Struct` deserialization?
51    ///
52    /// # Why need this flag?
53    /// This flag is necessary because XML handles deserialization of a field in a struct differently
54    /// than it handles deserialization of a struct in a field in a struct.
55    ///
56    /// - root struct: `<hkobject name="#0050" class="" signature=""></hkobject>`
57    /// - struct in field: `<hkobject></hkobject>`
58    in_struct: bool,
59}
60
61impl<'de> XmlDeserializer<'de> {
62    /// from xml string
63    #[allow(clippy::should_implement_trait)]
64    #[inline]
65    pub const fn from_str(input: &'de str) -> Self {
66        XmlDeserializer {
67            input,
68            original: input,
69            class_index: None,
70            in_struct: false,
71        }
72    }
73}
74
75/// from partial xml string.
76///
77/// # Errors
78/// If XML parsing fails.
79#[inline]
80pub fn from_partial_str<'a, T>(s: &'a str) -> Result<T>
81where
82    T: Deserialize<'a>,
83{
84    from_partial_str_with_opt(XmlDeserializer::from_str(s))
85}
86
87/// from partial xml string with custom `XmlDeserializer` settings.
88///
89/// # Errors
90/// If XML parsing fails.
91pub fn from_partial_str_with_opt<'a, T>(de: XmlDeserializer<'a>) -> Result<T>
92where
93    T: Deserialize<'a>,
94{
95    let mut deserializer = de;
96    let t = tri!(T::deserialize(&mut deserializer));
97
98    if deserializer.input.is_empty() {
99        Ok(t)
100    } else {
101        Err(Error::TrailingCharacters {
102            remain: deserializer.input.to_string(),
103        })
104    }
105}
106
107/// From xml string.
108///
109/// # Errors
110/// If XML parsing fails.
111#[inline]
112pub fn from_str<'a, T>(s: &'a str) -> Result<T>
113where
114    T: Deserialize<'a>,
115{
116    from_str_with_opt(XmlDeserializer::from_str(s))
117}
118
119/// from xml string with custom `XmlDeserializer` settings.
120///
121/// # Errors
122/// If XML parsing fails.
123pub fn from_str_with_opt<'a, T>(de: XmlDeserializer<'a>) -> Result<T>
124where
125    T: Deserialize<'a>,
126{
127    let mut de = de;
128
129    tri!(
130        de.parse_next(opt(winnow::token::take_until(
131            0..,
132            "<hksection name=\"__data__\">"
133        )))
134        .map_err(|err| de.to_readable_err(err))
135    );
136    tri!(
137        de.parse_next(winnow::token::take_until(0.., "<hkobject"))
138            .map_err(|err| de.to_readable_err(err))
139    );
140    let t = tri!(T::deserialize(&mut de).map_err(|err| de.to_readable_err(err)));
141    tri!(
142        de.parse_next(opt(end_tag("hksection")))
143            .map_err(|err| de.to_readable_err(err))
144    );
145    tri!(
146        de.parse_next(opt(end_tag("hkpackfile")))
147            .map_err(|err| de.to_readable_err(err))
148    );
149
150    if de.input.is_empty() {
151        Ok(t)
152    } else {
153        Err(de.to_readable_err(Error::TrailingCharacters {
154            remain: de.input.to_string(),
155        }))
156    }
157}
158
159/// Deserializes any value and returns the rest of the string together.
160///
161/// # Errors
162/// If XML parsing fails.
163///
164/// # Returns
165/// (remain input, deserialized value)
166pub fn from_str_peek<'a, T>(s: &'a str) -> Result<(&'a str, T)>
167where
168    T: Deserialize<'a>,
169{
170    let mut deserializer = XmlDeserializer::from_str(s);
171    let t = T::deserialize(&mut deserializer)?;
172    Ok((deserializer.input, t))
173}
174
175// SERDE IS NOT A PARSING LIBRARY. This impl block defines a few basic parsing
176// functions from scratch. More complicated formats may wish to use a dedicated
177// parsing library to help implement their Serde deserializer.
178impl<'de> XmlDeserializer<'de> {
179    /// Parse by argument parser.
180    ///
181    /// If an error occurs, it is converted to [`ReadableError`] and returned.
182    fn parse_next<O>(
183        &mut self,
184        mut parser: impl Parser<&'de str, O, winnow::error::ContextError>,
185    ) -> Result<O> {
186        let res = parser
187            .parse_next(&mut self.input)
188            .map_err(|err| Error::ContextError { err })?;
189        Ok(res)
190    }
191
192    /// Parse by argument parser no consume.
193    ///
194    /// If an error occurs, it is converted to [`ReadableError`] and returned.
195    fn parse_peek<O>(
196        &self,
197        mut parser: impl Parser<&'de str, O, winnow::error::ContextError>,
198    ) -> Result<O> {
199        let (_, res) = parser
200            .parse_peek(self.input)
201            .map_err(|err| Error::ContextError { err })?;
202        Ok(res)
203    }
204
205    /// Convert Visitor errors to position-assigned errors.
206    ///
207    /// # Why is this necessary?
208    /// Because Visitor errors that occur within each `Deserialize` implementation cannot indicate the error location in XML.
209    #[cold]
210    fn to_readable_err(&self, err: Error) -> Error {
211        let readable = match err {
212            Error::ContextError { err } => ReadableError::from_context(
213                err,
214                self.original,
215                self.original.len() - self.input.len(),
216            ),
217            Error::ReadableError { source } => source,
218            err => ReadableError::from_display(
219                err,
220                self.original,
221                self.original.len() - self.input.len(),
222            ),
223        };
224
225        Error::ReadableError { source: readable }
226    }
227}
228
229// INFO:
230// Where did the visit method come from?
231// It creates a visit when implementing each Deserialize and reads it. The default is to return an error.
232impl<'de> de::Deserializer<'de> for &mut XmlDeserializer<'de> {
233    type Error = Error;
234
235    /// # Note
236    /// The enum implementor must parse the incoming parsed enum (or bitflags) by calling
237    /// `visit_stringptr` in `impl Deserialize`.
238    ///
239    /// 1. Read `ANY_ENUM_VARIANT` in `<hkparam>ANY_ENUM_VARIANT</hkparam>`
240    /// 2. Check by calling `visit_stringptr` .
241    fn deserialize_identifier<V>(
242        self,
243        _size: ReadEnumSize,
244        visitor: V,
245    ) -> Result<V::Value, Self::Error>
246    where
247        V: Visitor<'de>,
248    {
249        let s = tri!(self.parse_next(string)); // Read Until `</`
250        visitor.visit_stringptr(StringPtr::from_option(Some(s)))
251    }
252
253    // To parse field in struct (e.g. `<hkparam name="key"></hkparam>`)
254    #[inline]
255    fn deserialize_key<V>(self, visitor: V) -> Result<V::Value, Self::Error>
256    where
257        V: Visitor<'de>,
258    {
259        let key = tri!(self.parse_next(attr_string));
260        #[cfg(feature = "tracing")]
261        tracing::debug!(key);
262
263        visitor.visit_key(key)
264    }
265
266    #[inline]
267    fn deserialize_class_index<V>(self, visitor: V) -> Result<V::Value, Self::Error>
268    where
269        V: Visitor<'de>,
270    {
271        visitor.visit_class_index(ClassIndexMapDeserializer::new(self))
272    }
273
274    #[inline]
275    fn deserialize_void<V>(self, visitor: V) -> Result<V::Value, Self::Error>
276    where
277        V: Visitor<'de>,
278    {
279        visitor.visit_void(())
280    }
281
282    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
283    where
284        V: Visitor<'de>,
285    {
286        visitor.visit_bool(tri!(self.parse_next(boolean)))
287    }
288
289    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
290    where
291        V: Visitor<'de>,
292    {
293        let ch = self.input.chars().next().ok_or(Error::Eof)?;
294        self.input = &self.input[ch.len_utf8()..];
295        visitor.visit_char(ch)
296    }
297
298    fn deserialize_int8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
299    where
300        V: Visitor<'de>,
301    {
302        visitor.visit_int8(tri!(self.parse_next(dec_int)))
303    }
304
305    fn deserialize_uint8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
306    where
307        V: Visitor<'de>,
308    {
309        visitor.visit_uint8(tri!(self.parse_next(dec_uint)))
310    }
311
312    fn deserialize_int16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
313    where
314        V: Visitor<'de>,
315    {
316        visitor.visit_int16(tri!(self.parse_next(dec_int)))
317    }
318
319    fn deserialize_uint16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
320    where
321        V: Visitor<'de>,
322    {
323        visitor.visit_uint16(tri!(self.parse_next(dec_uint)))
324    }
325
326    fn deserialize_int32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
327    where
328        V: Visitor<'de>,
329    {
330        visitor.visit_int32(tri!(self.parse_next(dec_int)))
331    }
332
333    fn deserialize_uint32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
334    where
335        V: Visitor<'de>,
336    {
337        visitor.visit_uint32(tri!(self.parse_next(dec_uint)))
338    }
339
340    fn deserialize_int64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
341    where
342        V: Visitor<'de>,
343    {
344        visitor.visit_int64(tri!(self.parse_next(dec_int)))
345    }
346
347    fn deserialize_uint64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
348    where
349        V: Visitor<'de>,
350    {
351        visitor.visit_uint64(tri!(self.parse_next(dec_uint)))
352    }
353
354    fn deserialize_real<V>(self, visitor: V) -> Result<V::Value, Self::Error>
355    where
356        V: Visitor<'de>,
357    {
358        visitor.visit_real(tri!(self.parse_next(real)))
359    }
360
361    fn deserialize_vector4<V>(self, visitor: V) -> Result<V::Value, Self::Error>
362    where
363        V: Visitor<'de>,
364    {
365        visitor.visit_vector4(tri!(self.parse_next(vector4)))
366    }
367
368    fn deserialize_quaternion<V>(self, visitor: V) -> Result<V::Value, Self::Error>
369    where
370        V: Visitor<'de>,
371    {
372        visitor.visit_quaternion(tri!(self.parse_next(quaternion)))
373    }
374
375    fn deserialize_matrix3<V>(self, visitor: V) -> Result<V::Value, Self::Error>
376    where
377        V: Visitor<'de>,
378    {
379        visitor.visit_matrix3(tri!(self.parse_next(matrix3)))
380    }
381
382    fn deserialize_rotation<V>(self, visitor: V) -> Result<V::Value, Self::Error>
383    where
384        V: Visitor<'de>,
385    {
386        visitor.visit_rotation(tri!(self.parse_next(rotation)))
387    }
388
389    fn deserialize_qstransform<V>(self, visitor: V) -> Result<V::Value, Self::Error>
390    where
391        V: Visitor<'de>,
392    {
393        visitor.visit_qstransform(tri!(self.parse_next(qstransform)))
394    }
395
396    fn deserialize_matrix4<V>(self, visitor: V) -> Result<V::Value, Self::Error>
397    where
398        V: Visitor<'de>,
399    {
400        visitor.visit_matrix4(tri!(self.parse_next(matrix4)))
401    }
402
403    fn deserialize_transform<V>(self, visitor: V) -> Result<V::Value, Self::Error>
404    where
405        V: Visitor<'de>,
406    {
407        visitor.visit_transform(tri!(self.parse_next(transform)))
408    }
409
410    fn deserialize_pointer<V>(self, visitor: V) -> Result<V::Value, Self::Error>
411    where
412        V: Visitor<'de>,
413    {
414        visitor.visit_pointer(tri!(self.parse_next(pointer)))
415    }
416
417    fn deserialize_array<V>(self, visitor: V) -> Result<V::Value, Self::Error>
418    where
419        V: Visitor<'de>,
420    {
421        tri!(self.parse_next(comment_multispace0));
422        visitor.visit_array(SeqDeserializer::new(self))
423    }
424
425    #[inline]
426    fn deserialize_fixed_array<V>(self, visitor: V) -> Result<V::Value, Self::Error>
427    where
428        V: Visitor<'de>,
429    {
430        self.deserialize_array(visitor)
431    }
432
433    #[inline]
434    fn deserialize_class_index_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
435    where
436        V: Visitor<'de>,
437    {
438        self.deserialize_array(visitor)
439    }
440
441    #[inline]
442    fn deserialize_enum<V>(
443        self,
444        _name: &'static str,
445        _variants: &'static [&'static str],
446        visitor: V,
447    ) -> Result<V::Value, Self::Error>
448    where
449        V: Visitor<'de>,
450    {
451        visitor.visit_enum(EnumDeserializer::new(self))
452    }
453
454    /// # Example of XML to be parsed
455    /// ```xml
456    /// <hkobject name="#0010" class="hkbProjectData" signature="0x13a39ba7">
457    ///   <!-- memSizeAndFlags SERIALIZE_IGNORED -->
458    ///   <!-- referenceCount SERIALIZE_IGNORED -->
459    ///   <hkparam name="worldUpWS">(0.000000 0.000000 1.000000 0.000000)</hkparam>
460    ///   <hkparam name="stringData">#0009</hkparam>
461    ///   <hkparam name="defaultEventMode">EVENT_MODE_IGNORE_FROM_GENERATOR</hkparam>
462    /// </hkobject>
463    /// ```
464    fn deserialize_struct<V>(
465        self,
466        name: &'static str,
467        _fields: &'static [&'static str], // current class's field names only
468        visitor: V,
469    ) -> Result<V::Value, Self::Error>
470    where
471        V: Visitor<'de>,
472    {
473        let ptr_name = if self.in_struct {
474            #[cfg(feature = "tracing")]
475            tracing::trace!("Parsed class=\"{name}\": <hkobject>");
476            // When a struct is present in the field of struct, the name and signature attributes are not present.
477            tri!(self.parse_next(start_tag("hkobject")));
478            None
479        } else {
480            let (ptr_name, class_name, _signature) = tri!(self.parse_next(class_start_tag));
481            #[cfg(feature = "tracing")]
482            tracing::trace!(
483                "Parsed: <hkobject name=\"{ptr_name}\" class=\"{class_name}\" signature=\"{_signature}\">"
484            );
485
486            if name != class_name {
487                return Err(Error::MismatchClassName {
488                    actual: name,
489                    expected: class_name.to_string(),
490                });
491            };
492            self.in_struct = true;
493            self.class_index = Some(ptr_name.get()); // For `HashMap`'s seq key.
494            Some(ptr_name)
495        };
496        #[cfg(feature = "tracing")]
497        tracing::trace!("fields: {_fields:?}");
498
499        let value = tri!(visitor.visit_struct(MapDeserializer::new(self, ptr_name, name,)));
500        tri!(self.parse_next(end_tag("hkobject")));
501        Ok(value)
502    }
503
504    /// TODO: XML representation of Variant is unknown.
505    fn deserialize_variant<V>(self, visitor: V) -> Result<V::Value, Self::Error>
506    where
507        V: Visitor<'de>,
508    {
509        visitor.visit_pointer(tri!(self.parse_next(pointer)))
510    }
511
512    fn deserialize_cstring<V>(self, visitor: V) -> Result<V::Value, Self::Error>
513    where
514        V: Visitor<'de>,
515    {
516        let s = tri!(self.parse_next(string)); // take until `</`
517        let s = if s == "\u{2400}" { None } else { Some(s) }; // NOTE: Unicode null to null ptr.
518        visitor.visit_cstring(CString::from_option(s))
519    }
520
521    #[inline]
522    fn deserialize_ulong<V>(self, visitor: V) -> Result<V::Value, Self::Error>
523    where
524        V: Visitor<'de>,
525    {
526        self.deserialize_uint64(visitor)
527    }
528
529    fn deserialize_flags<V>(self, _size: ReadEnumSize, visitor: V) -> Result<V::Value, Self::Error>
530    where
531        V: Visitor<'de>,
532    {
533        let s = tri!(self.parse_next(string));
534        // Call `FromStr` of each flags. Always returns Some because `unwrap` is used on the assumption that there is a string inside.
535        visitor.visit_stringptr(StringPtr::from_option(Some(s)))
536    }
537
538    fn deserialize_half<V>(self, visitor: V) -> Result<V::Value, Self::Error>
539    where
540        V: Visitor<'de>,
541    {
542        let float = tri!(self.parse_next(real));
543        visitor.visit_half(f16::from_f32(float))
544    }
545
546    fn deserialize_stringptr<V>(self, visitor: V) -> Result<V::Value, Self::Error>
547    where
548        V: Visitor<'de>,
549    {
550        let s = tri!(self.parse_next(string)); // take until `</`
551        let s = if s == "\u{2400}" { None } else { Some(s) }; // NOTE: Unicode null to null ptr.
552        visitor.visit_stringptr(StringPtr::from_option(s))
553    }
554}
555
556#[cfg(test)]
557mod tests {
558    use super::*;
559    use crate::tests::ClassMap;
560    use pretty_assertions::assert_eq;
561
562    fn partial_parse_assert<'a, T>(s: &'a str, expected: T)
563    where
564        T: Deserialize<'a> + PartialEq + fmt::Debug,
565    {
566        match from_partial_str::<T>(s) {
567            Ok(res) => assert_eq!(res, expected),
568            Err(err) => {
569                tracing::error!(?err);
570                panic!("{err}")
571            }
572        }
573    }
574
575    fn parse_peek_assert<'a, T>(s: &'a str, expected: (&str, T))
576    where
577        T: Deserialize<'a> + PartialEq + fmt::Debug,
578    {
579        match from_str_peek::<T>(s) {
580            Ok(res) => assert_eq!(res, expected),
581            Err(err) => {
582                tracing::error!(?err);
583                panic!("{err}")
584            }
585        }
586    }
587
588    #[test]
589    fn test_deserialize_primitive() {
590        use havok_classes::EventMode;
591        use havok_classes::hkClassMember_::FlagValues;
592
593        parse_peek_assert(
594            "ALIGN_8|ALiGN_16|SERIALIZE_IGNORED</hkparam>",
595            (
596                "</hkparam>",
597                FlagValues::ALIGN_8 | FlagValues::ALIGN_16 | FlagValues::SERIALIZE_IGNORED,
598            ),
599        );
600
601        parse_peek_assert(
602            "EVENT_MODE_DEFAULT</hkparam>",
603            ("</hkparam>", EventMode::EVENT_MODE_DEFAULT),
604        );
605
606        // Overflow wrapping(By havok specification)
607        use havok_classes::RoleFlags;
608        const REMAIN_BITS: i16 = 0xFFFFF300_u32 as i16; // -3328 (0xF300)
609        parse_peek_assert(
610            "FLAG_HIDDEN|FLAG_NORMALIZED|FLAG_NONE|0xfffff300</hkparam>",
611            (
612                "</hkparam>",
613                RoleFlags::FLAG_HIDDEN
614                    | RoleFlags::FLAG_NORMALIZED
615                    | RoleFlags::FLAG_NONE
616                    | RoleFlags::from_bits_retain(REMAIN_BITS),
617            ),
618        );
619    }
620
621    #[test]
622    fn test_deserialize_string() {
623        partial_parse_assert::<Vec<StringPtr>>(
624            r#"
625    <hkcstring>Hello</hkcstring>
626        <hkcstring>World</hkcstring>
627    <hkcstring></hkcstring>
628        "#,
629            vec!["Hello".into(), "World".into(), "".into()],
630        );
631    }
632
633    #[test]
634    fn test_deserialize_primitive_vec() {
635        partial_parse_assert(
636            r#"
637                <!-- Hi? -->
638                <!-- Hi? -->
639                true
640
641                <!-- Hi? -->
642                   false
643                <!-- Hi?2 -->
644        "#,
645            vec![true, false],
646        );
647
648        partial_parse_assert(
649            r#"
650    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
651    16 17 18 19 20
652"#,
653            (0..21).collect::<Vec<i32>>(),
654        );
655    }
656
657    #[test]
658    fn test_deserialize_math_vec() {
659        #[rustfmt::skip]
660        partial_parse_assert(
661            r#"   <!-- comment -->
662
663        (-0.000000 0.000000 -0.000000 1.000000  )
664<!-- comment -->
665
666            (   0.000000 0.000000 -0.000000 1.000000  )
667
668                <!-- COmment -->
669
670(   -0.000000 0.000000 -0.000000 1.000000 )
671                            <!-- comment -->
672"#,
673            vec![
674                Vector4 { x: -0.0, y: 0.0, z: -0.0, w: 1.0, },
675                Vector4 { x:  0.0, y: 0.0, z: -0.0, w: 1.0, },
676                Vector4 { x: -0.0, y: 0.0, z: -0.0, w: 1.0, },
677            ],
678        );
679    }
680
681    #[test]
682    fn test_deserialize_primitive_array() {
683        partial_parse_assert::<[char; 0]>("", []);
684    }
685
686    #[test]
687    fn should_skip_class() {
688        use havok_classes::{hkBaseObject, hkReferencedObject};
689        partial_parse_assert(
690            r##"
691<hkobject name="#01000" class="hkReferencedObject" signature="0xea7f1d08">
692    <!-- memSizeAndFlags SERIALIZE_IGNORED -->
693    <!-- referenceCount SERIALIZE_IGNORED -->
694</hkobject>
695            "##,
696            hkReferencedObject {
697                __ptr: Some(Pointer::new(1000)),
698                parent: hkBaseObject { __ptr: None },
699                m_memSizeAndFlags: 0,
700                m_referenceCount: 0,
701            },
702        );
703    }
704
705    #[test]
706    fn test_hka_skeleton_invalid_lock_translation() {
707        use havok_classes::hkaSkeleton;
708
709        let xml = r###"
710        <hkobject name="#0122" class="hkaSkeleton" signature="0x366e8220">
711            <hkparam name="name">NPC Root [Root]</hkparam>
712            <hkparam name="parentIndices" numelements="52">-1 0 1</hkparam>
713            <hkparam name="bones" numelements="52">
714                <hkobject>
715                    <hkparam name="name">NPC Root [Root]</hkparam>
716                    <hkparam name="lockTranslation">fals</hkparam>
717                </hkobject>
718            </hkparam>
719        </hkobject>
720    "###;
721
722        assert!(from_str::<hkaSkeleton>(xml).is_err());
723    }
724
725    #[test]
726    fn test_hka_skeleton_valid() {
727        use havok_classes::{hkBaseObject, hkReferencedObject, hkaBone, hkaSkeleton};
728
729        let xml = r###"
730        <hkobject name="#0122" class="hkaSkeleton" signature="0x366e8220">
731            <hkparam name="name">NPC Root [Root]</hkparam>
732            <hkparam name="parentIndices" numelements="0" />
733            <hkparam name="bones" numelements="52">
734                <hkobject>
735                    <hkparam name="name">NPC Root [Root]</hkparam>
736                    <hkparam name="lockTranslation">false</hkparam>
737                </hkobject>
738            </hkparam>
739        </hkobject>
740    "###;
741
742        assert_eq!(
743            from_str::<hkaSkeleton>(xml),
744            Ok(hkaSkeleton {
745                __ptr: Some(Pointer::new(122)),
746                parent: hkReferencedObject {
747                    __ptr: None,
748                    parent: hkBaseObject { __ptr: None },
749                    m_memSizeAndFlags: 0,
750                    m_referenceCount: 0,
751                },
752                m_name: StringPtr::from_str("NPC Root [Root]"),
753                m_parentIndices: vec![],
754                m_bones: vec![hkaBone {
755                    __ptr: None,
756                    m_name: StringPtr::from_str("NPC Root [Root]"),
757                    m_lockTranslation: false,
758                }],
759                m_referencePose: vec![],
760                m_referenceFloats: vec![],
761                m_floatSlots: vec![],
762                m_localFrames: vec![],
763            }),
764        );
765    }
766
767    #[test]
768    fn test_hk_root_level_container_valid() {
769        use havok_classes::{hkRootLevelContainer, hkRootLevelContainerNamedVariant};
770
771        let xml = r###"
772        <hkobject name="#0008" class="hkRootLevelContainer" signature="0x2772c11e">
773            <hkparam name="namedVariants" numelements="1">
774                <hkobject>
775                    <hkparam name="name">hkbProjectData</hkparam>
776                    <hkparam name="className">hkbProjectData</hkparam>
777                    <hkparam name="variant">#0010</hkparam>
778                </hkobject>
779            </hkparam>
780        </hkobject>
781    "###;
782
783        assert_eq!(
784            from_str::<hkRootLevelContainer>(xml),
785            Ok(hkRootLevelContainer {
786                __ptr: Some(8.into()),
787                m_namedVariants: vec![hkRootLevelContainerNamedVariant {
788                    __ptr: None,
789                    m_name: "hkbProjectData".into(),
790                    m_className: "hkbProjectData".into(),
791                    m_variant: Pointer::new(10),
792                }],
793            })
794        );
795    }
796
797    #[test]
798    fn should_deserialize_classes_from_xml() {
799        use crate::tests::mocks::new_defaultmale;
800        use havok_classes::Classes;
801
802        fn from_file<'a, T>(xml: &'a str) -> T
803        where
804            T: Deserialize<'a>,
805        {
806            match from_str::<T>(xml) {
807                Ok(res) => res,
808                Err(err) => {
809                    tracing::error!("{err}");
810                    panic!("{err}")
811                }
812            }
813        }
814
815        let xml = include_str!("../../../../docs/handson_hex_dump/defaultmale/defaultmale_x86.xml");
816        tracing::debug!("{:#?}", from_file::<Vec<Classes>>(xml));
817
818        let actual = from_file::<ClassMap>(xml);
819        let expected = new_defaultmale();
820        assert_eq!(actual, expected);
821    }
822}