ra2_mix/names_db/
mod.rs

1use crate::{checksum::ra2_crc, constants::XCC_HEADER_SIZE};
2use ra2_types::Ra2Error;
3use std::{collections::BTreeMap, ffi::OsStr, io::Write, path::Path};
4
5mod reader;
6mod writer;
7
8/// The reverse of the MIX database
9#[derive(Clone, Debug, Default)]
10pub struct MixDatabase {
11    map: BTreeMap<u32, String>,
12}
13
14impl MixDatabase {
15    pub fn decode(dat: &[u8]) -> Result<MixDatabase, Ra2Error> {
16        let mut out = MixDatabase::default();
17        let names = get_filenames_from_mix_db(dat);
18        names.into_iter().for_each(|name| out.add(name));
19        Ok(out)
20    }
21    pub fn load(path: &Path) -> Result<MixDatabase, Ra2Error> {
22        match path.extension() {
23            #[cfg(feature = "toml")]
24            Some(s) if s.eq("toml") => {
25                let text = std::fs::read_to_string(path)?;
26                Ok(Self { map: toml::from_str(&text)? })
27            }
28            _ => Ok(Self::decode(&std::fs::read(path)?)?),
29        }
30    }
31}
32
33impl MixDatabase {
34    #[cfg(feature = "serde_json")]
35    pub fn encode_json(self) -> Result<String, Ra2Error> {
36        Ok(serde_json::to_string(&self.map)?)
37    }
38
39    pub fn save(&self, path: &Path) -> Result<(), Ra2Error> {
40        match path.extension() {
41            #[cfg(feature = "toml")]
42            Some(s) if s.eq("toml") => {
43                let mut file = std::fs::File::create(path)?;
44                for (crc_id, filename) in &self.map {
45                    writeln!(file, "{} = {:?}", crc_id, filename)?;
46                }
47            }
48            _ => {}
49        }
50        Ok(())
51    }
52}
53
54impl MixDatabase {
55    pub fn get(&self, crc_id: u32) -> Option<&String> {
56        self.map.get(&crc_id)
57    }
58    pub fn add(&mut self, filename: String) {
59        self.map.insert(ra2_crc(&filename), filename);
60    }
61}
62
63/// Extracts filenames from a MIX database file
64fn get_filenames_from_mix_db(mix_db_file_data: &[u8]) -> Vec<String> {
65    let mut filenames = Vec::new();
66    let mut start = XCC_HEADER_SIZE;
67
68    while start < mix_db_file_data.len() {
69        let mut end = start;
70        while end < mix_db_file_data.len() && mix_db_file_data[end] != 0 {
71            end += 1;
72        }
73
74        if start < end {
75            if let Ok(filename) = std::str::from_utf8(&mix_db_file_data[start..end]) {
76                filenames.push(filename.to_string());
77            }
78        }
79
80        end += 1; // Skip the null terminator
81        start = end;
82    }
83
84    filenames
85}