serde_hkx/bytes/ser/sub_ser/
structs.rs

1use crate::{align, lib::*, tri};
2
3use super::super::ByteSerializer;
4use crate::errors::ser::{Error, Result};
5use havok_serde::ser::{Serialize, SerializeStruct, Serializer, TypeSize};
6use havok_types::Ulong;
7use std::io::Write as _;
8
9/// For bytes struct serializer.
10///
11/// # Why separate `ByteSerializer`?
12/// Avoid mixing `local_fixups` for each field by creating local variables with separate Serializer.
13pub struct StructSerializer<'a> {
14    ser: &'a mut ByteSerializer,
15    is_root: bool,
16}
17
18impl<'a> StructSerializer<'a> {
19    pub const fn new(ser: &'a mut ByteSerializer, is_root: bool) -> Self {
20        Self { ser, is_root }
21    }
22
23    /// processing of array(`hkArray`).
24    fn serialize_array_inner<V, T>(
25        &mut self,
26        array: V,
27        size: TypeSize,
28        local_src: u32,
29    ) -> Result<()>
30    where
31        V: AsRef<[T]> + Serialize,
32        T: Serialize,
33    {
34        let next_src_pos = self.ser.output.position();
35
36        {
37            let pointed_pos = tri!(self.ser.goto_local_dst());
38            self.ser.write_local_fixup_pair(local_src, pointed_pos)?;
39        }
40        let array_base_pos = self.ser.current_last_local_dst;
41
42        // push nest
43        let len = array.as_ref().len() as u32;
44        match size {
45            TypeSize::Struct {
46                size_x86,
47                size_x86_64,
48            } => {
49                let one_size = if self.ser.is_x86 {
50                    size_x86
51                } else {
52                    size_x86_64
53                };
54                let mut write_pointed_pos = { array_base_pos + (one_size * (len as u64)) };
55                #[cfg(feature = "tracing")]
56                tracing::trace!(
57                    "Calculate Struct of Array local dst: array_base_pos({array_base_pos:#x}) + one_size({one_size}) * len({len}) = {write_pointed_pos:#x}"
58                );
59
60                // NOTE: The first write beyond the ptr after the Array nests twice should be align16 (not sure why)
61                //       Then, for some reason, the binary data reproduction is perfect.
62                if self.ser.pointed_pos.len() >= 2 {
63                    let new_write_pointed_pos = align!(write_pointed_pos, 16_u64);
64                    #[cfg(feature = "tracing")]
65                    tracing::trace!(
66                        "Apply special align16 to `next_struct_local_dst` because the hkArray is nested twice: {write_pointed_pos:#x} -> {new_write_pointed_pos:#x}"
67                    );
68                    write_pointed_pos = new_write_pointed_pos;
69                }
70                self.ser.pointed_pos.push(write_pointed_pos); // To write inner member type.
71            }
72            TypeSize::String => {
73                self.ser.is_in_str_array = true;
74                let one_size = if self.ser.is_x86 { 4 } else { 8 };
75                let write_pointed_pos = { array_base_pos + (one_size * (len as u64)) };
76
77                #[cfg(feature = "tracing")]
78                tracing::trace!(
79                    "Calculate String of Array local dst: array_base_pos({array_base_pos:#x}) + one_size({one_size}) * len({len}) = {write_pointed_pos:#x}"
80                );
81
82                self.ser.pointed_pos.push(write_pointed_pos); // To write pointed string data.
83            }
84            TypeSize::NonPtr => {}
85        }
86        #[cfg(feature = "tracing")]
87        tracing::trace!("pointed_pos:({:#x?})", self.ser.pointed_pos);
88
89        tri!(array.serialize(&mut *self.ser));
90        self.ser.is_in_str_array = false;
91
92        if size == TypeSize::NonPtr {
93            let next_pointed_ser_pos = align!(self.ser.output.position(), 16_u64);
94            self.ser.current_last_local_dst = next_pointed_ser_pos;
95            if let Some(last) = self.ser.pointed_pos.last_mut() {
96                *last = next_pointed_ser_pos; // Update to serialize the next pointed data.
97            };
98        } else {
99            // HACK: unused last value to update;
100            let pos = tri!(
101                self.ser
102                    .pointed_pos
103                    .pop()
104                    .ok_or(Error::NotFoundPointedPosition)
105            );
106            let pos = align!(pos, 16_u64);
107            if let Some(last) = self.ser.pointed_pos.last_mut() {
108                *last = pos;
109            };
110            self.ser.current_last_local_dst = pos;
111        }
112
113        self.ser.output.set_position(next_src_pos); // Go to the next field serialization position.
114        Ok(())
115    }
116
117    /// Common processing of fixed_array(e.g. `[bool; 3]`)
118    fn serialize_array_fixed<V, T>(
119        &mut self,
120        array: V,
121        size: TypeSize,
122        local_src: u32,
123    ) -> Result<()>
124    where
125        V: AsRef<[T]> + Serialize,
126        T: Serialize,
127    {
128        let need_local_jump = size != TypeSize::NonPtr;
129        if need_local_jump {
130            let pointed_pos = tri!(self.ser.goto_local_dst());
131            let array_base_pos = self.ser.current_last_local_dst;
132            self.ser.write_local_fixup_pair(local_src, pointed_pos)?;
133
134            // push nest
135            let len = array.as_ref().len() as u32;
136            match size {
137                TypeSize::Struct {
138                    size_x86,
139                    size_x86_64,
140                } => {
141                    let write_pointed_pos = {
142                        let one_size = if self.ser.is_x86 {
143                            size_x86
144                        } else {
145                            size_x86_64
146                        };
147                        array_base_pos + (one_size * (len as u64))
148                    }; // `local_dst` starting position of class.
149                    self.ser.pointed_pos.push(write_pointed_pos); // To write inner member type.
150                }
151                TypeSize::String => {
152                    let write_pointed_pos = {
153                        let one_size = if self.ser.is_x86 { 4 } else { 8 };
154                        array_base_pos + (one_size * (len as u64))
155                    }; // `local_dst` starting position of string.
156
157                    self.ser.pointed_pos.push(write_pointed_pos); // To write pointed string data.
158                }
159                TypeSize::NonPtr => {}
160            }
161        };
162        #[cfg(feature = "tracing")]
163        tracing::trace!("pointed_pos:({:#x?})", self.ser.pointed_pos);
164
165        tri!(array.serialize(&mut *self.ser));
166
167        if size != TypeSize::NonPtr {
168            // HACK: unused last value to update;
169            let pos = tri!(
170                self.ser
171                    .pointed_pos
172                    .pop()
173                    .ok_or(Error::NotFoundPointedPosition)
174            );
175            if let Some(last) = self.ser.pointed_pos.last_mut() {
176                *last = pos;
177            };
178        }
179        Ok(())
180    }
181}
182
183impl SerializeStruct for StructSerializer<'_> {
184    type Ok = ();
185    type Error = Error;
186
187    #[inline]
188    fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<()>
189    where
190        T: ?Sized + Serialize,
191    {
192        #[cfg(feature = "tracing")]
193        tracing::trace!("serialize field({:#x}): {_key}", self.ser.output.position());
194        value.serialize(&mut *self.ser)
195    }
196
197    /// Even if it is skipped on XML, it is not skipped because it exists in binary data.
198    #[inline]
199    fn skip_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
200    where
201        T: ?Sized + Serialize,
202    {
203        self.serialize_field(key, value)
204    }
205
206    fn pad_field<T>(&mut self, x86_pads: &T, x64_pads: &T) -> Result<()>
207    where
208        T: ?Sized + AsRef<[u8]>,
209    {
210        let pads = match self.ser.is_x86 {
211            true => x86_pads.as_ref(),
212            false => x64_pads.as_ref(),
213        };
214
215        if pads.is_empty() {
216            return Ok(());
217        };
218        self.ser.output.write_all(pads)?;
219        #[cfg(feature = "tracing")]
220        {
221            let pads_len = pads.len();
222            let current_position = self.ser.output.position();
223            tracing::trace!("padding: {pads_len} -> current position: {current_position:#x}");
224        }
225        Ok(())
226    }
227
228    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
229    // Fixed Array
230
231    #[inline]
232    fn serialize_fixed_array_field<V, T>(
233        &mut self,
234        _key: &'static str,
235        value: V,
236        size: TypeSize,
237    ) -> Result<()>
238    where
239        V: AsRef<[T]> + Serialize,
240        T: Serialize,
241    {
242        #[cfg(feature = "tracing")]
243        tracing::trace!(
244            "serialize FixedArray field({:#x}): {_key}",
245            self.ser.output.position()
246        );
247
248        if value.as_ref().is_empty() {
249            return Ok(());
250        }
251        let start_relative = tri!(self.ser.relative_position()); // Ptr type need to pointing data position(local.dst).
252        self.serialize_array_fixed(value, size, start_relative)
253    }
254
255    #[inline]
256    fn skip_fixed_array_field<V, T>(
257        &mut self,
258        key: &'static str,
259        value: V,
260        size: TypeSize,
261    ) -> Result<()>
262    where
263        V: AsRef<[T]> + Serialize,
264        T: Serialize,
265    {
266        self.serialize_fixed_array_field(key, value, size)
267    }
268
269    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
270    // Array
271
272    /// In the binary serialization of hkx, we are at this stage writing each field of the structure.
273    /// ptr type writes only the size of C++ `Array` here, since the data pointed to by the pointer
274    /// will be written later.
275    ///
276    /// That is, ptr(x86: 12bytes, x64: 16bytes).
277    fn serialize_array_field<V, T>(
278        &mut self,
279        _key: &'static str,
280        value: V,
281        size: TypeSize,
282    ) -> Result<()>
283    where
284        V: AsRef<[T]> + Serialize,
285        T: Serialize,
286    {
287        #[cfg(feature = "tracing")]
288        tracing::trace!(
289            "serialize Array field({:#x}): {_key}",
290            self.ser.output.position()
291        );
292
293        let local_src = tri!(self.ser.relative_position()); // Ptr type need to pointing data position(local.dst).
294        tri!(self.ser.serialize_ulong(Ulong::new(0))); // ptr size
295        let len = value.as_ref().len() as u32;
296        tri!(self.ser.serialize_uint32(len)); // array size
297        tri!(self.ser.serialize_uint32(len | (1 << 31))); // Capacity(same as size) | Owned flag(32nd bit)
298
299        if len == 0 {
300            return Ok(());
301        }
302        self.serialize_array_inner(value, size, local_src)
303    }
304
305    #[inline]
306    fn skip_array_field<V, T>(&mut self, key: &'static str, value: V, size: TypeSize) -> Result<()>
307    where
308        V: AsRef<[T]> + Serialize,
309        T: Serialize,
310    {
311        self.serialize_array_field(key, value, size)
312    }
313
314    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
315
316    #[inline]
317    fn end(self) -> Result<()> {
318        if self.is_root {
319            #[cfg(feature = "tracing")]
320            tracing::trace!("pointed_pos:({:#x?})", self.ser.pointed_pos);
321            self.ser.pointed_pos.clear();
322
323            #[cfg(feature = "tracing")]
324            tracing::trace!("current_last_pos:({:#x?})", self.ser.current_last_local_dst);
325            self.ser
326                .output
327                .set_position(self.ser.current_last_local_dst);
328        }
329        Ok(())
330    }
331}