serde_hkx/xml/ser/
mod.rs

1//! XML Serialization
2use crate::{lib::*, tri};
3
4use crate::errors::ser::{Error, Result};
5use havok_serde::ser::{
6    Serialize, SerializeFlags, SerializeSeq, SerializeStruct, Serializer, TypeSize,
7};
8use havok_types::variant::Variant;
9use havok_types::{
10    CString, Matrix3, Matrix4, Pointer, QsTransform, Quaternion, Rotation, Signature, StringPtr,
11    Transform, Ulong, Vector4, f16,
12};
13
14#[derive(Debug)]
15pub struct XmlSerializer {
16    /// XML string
17    output: String,
18    /// Indent type(tab, space)
19    indent: &'static str,
20    /// Indent time
21    depth: usize,
22    /// If you want to output XML partially, put [`Option::None`].
23    /// # Example XML
24    /// ```xml
25    /// <?xml version="1.0" encoding="ascii"?>
26    /// <hkpackfile classversion="8" contentsversion="hk_2010.2.0-r1" toplevelobject=""
27    /// ```
28    start_root: Option<&'static str>,
29    /// If you want to output XML partially, put [`Option::None`].
30    /// # Example XML
31    /// ```xml
32    /// </hkpackfile>
33    /// ```
34    end_root: Option<&'static str>,
35}
36
37impl Default for XmlSerializer {
38    fn default() -> Self {
39        Self {
40            output: String::new(),
41            indent: "\t",
42            depth: 0,
43            start_root: Some(
44                r###"<?xml version="1.0" encoding="ascii"?>
45<hkpackfile classversion="8" contentsversion="hk_2010.2.0-r1" toplevelobject=""###,
46            ),
47            end_root: Some("</hkpackfile>\n"),
48        }
49    }
50}
51
52/// To XML String.
53///
54/// # Errors
55/// serde(fork version) Error defined on crate's trace definition, but will not fail due to mere XML stringing.
56#[inline]
57pub fn to_string<T>(value: &T, top_ptr: usize) -> Result<String>
58where
59    T: Serialize,
60{
61    to_string_with_opt(value, top_ptr, XmlSerializer::default())
62}
63
64/// To xml string with custom `XmlSerializer` settings.
65///
66/// # Errors
67/// serde(fork version) Error defined on crate's trace definition, but will not fail due to mere XML stringing.
68///
69/// # Info
70/// This can be done in partial mode by eliminating the root string.
71pub fn to_string_with_opt<T>(value: &T, top_ptr: usize, ser: XmlSerializer) -> Result<String>
72where
73    T: Serialize,
74{
75    let mut serializer = ser;
76
77    if let Some(start_root) = serializer.start_root {
78        serializer.output += start_root;
79        serializer.output += &Pointer::new(top_ptr).to_string();
80        serializer.output += "\">\n\n";
81        serializer.increment_depth();
82        serializer.indent();
83        serializer.output += "<hksection name=\"__data__\">\n";
84        serializer.increment_depth();
85    };
86
87    tri!(value.serialize(&mut serializer));
88
89    if let Some(end_root) = serializer.end_root {
90        serializer.decrement_depth();
91        serializer.output += "\n";
92        serializer.indent();
93        serializer.output += "</hksection>";
94        serializer.decrement_depth();
95        serializer.output += "\n\n";
96        serializer.output += end_root;
97    };
98    Ok(serializer.output)
99}
100
101impl Serializer for &mut XmlSerializer {
102    type Ok = ();
103    type Error = Error;
104
105    type SerializeSeq = Self;
106    type SerializeStruct = Self;
107    type SerializeFlags = Self;
108
109    #[inline]
110    fn serialize_void(self, _: ()) -> Result<Self::Ok> {
111        Ok(())
112    }
113
114    #[inline]
115    fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
116        self.output += if v { "true" } else { "false" };
117        Ok(())
118    }
119
120    #[inline]
121    fn serialize_char(self, v: char) -> Result<Self::Ok> {
122        self.output.push(v);
123        Ok(())
124    }
125
126    #[inline]
127    fn serialize_int8(self, v: i8) -> Result<Self::Ok> {
128        self.serialize_int64(v as i64)
129    }
130
131    #[inline]
132    fn serialize_uint8(self, v: u8) -> Result<Self::Ok> {
133        self.serialize_uint64(v as u64)
134    }
135
136    #[inline]
137    fn serialize_int16(self, v: i16) -> Result<Self::Ok> {
138        self.serialize_int64(v as i64)
139    }
140
141    #[inline]
142    fn serialize_uint16(self, v: u16) -> Result<Self::Ok> {
143        self.serialize_uint64(v as u64)
144    }
145
146    #[inline]
147    fn serialize_int32(self, v: i32) -> Result<Self::Ok> {
148        self.serialize_int64(v as i64)
149    }
150
151    #[inline]
152    fn serialize_uint32(self, v: u32) -> Result<Self::Ok> {
153        self.serialize_uint64(v as u64)
154    }
155
156    #[inline]
157    fn serialize_int64(self, v: i64) -> Result<Self::Ok> {
158        self.output += &v.to_string();
159        Ok(())
160    }
161
162    #[inline]
163    fn serialize_uint64(self, v: u64) -> Result<Self::Ok> {
164        self.output += &v.to_string();
165        Ok(())
166    }
167
168    #[inline]
169    fn serialize_real(self, v: f32) -> Result<Self::Ok> {
170        self.output += &format!("{v:.06}");
171        Ok(())
172    }
173
174    #[inline]
175    fn serialize_vector4(self, v: &Vector4) -> Result<Self::Ok> {
176        self.output += &v.to_string();
177        Ok(())
178    }
179
180    #[inline]
181    fn serialize_quaternion(self, v: &Quaternion) -> Result<Self::Ok> {
182        self.output += &v.to_string();
183        Ok(())
184    }
185
186    #[inline]
187    fn serialize_matrix3(self, v: &Matrix3) -> Result<Self::Ok> {
188        self.output += &v.to_string();
189        Ok(())
190    }
191
192    #[inline]
193    fn serialize_rotation(self, v: &Rotation) -> Result<Self::Ok> {
194        self.output += &v.to_string();
195        Ok(())
196    }
197
198    #[inline]
199    fn serialize_qstransform(self, v: &QsTransform) -> Result<Self::Ok> {
200        self.output += &v.to_string();
201        Ok(())
202    }
203
204    #[inline]
205    fn serialize_matrix4(self, v: &Matrix4) -> Result<Self::Ok> {
206        self.output += &v.to_string();
207        Ok(())
208    }
209
210    #[inline]
211    fn serialize_transform(self, v: &Transform) -> Result<Self::Ok> {
212        self.output += &v.to_string();
213        Ok(())
214    }
215
216    #[inline]
217    fn serialize_pointer(self, v: Pointer) -> Result<Self::Ok> {
218        if v.get() == 0 {
219            self.output += "null"; // Null pointer
220        } else {
221            self.output += &v.to_string();
222        }
223        Ok(())
224    }
225
226    #[inline]
227    fn serialize_array(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
228        Ok(self)
229    }
230
231    /// Create an XML string like the following
232    /// ```xml
233    /// <hkobject name="#0010" class="hkbProjectData" signature="0x13a39ba7">
234    ///   <!-- memSizeAndFlags SERIALIZE_IGNORED -->
235    ///   <!-- referenceCount SERIALIZE_IGNORED -->
236    ///   <hkparam name="worldUpWS">(0.000000 0.000000 1.000000 0.000000)</hkparam>
237    ///   <hkparam name="stringData">#0009</hkparam>
238    ///   <hkparam name="defaultEventMode">EVENT_MODE_IGNORE_FROM_GENERATOR</hkparam>
239    /// </hkobject>
240    /// ```
241    fn serialize_struct(
242        self,
243        name: &'static str,
244        class_meta: Option<(Pointer, Signature)>,
245        _sizes: (u64, u64),
246    ) -> Result<Self::SerializeStruct> {
247        if let Some((ptr_name, sig)) = class_meta {
248            self.output += "\n";
249            self.indent();
250            self.output +=
251                &format!("<hkobject name=\"{ptr_name}\" class=\"{name}\" signature=\"{sig}\">\n");
252        } else {
253            // If there is a line break in the class of a single field, but it is impossible to
254            // determine whether it is a class or not within the serialize method of a single
255            // field, so if there is no line break here, it is processed as class processing
256            // within the field.
257            if !self.output.ends_with('\n') {
258                self.output += "\n";
259                self.increment_depth();
260            };
261            self.indent();
262            self.output += "<hkobject>\n"; // If ptr & signature are not provided, the class is considered to be an in-field class. (e.g. `Array<hkRootContainerNamedVariant>`)
263        }
264
265        self.increment_depth(); // entered <hkparam>(each fields process). so we increment indent.
266        Ok(self)
267    }
268
269    /// FIXME: Unclear XML representation
270    #[inline]
271    fn serialize_variant(self, v: &Variant) -> Result<Self::Ok> {
272        tri!(self.serialize_pointer(v.object));
273        self.serialize_pointer(v.class)
274    }
275
276    #[inline]
277    fn serialize_cstring(self, v: &CString) -> Result<Self::Ok> {
278        if let Some(s) = v.get_ref() {
279            self.output += &html_escape::encode_text(s);
280        } else {
281            // null is &#9216: https://www.compart.com/en/unicode/U+2400
282            self.output += "&#9216;";
283        };
284        Ok(())
285    }
286
287    #[inline]
288    fn serialize_ulong(self, v: Ulong) -> Result<Self::Ok> {
289        self.output += &v.to_string();
290        Ok(())
291    }
292
293    #[inline]
294    fn serialize_enum_flags(self) -> Result<Self::SerializeFlags> {
295        Ok(self)
296    }
297
298    #[inline]
299    fn serialize_half(self, v: f16) -> Result<Self::Ok> {
300        self.output += &format!("{v:.06}");
301        Ok(())
302    }
303
304    #[inline]
305    fn serialize_stringptr(self, v: &StringPtr) -> Result<Self::Ok> {
306        if let Some(s) = v.get_ref() {
307            self.output += &html_escape::encode_text(s);
308        } else {
309            // null is &#9216: https://www.compart.com/en/unicode/U+2400
310            self.output += "&#9216;";
311        };
312        Ok(())
313    }
314}
315
316impl XmlSerializer {
317    /// Do indentation by `self.depth`.
318    #[inline]
319    fn indent(&mut self) {
320        match self.depth {
321            // Heap alloc optimizations
322            0 => (),                                             // 0 alloc
323            1 => self.output += self.indent,                     // 1 time alloc
324            _ => self.output += &self.indent.repeat(self.depth), // 2 time alloc
325        }
326    }
327
328    /// Increment `self.depth` for indentation.
329    #[inline]
330    const fn increment_depth(&mut self) {
331        self.depth += 1;
332    }
333
334    /// Decrement `self.depth` for indentation.
335    #[inline]
336    const fn decrement_depth(&mut self) {
337        self.depth -= 1;
338    }
339}
340
341impl SerializeSeq for &mut XmlSerializer {
342    type Ok = ();
343    type Error = Error;
344
345    /// # Expected XML Examples
346    ///
347    /// - `hkArray<hkInt8>`(same as ptr, hkReal, etc...)
348    /// ```xml
349    /// <hkparam name="key" numelements="20">
350    ///     0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
351    ///     16 17 18 19 20
352    /// </hkparam>
353    /// ```
354    fn serialize_primitive_element<T>(
355        &mut self,
356        value: &T,
357        index: usize,
358        len: usize,
359    ) -> Result<(), Self::Error>
360    where
361        T: ?Sized + havok_serde::ser::Serialize,
362    {
363        if index == 0 {
364            self.indent();
365        };
366        tri!(value.serialize(&mut **self));
367
368        if index + 1 == len {
369            // Align the closing tag of `</hkparam>` by breaking the line at the end of the output,
370            // regardless of whether it is every 16 or not.
371            self.output.push('\n');
372            return Ok(());
373        } else if (index + 1) % 16 == 0 {
374            self.output.push('\n'); // After 16 outputs, indent and make 16 columns.
375            self.indent();
376        } else {
377            self.output.push(' '); // add space each element.
378        }
379        Ok(())
380    }
381
382    #[inline]
383    fn serialize_class_element<T>(&mut self, value: &T) -> Result<()>
384    where
385        T: ?Sized + Serialize,
386    {
387        tri!(value.serialize(&mut **self));
388        self.output += "\n";
389        Ok(())
390    }
391
392    fn serialize_math_element<T>(&mut self, value: &T) -> Result<()>
393    where
394        T: ?Sized + Serialize,
395    {
396        self.indent();
397        tri!(value.serialize(&mut **self));
398        self.output += "\n";
399        Ok(())
400    }
401
402    /// # XML Examples
403    ///
404    /// ```xml
405    ///     <hkcstring>CString</hkcstring>
406    /// ```
407    fn serialize_cstring_element(&mut self, value: &CString) -> Result<()> {
408        self.indent();
409        self.output += "<hkcstring>";
410        tri!(value.serialize(&mut **self));
411        self.output += "</hkcstring>\n";
412        Ok(())
413    }
414
415    /// # XML Examples
416    ///
417    /// ```xml
418    ///     <hkcstring>StringPtr</hkcstring>
419    /// ```
420    fn serialize_stringptr_element(&mut self, value: &StringPtr) -> Result<()> {
421        self.indent();
422        self.output += "<hkcstring>";
423        tri!(value.serialize(&mut **self));
424        self.output += "</hkcstring>\n";
425        Ok(())
426    }
427
428    #[inline]
429    fn end(self) -> Result<()> {
430        Ok(())
431    }
432}
433
434impl SerializeStruct for &mut XmlSerializer {
435    type Ok = ();
436    type Error = Error;
437
438    /// # XML Examples
439    ///
440    /// ```xml
441    /// <hkparam name="key1">value</hkparam>
442    /// ```
443    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
444    where
445        T: ?Sized + Serialize,
446    {
447        self.indent();
448        self.output += "<hkparam name=\"";
449        self.output += key;
450        self.output += "\">";
451
452        tri!(value.serialize(&mut **self));
453
454        // The class in the field is currently having line break processing problems,
455        // so it is processed to format it well. from `serialize_struct`
456        if self.output.ends_with("</hkobject>") {
457            self.output += "\n";
458            self.decrement_depth();
459            self.indent();
460        };
461        self.output += "</hkparam>\n";
462        Ok(())
463    }
464
465    #[inline]
466    fn serialize_fixed_array_field<V, T>(
467        &mut self,
468        key: &'static str,
469        value: V,
470        size: TypeSize,
471    ) -> std::result::Result<(), Self::Error>
472    where
473        V: AsRef<[T]> + Serialize,
474        T: Serialize,
475    {
476        SerializeStruct::serialize_array_field(self, key, value, size)
477    }
478
479    /// # XML Examples
480    ///
481    /// - `hkArray<hkInt8>`(same as ptr, hkReal, etc...)
482    /// ```xml
483    /// <hkparam name="key" numelements="20">
484    ///     0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
485    ///     16 17 18 19 20
486    /// </hkparam>
487    /// ```
488    ///
489    /// - `hkArray<hkStringPtr>`
490    /// ```xml
491    /// <hkparam name="key" numelements="3">
492    ///     <hkcstring>StringPtr1</hkcstring>
493    ///     <hkcstring>StringPtr2</hkcstring>
494    ///     <hkcstring>StringPtr3</hkcstring>
495    /// </hkparam>
496    /// ```
497    ///
498    /// - `hkArray<Vector4>`
499    /// ```xml
500    /// <hkparam name="key" numelements="2">
501    ///     (0.000000 0.000000 0.000000 0.000000)
502    ///     (0.000000 0.000000 0.000000 0.000000)
503    /// </hkparam>
504    /// ```
505    fn serialize_array_field<V, T>(
506        &mut self,
507        key: &'static str,
508        value: V,
509        _size: TypeSize,
510    ) -> Result<()>
511    where
512        V: AsRef<[T]> + Serialize,
513        T: Serialize,
514    {
515        self.indent();
516        self.output += "<hkparam name=\"";
517        self.output += key;
518
519        let array = value.as_ref();
520        if array.is_empty() {
521            self.output += "\" numelements=\"0\"></hkparam>\n";
522            return Ok(());
523        };
524
525        let len = array.len();
526        self.output += &format!("\" numelements=\"{len}\">\n");
527        self.increment_depth();
528        tri!(value.serialize(&mut **self));
529        self.decrement_depth();
530        self.indent();
531        self.output += "</hkparam>\n";
532        Ok(())
533    }
534
535    /// # XML Examples
536    ///
537    /// ```xml
538    /// <!-- key SERIALIZE_IGNORED --><!-- This is skip_field -->
539    /// <hkparam name="otherKey"></hkparam>
540    /// ```
541    #[inline]
542    fn skip_field<T>(&mut self, key: &'static str, _: &T) -> Result<()>
543    where
544        T: ?Sized + Serialize,
545    {
546        self.indent();
547        self.output += &format!("<!-- {key} SERIALIZE_IGNORED -->\n");
548        Ok(())
549    }
550
551    #[inline]
552    fn end(self) -> Result<()> {
553        self.decrement_depth();
554        self.indent();
555        self.output += "</hkobject>";
556        Ok(())
557    }
558}
559
560impl SerializeFlags for &mut XmlSerializer {
561    type Ok = ();
562    type Error = Error;
563
564    /// e.g. <hkparam>0</hkparam>
565    #[inline]
566    fn serialize_empty_bit(&mut self) -> Result<(), Self::Error> {
567        self.output += "0";
568        Ok(())
569    }
570
571    #[inline]
572    fn serialize_field<T>(&mut self, key: &str, _value: &T) -> Result<(), Self::Error>
573    where
574        T: ?Sized + Serialize,
575    {
576        // Always prefix all flags except the first with `|` to indicate an OR operation.
577        // e.g. <hkparam>EXAMPLE|EXAMPLE</hkparam>
578        if !self.output.ends_with('>') {
579            self.output += "|";
580        }
581        self.output += key;
582
583        Ok(())
584    }
585
586    #[inline]
587    fn end(self) -> Result<Self::Ok, Self::Error> {
588        Ok(())
589    }
590}
591
592#[cfg(test)]
593mod tests {
594    use super::*;
595    use crate::{HavokSort as _, tests::mocks::new_defaultmale};
596    use pretty_assertions::assert_eq;
597
598    #[ignore = "No error on local PC Windows, but for some reason error occurs on GitHub Actions Windows"]
599    #[test]
600    fn test_serialize_defaultmale() -> Result<()> {
601        let mut classes = new_defaultmale();
602        let top_ptr = classes.sort_for_xml()?; // hkRootContainer" is processed last.
603
604        let actual = tri!(to_string(&classes, top_ptr));
605        let expected =
606            include_str!("../../../../docs/handson_hex_dump/defaultmale/defaultmale_x86.xml");
607
608        assert_eq!(actual, expected);
609        Ok(())
610    }
611}