serde_hkx/bytes/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::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////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
35
36#[derive(Debug, educe::Educe)]
37#[educe(Default)]
38pub struct BytesDeserializer<'de> {
39    /// This is readonly bytes data.
40    input: &'de [u8],
41
42    /// Binary data position currently being read
43    current_position: usize,
44
45    /// Big or Little Endian
46    #[educe(Default = Endianness::Little)]
47    endian: Endianness,
48    /// Is this binary data for 32-bit?
49    ///
50    /// # Note
51    /// This is related to the read size of the pointer type and the skip size of the padding.
52    is_x86: bool,
53
54    /// `__classnames__` section contents
55    ///
56    /// - key: class name start offset
57    /// - value: class name
58    classnames: ClassNames<'de>,
59
60    /// `__classnames__` header
61    classnames_header: SectionHeader,
62
63    /// `__data__` header
64    data_header: SectionHeader,
65    /// data section fixups
66    data_fixups: Fixups,
67
68    /// Unique Class index & XML name attribute(e.g. `#0050`).
69    ///
70    /// Incremented each time deserialize_struct is called.
71    /// # Note
72    /// It exists to enable class_index to be retrieved at any time when seq'd as a key in a HashMap, etc.
73    class_index: usize,
74
75    /// takeable index for root class (e.g. `#0050`)
76    ///
77    /// # Intent that field exists.
78    /// Provide an [`Option::take`] able index to prevent accidentally giving an index to a class in the structure
79    /// or to the parent class of an inheritance source.
80    ///
81    /// The only place it is incremented is `next_key` in `class_index_map`.
82    /// Currently, this means that the index is not incremented except for `HashMap<usize, Classes>`.
83    takable_class_index: Option<Pointer>,
84}
85
86impl<'de> BytesDeserializer<'de> {
87    /// from xml string
88    pub fn from_bytes(input: &'de [u8]) -> Self {
89        Self {
90            input,
91            ..Default::default()
92        }
93    }
94}
95
96/// Parse binary data as the type specified in the partial generics.
97///
98/// e.g. one class, 3 booleans, `[u32; 10]`,
99///
100/// # Errors
101/// Fail to parse bytes.
102///
103/// # Note
104/// If pointer types are included, it is impossible to deserialize correctly because fixups information is required.
105pub 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
112/// Parse binary data as the type specified in the partial generics with custom `BytesDeserializer` settings.
113///
114/// e.g. one class, 3 booleans, `[u32; 10]`,
115///
116/// # Errors
117/// Fail to parse bytes.
118///
119/// # Note
120/// If pointer types are included, it is impossible to deserialize correctly because fixups information is required.
121pub 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
135/// Analyze as binary data of one file in order from hkx header.
136///
137/// # Errors
138/// Fail to parse bytes.
139pub 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
146/// Analyze as binary data of one file in order from hkx header(with custom deserializer settings).
147///
148/// # Errors
149/// Fail to parse bytes.
150pub 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    // 1. Deserialize root file header.
157    let header = tri!(
158        de.parse_peek(HkxHeader::parser())
159            .map_err(|err| de.to_readable_err(err))
160    );
161    de.current_position += 64; // Advance the position by the header size.
162    de.is_x86 = header.pointer_size == 4;
163    de.endian = header.endian();
164
165    // 2. Deserialize the fixups in the classnames and data sections.
166    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    // 3. Parse `__classnames__` section.
176    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; // FIXME: Assumption that `classnames_abs` < `data_abs`
179    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    // 4. Parse `__data__` section.
185    de.current_position = data_abs; // move to data section start
186    T::deserialize(&mut de).map_err(|err| de.to_readable_err(err))
187}
188
189// SERDE IS NOT A PARSING LIBRARY. This impl block defines a few basic parsing
190// functions from scratch. More complicated formats may wish to use a dedicated
191// parsing library to help implement their Serde deserializer.
192impl<'de> BytesDeserializer<'de> {
193    /// Parse by argument parser.
194    ///
195    /// If an error occurs, it is converted to [`ReadableError`] and returned.
196    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    /// Parse by argument parser.
207    ///
208    /// If an error occurs, it is converted to [`Error::ContextError`] and returned.
209    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    /// Convert Visitor errors to position-assigned errors.
220    ///
221    /// # Why is this necessary?
222    /// Because Visitor errors that occur within each `Deserialize` implementation cannot indicate the error location in bytes.
223    #[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    /// Deserialize the fixups in the `classnames` and `data` sections, relying on the information in the root header.
236    ///
237    /// And, sets fixups to deserializer.
238    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                    // NOTE: no fixups
253                    // The `classnames` section always has no fixups but its place is filled with abs data.
254                }
255
256                i if data_section_index == i => {
257                    // 1/4: After parsing the headers, the fixups are parsed, but the position must be returned for the next header read.
258                    let backup_pos = self.current_position;
259
260                    // 2/4: read header
261                    let header = tri!(self.parse_peek(SectionHeader::from_bytes(self.endian)));
262
263                    // 3/4: read fixups
264                    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; // Let them be substituted here to avoid ownership issues.
275
276                    // 4/4: back to header position
277                    self.current_position = backup_pos;
278                }
279                _ => {} // Skip unused __types__ section
280            };
281            self.current_position += 48; // advance section header size(48bytes)
282        }
283        Ok(())
284    }
285
286    /// Get current position(as `global_fixup.src`) -> `global_fixup.dst` -> class index
287    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    /// Extract the absolute position of the data position pointed to by ptr
320    ///
321    /// Take the relative position of the `__data__` section at the current position as a key
322    /// and extract the corresponding value from the `local_fixups`.
323    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        // Change to abs
345        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    /// Jump current position(`local_fixup.src`) to dst, then parse, and back to current position.
352    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    /// Skip ptr size
369    ///
370    /// # Errors
371    /// Error if the value of ptr to skip is not 0.
372    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    /// Get current bytes position.
398    ///
399    /// # Note
400    /// This returns [`u32`] to be used as a key to retrieve the data position from the `fixups` that fixes
401    /// the data position pointed to by the pointer type.
402    #[inline]
403    const fn current_position(&self) -> u32 {
404        self.current_position as u32
405    }
406
407    /// Returns the relative position of the start of data_section as 0.
408    ///
409    /// # Intent
410    /// Use this API when key of fixups requires relative position.
411    #[inline]
412    const fn relative_position(&self) -> u32 {
413        self.current_position() - self.data_header.absolute_data_start
414    }
415}
416
417// INFO:
418// Where did the visit method come from?
419// It creates a visit when implementing each Deserialize and reads it. The default is to return an error.
420impl<'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    // NOTE: This method is never used with bytes because the number of times is controlled by the for loop.
436    #[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    // Deserialize one class.
445    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        // If size is 0, local_fixups does not exist, so check size first.
679        // NOTE: This is a look-ahead, assuming the position does not move with this method.
680        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            // The specification requires that the ptr data position be extracted before parsing meta information such as `ptr_size`.
689            let pointed_data_position = tri!(self.get_local_fixup_dst());
690            self.current_position += if self.is_x86 { 12 } else { 16 }; // NOTE: If we move position before asking for local_fixup, we will not get key correctly.
691            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    // Fixed size array(stack array) is written directly without metadata
701    #[inline]
702    fn deserialize_fixed_array<V>(self, visitor: V) -> Result<V::Value, Self::Error>
703    where
704        V: Visitor<'de>,
705    {
706        // The fixed size array is controlled by a for loop, so the number of times is not controlled on the deserializer side.
707        // Therefore, a dummy is plugged in.
708        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    /// TODO: binary representation of Variant is unknown.
747    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, // 1 vec4
879                0.0, 0.0, -0.0, 1.0, // 2 vec4
880                -0.0, 0.0, -0.0, 1.0, // 3 vec4
881            ]
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, // hkBaseObject
892                2, 0, // mem_size_and_flags
893                0, 0, // reference_count
894                0, 0, 0, 0, // 8bytes align for struct
895            ],
896            hkReferencedObject {
897                __ptr: None, // In single class partial mode, ptr is not allocated.
898                parent: hkBaseObject { __ptr: None },
899                m_memSizeAndFlags: 2,
900                m_referenceCount: 0,
901            },
902        );
903    }
904
905    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
906
907    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}