1use std::collections::HashMap;
2
3use winnow::{
4 Parser,
5 binary::{self, Endianness},
6 error::{ContextError, StrContext, StrContextValue::*},
7 seq,
8 token::take_while,
9};
10
11use crate::{bytes::de::parser::type_kind::string, tri};
12
13pub type ClassNames<'a> = HashMap<u32, &'a str>;
16
17const FIXUP_VALUE_FOR_ALIGN: u32 = u32::MAX;
18
19pub fn classnames_section<'a>(
27 endian: Endianness,
28 base_offset: usize,
29) -> impl Parser<&'a [u8], ClassNames<'a>, ContextError> {
30 move |bytes: &mut &'a [u8]| {
31 let mut class_map = HashMap::new();
32 let mut offset = base_offset; while let Ok(_signature) = binary::u32::<&[u8], ContextError>(endian)
35 .verify(|src| *src != FIXUP_VALUE_FOR_ALIGN)
36 .context(StrContext::Expected(Description("local_fixup.src(u32)")))
37 .parse_next(bytes)
38 {
39 #[cfg(feature = "tracing")]
40 tracing::trace!("signature: {_signature:#x}");
41
42 let (class_name,) =tri!(seq! {
43 _: binary::u8::<&[u8], ContextError>
44 .verify(|byte| *byte == 0x9)
45 .context(StrContext::Expected(Description("class name separator(0x9)"))),
46 string .verify(|s: &str| s.is_ascii())
48 .context(StrContext::Expected(Description("ASCII class name(e.g. `hkReferencedObject`")))
49 }.parse_next(bytes));
50
51 offset += 5; #[cfg(feature = "tracing")]
53 tracing::trace!("class_name: {class_name}, offset: {offset}");
54 class_map.insert(offset as u32, class_name);
55
56 offset += class_name.len() + 1; }
59 tri!(take_while(0.., 0xff).parse_next(bytes)); Ok(class_map)
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::classnames_section;
67 use pretty_assertions::assert_eq;
68 use winnow::{Parser, binary::Endianness};
69
70 #[test]
71 fn should_parse_classnames() {
72 #[rustfmt::skip]
73 let bytes= &[
74 0xf6, 0x5e, 0x58, 0x75, 0x09, 0x68, 0x6b, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x00, 0xc2, 0xa4, 0x7e, 0x5c, 0x09, 0x68, 0x6b, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x00, 0xcf, 0x09, 0x36, 0x8a, 0x09, 0x68, 0x6b, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x75, 0x6d, 0x00, 0x6c, 0x8a, 0x6f, 0xce, 0x09, 0x68, 0x6b, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x75, 0x6d, 0x49, 0x74, 0x65, 0x6d, 0x00, 0x1e, 0xc1, 0x72, 0x27, 0x09, 0x68, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x00, 0xa7, 0x9b, 0xa3, 0x13, 0x09, 0x68, 0x6b, 0x62, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x61, 0x74, 0x61, 0x00, 0x0a, 0xd6, 0x6a, 0x07, 0x09, 0x68, 0x6b, 0x62, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x61, 0x00, 0xff, 0xff, ];
84
85 match classnames_section(Endianness::Little, 0).parse(bytes) {
86 Ok(class_map) => {
87 let expected_class_map = [
88 (5, "hkClass"),
89 (18, "hkClassMember"),
90 (37, "hkClassEnum"),
91 (54, "hkClassEnumItem"),
92 (75, "hkRootLevelContainer"),
93 (101, "hkbProjectData"),
94 (121, "hkbProjectStringData"),
95 ]
96 .into();
97 assert_eq!(class_map, expected_class_map);
98 }
99 Err(err) => panic!("{err}"),
100 }
101 }
102}