nom_mpq/
builder.rs

1//! The MPQ Builder.
2//! Allows progressively creating the MPQ as the file is read.
3use crate::{MPQParserError, MPQResult};
4
5use super::parser::peek_hex;
6use super::{MPQBlockTableEntry, MPQFileHeader, MPQHashTableEntry, MPQHashType, MPQUserData, MPQ};
7use std::collections::HashMap;
8
9/// A builder for the MPQ parsing, allowing for building the archive progressively
10#[derive(Debug)]
11pub struct MPQBuilder {
12    /// Mandatory field, all MPQ Archives must have an archive header.
13    pub archive_header: Option<MPQFileHeader>,
14    /// Optional field, not all MPQ Archives have user_data.
15    /// The user_data contains in the case of Starcrat 2 Replay file,
16    /// the build information of the version of the game under whcich is played.
17    /// Different game versions would become different protocols/details, maybe
18    /// even units?
19    pub user_data: Option<MPQUserData>,
20    /// The MPQ Hash Table Entries content.
21    pub hash_table_entries: Vec<MPQHashTableEntry>,
22    /// The MPQ Block Table Entries content
23    pub block_table_entries: Vec<MPQBlockTableEntry>,
24    /// An encryption table to lookup, this is shared with the [`crate::MPQ`] object itself.
25    pub encryption_table: HashMap<u32, u32>,
26}
27
28impl Default for MPQBuilder {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl MPQBuilder {
35    /// Initializes the Builder, internally creates the encryption table.
36    pub fn new() -> Self {
37        Self {
38            archive_header: None,
39            user_data: None,
40            hash_table_entries: vec![],
41            block_table_entries: vec![],
42            encryption_table: MPQ::prepare_encryption_table(),
43        }
44    }
45
46    /// Sets the archive header field
47    pub fn with_archive_header(mut self, archive_header: MPQFileHeader) -> Self {
48        self.archive_header = Some(archive_header);
49        self
50    }
51
52    /// Sets the user data field
53    pub fn with_user_data(mut self, user_data: Option<MPQUserData>) -> Self {
54        self.user_data = user_data;
55        self
56    }
57
58    /// Sets the hash table entries
59    pub fn with_hash_table(mut self, entries: Vec<MPQHashTableEntry>) -> Self {
60        self.hash_table_entries = entries;
61        self
62    }
63
64    /// Sets the block table entries
65    pub fn with_block_table(mut self, entries: Vec<MPQBlockTableEntry>) -> Self {
66        self.block_table_entries = entries;
67        self
68    }
69
70    /// Performs mpq string hashing using the encryption table.
71    pub fn mpq_string_hash(
72        &self,
73        location: &str,
74        hash_type: MPQHashType,
75    ) -> Result<u32, MPQParserError> {
76        MPQ::mpq_string_hash(&self.encryption_table, location, hash_type)
77    }
78
79    /// Uses the encryption table and key to decrypt some bytes
80    #[tracing::instrument(level = "trace", skip(self, data))]
81    pub fn mpq_data_decrypt<'a>(
82        &'a self,
83        data: &'a [u8],
84        key: u32,
85    ) -> MPQResult<&'a [u8], Vec<u8>> {
86        tracing::trace!("Encrypted: {:?}", peek_hex(data));
87        let (tail, res) = MPQ::mpq_data_decrypt(&self.encryption_table, data, key)?;
88        tracing::trace!("Decrypted: {:?}", peek_hex(&res));
89        Ok((tail, res))
90    }
91
92    /// Consumes self and turns into the final MPQ.
93    pub fn build(self, _orig_input: &[u8]) -> Result<MPQ, MPQParserError> {
94        let archive_header = self
95            .archive_header
96            .ok_or(MPQParserError::MissingArchiveHeader)?;
97        let user_data = self.user_data;
98        let hash_table_entries = self.hash_table_entries;
99        let block_table_entries = self.block_table_entries;
100        let encryption_table = self.encryption_table;
101        Ok(MPQ {
102            archive_header,
103            user_data,
104            hash_table_entries,
105            block_table_entries,
106            encryption_table,
107        })
108    }
109}