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}