1use crate::{lib::*, tri};
5
6use byteorder::{ByteOrder, WriteBytesExt};
7use std::io;
8use winnow::{
9 Parser,
10 binary::{self, Endianness},
11 error::{ContextError, StrContext, StrContextValue::*},
12 seq,
13 token::take,
14};
15
16#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
29#[repr(C)]
30pub struct SectionHeader {
31 pub section_tag: [u8; 19],
46 pub section_tag_separator: u8,
48 pub absolute_data_start: u32,
57 pub local_fixups_offset: u32,
59 pub global_fixups_offset: u32,
61 pub virtual_fixups_offset: u32,
63
64 pub exports_offset: u32,
70 pub imports_offset: u32,
76 pub end_offset: u32,
82}
83static_assertions::assert_eq_size!(SectionHeader, [u8; 48]); impl SectionHeader {
86 pub const DATA_SECTION_HEADER_TAG: [u8; 19] = *b"__data__\0\0\0\0\0\0\0\0\0\0\0";
88
89 pub fn from_bytes<'a>(endian: Endianness) -> impl Parser<&'a [u8], Self, ContextError> {
90 move |bytes: &mut &[u8]| {
91 {
92 seq! {
93 Self {
94 section_tag: take(19_usize).try_map(TryFrom::try_from).context(StrContext::Label("section_tag"))
95 .context(StrContext::Expected(StringLiteral("[u8; 19]"))),
96 section_tag_separator: 0xff.context(StrContext::Label("section_tag_separator"))
97 .context(StrContext::Expected(StringLiteral("0xFF"))),
98 absolute_data_start: binary::u32(endian).context(StrContext::Label("absolute_data_start"))
99 .context(StrContext::Expected(StringLiteral("u32"))),
100 local_fixups_offset: binary::u32(endian).context(StrContext::Label("local_fixups_offset"))
101 .context(StrContext::Expected(StringLiteral("u32"))),
102 global_fixups_offset: binary::u32(endian).context(StrContext::Label("global_fixups_offset"))
103 .context(StrContext::Expected(StringLiteral("u32"))),
104 virtual_fixups_offset: binary::u32(endian).context(StrContext::Label("virtual_fixups_offset"))
105 .context(StrContext::Expected(StringLiteral("u32"))),
106 exports_offset: binary::u32(endian).context(StrContext::Label("exports_offset"))
107 .context(StrContext::Expected(StringLiteral("u32"))),
108 imports_offset: binary::u32(endian).context(StrContext::Label("imports_offset"))
109 .context(StrContext::Expected(StringLiteral("u32"))),
110 end_offset: binary::u32(endian).context(StrContext::Label("end_offset"))
111 .context(StrContext::Expected(StringLiteral("u32"))),
112 }
113 }
114 }
115 .context(StrContext::Label("Hkx Section Header"))
116 .parse_next(bytes)
117 }
118 }
119
120 pub fn write_bytes<O>(&self, mut writer: impl WriteBytesExt) -> io::Result<()>
127 where
128 O: ByteOrder,
129 {
130 writer.write_all(&self.section_tag)?;
131 writer.write_u8(self.section_tag_separator)?;
132 writer.write_u32::<O>(self.absolute_data_start)?;
133 writer.write_u32::<O>(self.local_fixups_offset)?;
134 writer.write_u32::<O>(self.global_fixups_offset)?;
135 writer.write_u32::<O>(self.virtual_fixups_offset)?;
136 writer.write_u32::<O>(self.exports_offset)?;
137 writer.write_u32::<O>(self.imports_offset)?;
138 writer.write_u32::<O>(self.end_offset)?;
139 Ok(())
140 }
141
142 pub fn write_classnames<O>(
153 mut writer: impl WriteBytesExt,
154 section_offset: i16,
155 section_end_abs: u32,
156 ) -> io::Result<()>
157 where
158 O: ByteOrder,
159 {
160 writer.write_all(b"__classnames__\0\0\0\0\0\xff")?; let section_offset = match section_offset {
162 i16::MIN..=0_i16 => 0,
163 1.. => section_offset as u32,
164 };
165 const ABSOLUTE_CLASSNAMES_OFFSET: u32 = 0xd0;
166 writer.write_u32::<O>(ABSOLUTE_CLASSNAMES_OFFSET + section_offset)?; let fixups_offset = section_end_abs - 0xd0;
169 writer.write_u32::<O>(fixups_offset)?; writer.write_u32::<O>(fixups_offset)?; writer.write_u32::<O>(fixups_offset)?; writer.write_u32::<O>(fixups_offset)?; writer.write_u32::<O>(fixups_offset)?; writer.write_u32::<O>(fixups_offset) }
176
177 pub fn write_types<O>(mut writer: impl WriteBytesExt, abs_offset: u32) -> io::Result<()>
186 where
187 O: ByteOrder,
188 {
189 writer.write_all(b"__types__\0\0\0\0\0\0\0\0\0\0\xff")?; writer.write_u32::<O>(abs_offset)?; tri!(writer.write_all([0_u8; 24].as_slice())); Ok(())
193 }
194}
195
196impl Display for SectionHeader {
198 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
199 let Self {
200 section_tag,
201 section_tag_separator,
202 absolute_data_start,
203 local_fixups_offset,
204 global_fixups_offset,
205 virtual_fixups_offset,
206 exports_offset,
207 imports_offset,
208 end_offset,
209 } = *self;
210
211 let section_tag = core::str::from_utf8(§ion_tag)
212 .unwrap_or_default()
213 .trim_matches(char::from(0));
214 let l_offset = absolute_data_start + local_fixups_offset;
215 let g_offset = absolute_data_start + global_fixups_offset;
216 let v_offset = absolute_data_start + virtual_fixups_offset;
217 let e_offset = absolute_data_start + exports_offset;
218 let i_offset = absolute_data_start + imports_offset;
219 let end_off = absolute_data_start + end_offset;
220
221 write!(
222 f,
223 r#"
224 section tag: {section_tag}
225section tag separator: {section_tag_separator:#02x}
226
227Offsets:
228 absolute data start: {absolute_data_start:#02x}
229 local fixups: {local_fixups_offset:#02x}
230 global fixups: {global_fixups_offset:#02x}
231 virtual fixups: {virtual_fixups_offset:#02x}
232 exports: {exports_offset:#02x}
233 imports; {imports_offset:#02x}
234 end: {end_offset:#02x}
235 abs + local: {l_offset:#02x}
236 abs + global: {g_offset:#02x}
237 abs + virtual: {v_offset:#02x}
238 abs + exports: {e_offset:#02x}
239 abs + imports: {i_offset:#02x}
240 abs + end: {end_off:#02x}
241"#
242 )
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249 use byteorder::LittleEndian;
250 use std::io::Cursor;
251
252 #[test]
253 fn test_write_classnames() -> io::Result<()> {
254 let mut buffer = Cursor::new(Vec::new());
255 SectionHeader::write_classnames::<LittleEndian>(&mut buffer, 0, 0x160)?;
256 let written = buffer.into_inner();
257
258 #[rustfmt::skip]
259 const CLASSNAMES_SECTION_HEADER: [u8; 48] = [
260 0x5F, 0x5F, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x6E, 0x61, 0x6D, 0x65, 0x73, 0x5F, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00,
262 0xff, 0xd0, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, ];
271 assert_eq!(&written, CLASSNAMES_SECTION_HEADER.as_slice());
272 Ok(())
273 }
274
275 #[test]
276 fn test_write_types() -> io::Result<()> {
277 let mut buffer = Cursor::new(Vec::new());
278 SectionHeader::write_types::<LittleEndian>(&mut buffer, 0x160)?;
279 let written = buffer.into_inner();
280
281 #[rustfmt::skip]
282 const TYPES_SECTION_HEADER: [u8; 48] = [
283 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x5f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
285 0xff, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
296 assert_eq!(&written, TYPES_SECTION_HEADER.as_slice());
297 Ok(())
298 }
299
300 #[test]
301 fn test_write_data() -> io::Result<()> {
302 let mut buffer = Cursor::new(Vec::new());
303 SectionHeader {
304 section_tag: SectionHeader::DATA_SECTION_HEADER_TAG,
305 section_tag_separator: 0xff,
306 absolute_data_start: 0x160,
307 local_fixups_offset: 0x170,
308 global_fixups_offset: 0x1C0,
309 virtual_fixups_offset: 0x1E0,
310 exports_offset: 0x210,
311 imports_offset: 0x210,
312 end_offset: 0x210,
313 }
314 .write_bytes::<LittleEndian>(&mut buffer)?;
315 let written = buffer.into_inner();
316
317 #[rustfmt::skip]
318 const DATA_SECTION_HEADER: [u8; 48] = [
319 0x5f, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x5f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
321 0xff, 0x60, 0x01, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, ];
332 assert_eq!(&written, DATA_SECTION_HEADER.as_slice());
333 Ok(())
334 }
335
336 #[test]
337 fn test_ref_from_bytes_invalid_length() {
338 let written = vec![0; 40];
339 let result =
340 SectionHeader::from_bytes(Endianness::Little).parse_next(&mut written.as_slice());
341 assert!(result.is_err());
342 }
343
344 #[test]
345 fn test_ref_from_bytes_invalid_separator() {
346 let mut written = vec![0; 48];
347 written[19] = 0x00; let result =
349 SectionHeader::from_bytes(Endianness::Little).parse_next(&mut written.as_slice());
350 assert!(result.is_err());
351 }
352}