nom_mpq/parser/mpq_block_table_entry.rs
1//! The Block Table Parsing
2//!
3//! The block table contains entries for each region in the archive.
4//! Regions may be either files, empty space, which may be overwritten by new
5//! files (typically this space is from deleted file data), or unused block
6//! table entries.
7//! Empty space entries should have:
8//! - BlockOffset and BlockSize nonzero.
9//! - FileSize and Flags zero;
10//! Unused block table entries should have:
11//! - BlockSize, FileSize, and Flags zero.
12//! The block table is encrypted, using the hash of "(block table)" as the key.
13//! NOTES:
14//! - MPyQ uses struct_format: `'4I'`
15
16use super::LITTLE_ENDIAN;
17use crate::dbg_dmp;
18use nom::number::complete::u32;
19use nom::*;
20
21/// The block tables of the MPQ archive, they are stored sequentially and encrypted.
22#[derive(Debug, PartialEq, Default, Clone)]
23pub struct MPQBlockTableEntry {
24 /// Block Offset, an offset of the beginning of the block,
25 /// relative to the beginning of the archive header, this can
26 /// be used in conjunction with `fseek` to start reading a specific
27 /// block. In the case of this crate, since the contents are in memory
28 /// already, it's used as `data[(archive_header_offset + self.offset)..]`.
29 /// This is because the first section of the archive may be the UserData
30 /// section so the archive header offset must be known.
31 pub offset: u32,
32 /// Size of the block in the archive.
33 pub archived_size: u32,
34 /// Size of the file data stored in the block.
35 /// see [`MPQBlockTableEntry::parse_size`] for more information.
36 pub size: u32,
37 /// Bit mask of the flags for the block,
38 /// see [`MPQBlockTableEntry::parse_flags`] for more information.
39 pub flags: u32,
40}
41
42impl MPQBlockTableEntry {
43 /// This method is not related to parsing but for testing, maybe we should consider further
44 /// splitting this into a MPQBlockTableEntryParser, maybe overkill.
45 pub fn new(offset: u32, archived_size: u32, size: u32, flags: u32) -> Self {
46 Self {
47 offset,
48 archived_size,
49 size,
50 flags,
51 }
52 }
53
54 /// Parses all the fields in the expected order
55 pub fn parse(input: &[u8]) -> IResult<&[u8], Self> {
56 let (tail, offset) = Self::parse_offset(input)?;
57 let (tail, archived_size) = Self::parse_archived_size(tail)?;
58 let (tail, size) = Self::parse_size(tail)?;
59 let (tail, flags) = Self::parse_flags(tail)?;
60 Ok((
61 tail,
62 MPQBlockTableEntry {
63 offset,
64 archived_size,
65 size,
66 flags,
67 },
68 ))
69 }
70
71 /// `Offset 0x00`: int32 BlockOffset
72 ///
73 /// Offset of the beginning of the block, relative to the beginning of the
74 /// archive header.
75 pub fn parse_offset(input: &[u8]) -> IResult<&[u8], u32> {
76 dbg_dmp(u32(LITTLE_ENDIAN), "offset")(input)
77 }
78
79 /// `Offset 0x04`: int32 BlockSize
80 ///
81 /// Size of the block in the archive.
82 pub fn parse_archived_size(input: &[u8]) -> IResult<&[u8], u32> {
83 dbg_dmp(u32(LITTLE_ENDIAN), "archive_size")(input)
84 }
85
86 /// `Offset 0x08`: int32 FileSize
87 ///
88 /// Size of the file data stored in the block. Only valid if the block is
89 /// a file; otherwise meaningless, and should be 0.
90 /// If the file is compressed, this is the size of the uncompressed
91 /// file data.
92 pub fn parse_size(input: &[u8]) -> IResult<&[u8], u32> {
93 dbg_dmp(u32(LITTLE_ENDIAN), "size")(input)
94 }
95
96 /// `Offset 0x0c`: int32 Flags
97 ///
98 /// Bit mask of the flags for the block.
99 /// The following values are conclusively identified:
100 /// - `0x80000000` Block is a file, and follows the file data format;
101 /// otherwise, block is free space or unused.
102 /// If the block is not a file, all other flags should be
103 /// cleared, and FileSize should be 0.
104 /// - `0x04000000` File has checksums for each sector (explained in the
105 /// File Data section). Ignored if file is not compressed
106 /// or imploded.
107 /// - `0x02000000` File is a deletion marker, indicating that the file no
108 /// longer exists. This is used to allow patch archives to
109 /// delete files present in lower-priority archives in the
110 /// search chain.
111 /// - `0x01000000` File is stored as a single unit, rather than split into
112 /// sectors.
113 /// - `0x00020000` The file's encryption key is adjusted by the block offset
114 /// and file size (explained in detail in the File Data
115 /// section). File must be encrypted.
116 /// - `0x00010000` File is encrypted.
117 /// - `0x00000200` File is compressed. File cannot be imploded.
118 /// - `0x00000100` File is imploded. File cannot be compressed.
119 pub fn parse_flags(input: &[u8]) -> IResult<&[u8], u32> {
120 dbg_dmp(u32(LITTLE_ENDIAN), "flags")(input)
121 }
122}