1use alloc::{string::String, vec::Vec};
2
3use hashbrown::HashMap;
4use macros::generator;
5
6use crate::{
7 base::{ENTRY_SIZE, HEADER_SIZE, MAGIC},
8 util::ByteSliceExt,
9};
10
11use super::{Entry, ParseError, PkgState, RawFlags, ReadSeekRequest, Response};
12
13#[generator(static, yield ReadSeekRequest -> Response)]
14pub fn check_magic() -> bool {
15 for expected in MAGIC {
16 let byte = match request!(read 1)[..] {
17 [byte] => byte,
18 [] => return false,
19 _ => unreachable!(),
20 };
21
22 if byte != *expected {
23 return false;
24 }
25 }
26
27 true
28}
29
30#[generator(static, yield ReadSeekRequest -> Response)]
31pub fn parse(expect_magic: bool) -> Result<PkgState, ParseError> {
32 request!(rewind);
33
34 if expect_magic && !check_magic().await {
35 return Err(ParseError::MismatchedMagic);
36 }
37
38 let read = request!(read exact 4);
39
40 {
41 let header_size = read[0..2].as_u16_be();
42 if header_size as u64 != HEADER_SIZE {
43 return Err(ParseError::MismatchedHeaderSize { size: header_size });
44 }
45 }
46
47 {
48 let entry_size = read[2..4].as_u16_be();
49 if entry_size as u64 != ENTRY_SIZE {
50 return Err(ParseError::MismatchedEntrySize { size: entry_size });
51 }
52 }
53
54 let storage_len = request!(stream len);
55 let read = request!(read exact 8);
56 let entry_count = read[0..4].as_u32_be();
57 if (HEADER_SIZE + entry_count as u64 * ENTRY_SIZE) > storage_len {
58 return Err(ParseError::EntryOverflow);
59 }
60
61 let path_region_size = read[4..8].as_u32_be();
62 if (HEADER_SIZE + entry_count as u64 * ENTRY_SIZE) + path_region_size as u64 > storage_len {
63 return Err(ParseError::PathOverflow);
64 }
65
66 let mut entries = Vec::with_capacity(entry_count as usize);
67 let mut path_to_entry_index_map = HashMap::new();
68
69 for _ in 0..entry_count {
70 let read = request!(read exact 20);
71 let path_hash = read[0..4].as_u32_be();
72
73 let path_offset_and_flags = read[4..8].as_u32_be();
74 let path_offset = path_offset_and_flags & 0x00FFFFFF;
75 let flag_bits = path_offset_and_flags & 0xFF000000;
76 let flags =
77 RawFlags::from_bits(flag_bits).ok_or(ParseError::UnrecognisedEntryFlags(flag_bits))?;
78
79 let data_offset = read[8..12].as_u32_be();
80 let data_size = read[12..16].as_u32_be();
81 let unpacked_size = read[16..20].as_u32_be();
82
83 if data_offset == 0 {
84 entries.push(None)
85 } else {
86 entries.push(Some(Entry {
87 path_hash,
88 relative_path_offset: path_offset,
89 path: String::new(),
90 data_offset,
91 data_size,
92 unpacked_size,
93 flags,
94 }))
95 }
96 }
97
98 let read = request!(read exact path_region_size.into());
99 for (i, maybe_entry) in entries.iter_mut().enumerate() {
100 if let Some(entry) = maybe_entry {
101 let path = read[entry.relative_path_offset as usize..]
102 .iter()
103 .take_while(|b| **b != 0)
105 .map(|b| {
106 if !b.is_ascii() {
107 Err(ParseError::NonAsciiPath)
108 } else {
109 Ok(*b as char)
110 }
111 })
112 .try_collect::<String>()?;
113
114 entry.path = path.clone();
115 path_to_entry_index_map
116 .try_insert(path, i)
117 .map_err(|e| ParseError::SamePath(e.entry.key().clone()))?;
118 }
119 }
120
121 Ok(PkgState {
122 path_region_size,
123 path_region_empty_offset: path_region_size
124 .checked_add_signed(-((read.iter().rev().take_while(|b| **b == 0).count() as i32) - 1))
125 .unwrap(),
126 entries,
127 path_to_entry_index_map,
128 })
129}