serde_hkx/bytes/de/parser/
type_kind.rs

1//! TypeKind bytes parsers
2use crate::{lib::*, tri};
3
4use super::BytesStream;
5use havok_types::*;
6use winnow::binary::{self, Endianness};
7use winnow::combinator::{alt, seq, terminated};
8use winnow::error::{ContextError, StrContext, StrContextValue};
9use winnow::token::take_until;
10use winnow::{ModalResult, Parser};
11
12/// Parses [`bool`]. `true` or `false``
13/// - The corresponding type kind: `Bool`
14///
15/// # Errors
16/// When parse failed.
17#[inline]
18pub fn boolean(input: &mut BytesStream<'_>) -> ModalResult<bool> {
19    alt((1.value(true), 0.value(false)))
20        .context(StrContext::Label("bool(u8)"))
21        .context(StrContext::Expected(StrContextValue::Description(
22            "`1` or `0`",
23        )))
24        .parse_next(input)
25}
26
27// Unsigned integers -> use `dec_unit`
28//   Signed integers -> use `dec_nit`
29
30/// Parses [`f32`](`Real`)
31#[inline]
32pub fn real<'a>(endian: Endianness) -> impl Parser<BytesStream<'a>, f32, ContextError> {
33    binary::f32(endian)
34        .context(StrContext::Label("real(f32)"))
35        .context(StrContext::Expected(StrContextValue::Description(
36            "Real(e.g. `00 00 80 3f`(0.1))",
37        )))
38}
39
40////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
41// Math
42
43/// Parse as [`Vector4`]
44pub fn vector4<'a>(endian: Endianness) -> impl Parser<BytesStream<'a>, Vector4, ContextError> {
45    seq!(Vector4 {
46        x: real(endian).context(StrContext::Label("x")),
47        y: real(endian).context(StrContext::Label("y")),
48        z: real(endian).context(StrContext::Label("z")),
49        w: real(endian).context(StrContext::Label("w")),
50    })
51    .context(StrContext::Label("Vector4"))
52}
53
54/// Parse as [`Quaternion`]
55#[inline]
56pub fn quaternion<'a>(
57    endian: Endianness,
58) -> impl Parser<BytesStream<'a>, Quaternion, ContextError> {
59    move |input: &mut &'a [u8]| {
60        let Vector4 { x, y, z, w } = tri!(vector4(endian).parse_next(input));
61        Ok(Quaternion { x, y, z, scaler: w })
62    }
63}
64
65pub fn matrix3<'a>(endian: Endianness) -> impl Parser<BytesStream<'a>, Matrix3, ContextError> {
66    seq!(Matrix3 {
67        x: vector4(endian).context(StrContext::Label("x")),
68        y: vector4(endian).context(StrContext::Label("y")),
69        z: vector4(endian).context(StrContext::Label("z")),
70    })
71    .context(StrContext::Label("Matrix3"))
72}
73
74pub fn rotation<'a>(endian: Endianness) -> impl Parser<BytesStream<'a>, Rotation, ContextError> {
75    seq!(Rotation {
76        x: vector4(endian).context(StrContext::Label("x")),
77        y: vector4(endian).context(StrContext::Label("y")),
78        z: vector4(endian).context(StrContext::Label("z")),
79    })
80    .context(StrContext::Label("Rotation"))
81}
82
83pub fn qstransform<'a>(
84    endian: Endianness,
85) -> impl Parser<BytesStream<'a>, QsTransform, ContextError> {
86    seq!(QsTransform {
87        transition: vector4(endian).context(StrContext::Label("transition")),
88        quaternion: quaternion(endian).context(StrContext::Label("quaternion")),
89        scale: vector4(endian).context(StrContext::Label("scale")),
90    })
91    .context(StrContext::Label("QsTransform"))
92}
93
94pub fn matrix4<'a>(endian: Endianness) -> impl Parser<BytesStream<'a>, Matrix4, ContextError> {
95    seq!(Matrix4 {
96        x: vector4(endian).context(StrContext::Label("x")),
97        y: vector4(endian).context(StrContext::Label("y")),
98        z: vector4(endian).context(StrContext::Label("z")),
99        w: vector4(endian).context(StrContext::Label("w")),
100    })
101    .context(StrContext::Label("Matrix4"))
102}
103
104pub fn transform<'a>(endian: Endianness) -> impl Parser<BytesStream<'a>, Transform, ContextError> {
105    seq!(Transform {
106        rotation: rotation(endian).context(StrContext::Label("rotation")),
107        transition: vector4(endian).context(StrContext::Label("transition")),
108    })
109    .context(StrContext::Label("Transform"))
110}
111
112////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
113
114// NOTE: No Pointer parsing exists because it is automatically created as an index.
115
116/// Parses f16
117pub fn half<'a>(endian: Endianness) -> impl Parser<BytesStream<'a>, f16, ContextError> {
118    move |bytes: &mut BytesStream<'a>| {
119        let (b0, b1) = tri!(
120            seq! {
121                binary::u8,
122                binary::u8,
123            }
124            .context(StrContext::Label("half(f16)"))
125            .context(StrContext::Expected(StrContextValue::Description(
126                "half(f16)",
127            )))
128            .parse_next(bytes)
129        );
130
131        Ok(match endian {
132            Endianness::Little => f16::from_le_bytes([b0, b1]),
133            Endianness::Big => f16::from_be_bytes([b0, b1]),
134            Endianness::Native => {
135                if cfg!(target_endian = "big") {
136                    f16::from_be_bytes([b0, b1])
137                } else {
138                    #[allow(clippy::tuple_array_conversions)]
139                    f16::from_le_bytes([b0, b1]) // if cfg!(target_endian = "little")
140                }
141            }
142        })
143    }
144}
145
146/// Parses a string literal until `\0`
147///
148/// # Errors
149/// If parse failed.
150pub fn string<'a>(input: &mut BytesStream<'a>) -> ModalResult<&'a str> {
151    terminated(take_until(0.., b'\0'), b'\0')
152        .try_map(|bytes| core::str::from_utf8(bytes))
153        .context(StrContext::Label("string"))
154        .context(StrContext::Expected(StrContextValue::Description(
155            "Valid ASCII string literal",
156        )))
157        .parse_next(input)
158}
159
160/// Parses ptr size(verify 0), size(`int` -> `usize`), and capacity(`int`).
161///
162/// # Returns
163/// (size, capacityAndFlags)
164pub fn array_meta<'a>(
165    is_x86: bool,
166    endian: Endianness,
167) -> impl Parser<BytesStream<'a>, (usize, i32), ContextError> {
168    move |bytes: &mut BytesStream<'a>| {
169        if is_x86 {
170            tri!(
171                binary::u32(endian)
172                    .verify(|uint| *uint == 0)
173                    .map(|uint| uint as u64)
174                    .context(StrContext::Expected(StrContextValue::Description(
175                        "Skip x86 ptr size(0 fill 4bytes)",
176                    )))
177                    .parse_next(bytes)
178            )
179        } else {
180            tri!(
181                binary::u64(endian)
182                    .verify(|ulong| *ulong == 0)
183                    .context(StrContext::Expected(StrContextValue::Description(
184                        "Skip x64 ptr size(0 fill 8bytes)",
185                    )))
186                    .parse_next(bytes)
187            )
188        };
189
190        seq! {
191            binary::i32(endian)
192            .map(|size| size as usize)
193            .context(StrContext::Expected(
194                StrContextValue::Description("size(i32)")
195            )),
196            binary::i32(endian)
197            .verify(|cap| (cap & (1 << 31)) != 0) // bit 32th flag is enabled
198            .context(
199                StrContext::Expected(StrContextValue::Description("capacity&flags(i32: e.g. 00 00 00 80)"))
200            )
201        }
202        .parse_next(bytes)
203    }
204}
205
206////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
207
208#[cfg(test)]
209mod tests {
210    use super::*;
211    use pretty_assertions::assert_eq;
212    use zerocopy::IntoBytes as _;
213
214    #[test]
215    fn test_boolean() {
216        assert_eq!(boolean.parse(&[1]), Ok(true));
217        assert_eq!(boolean.parse(&[0]), Ok(false));
218        assert!(boolean.parse(b"yes").is_err());
219    }
220
221    #[test]
222    fn test_vector4() {
223        assert_eq!(
224            vector4(Endianness::Little).parse([-0.0_f32, 0.0, -0.0, 1.0].as_bytes()),
225            Ok(Vector4::new(-0.0, 0.0, -0.0, 1.0))
226        );
227    }
228
229    #[test]
230    fn test_matrix3() {
231        assert_eq!(
232            matrix3(Endianness::Little).parse(
233                [
234                    0.0_f32, 0.0, 0.0, 0.0, // 1 vec4
235                    -0.0, 0.0, 1.0, 0.0, // 2 vec4
236                    1.0, 1.0, 0.0, 0.0, // 3 vec4
237                ]
238                .as_bytes()
239            ),
240            Ok(Matrix3 {
241                x: Vector4::default(),
242                y: Vector4 {
243                    x: -0.0,
244                    y: 0.0,
245                    z: 1.0,
246                    w: 0.0
247                },
248                z: Vector4 {
249                    x: 1.0,
250                    y: 1.0,
251                    z: 0.0,
252                    w: 0.0
253                }
254            })
255        );
256    }
257
258    #[test]
259    fn test_half() {
260        assert_eq!(
261            half(Endianness::Little).parse(&[0x80, 0x3f]),
262            Ok(f16::from_f32(1.0))
263        );
264        assert_eq!(
265            half(Endianness::Big).parse(&[0x3f, 0x80]),
266            Ok(f16::from_f32(1.0))
267        );
268    }
269
270    #[test]
271    fn test_string() {
272        assert_eq!(string.parse(b"example\0"), Ok("example"));
273        assert!(string.parse(b"example").is_err());
274    }
275}