Skip to main content

uorustlibs/map/
diff.rs

1//! Methods for reading patches over a map from mapdifl (the lookup file) and mapdif (the data).
2//! This module also provides methods for reading static patches from stadifl (lookup), stadifi (index) and stadif (data)
3//!
4//! For both types of lookup files, the format is simple
5//!
6//! `|lookups:[u32..file_size]|`
7//!
8//! Each lookup value represents a block index in the map file, while their index represents
9//! the appropriate index in the patch data files. In the static files, the index instead
10//! represents a lookup into the index file.
11//!
12//! mapdif files are internally structured as a list of blocks.
13//! stadif and stadifl are structured the same way as the static locations files.
14use super::shared::{Block, StaticLocation, read_block, read_block_statics};
15use crate::error::MulReaderResult;
16use crate::mul::MulReader;
17use byteorder::{LittleEndian, ReadBytesExt};
18use std::collections::HashMap;
19use std::fs::File;
20use std::io::{Read, Seek};
21use std::path::Path;
22
23fn generate_lookup_table<T: Read + Seek>(
24    data: &mut T,
25    length: u32,
26) -> MulReaderResult<HashMap<u32, u32>> {
27    let mut lookup_table = HashMap::new();
28
29    for i in 0..(length / 4) {
30        lookup_table.insert(data.read_u32::<LittleEndian>()?, i);
31    }
32
33    Ok(lookup_table)
34}
35
36/// A struct to help read out map blocks from a diff file
37#[derive(Debug)]
38pub struct MapDiffReader<T: Read + Seek> {
39    lookup_table: HashMap<u32, u32>,
40    diff: T,
41}
42
43impl MapDiffReader<File> {
44    /// Create a new MapDiffReader from an lookup path and mul path
45    pub fn new(lookup_path: &Path, diff_path: &Path) -> MulReaderResult<MapDiffReader<File>> {
46        // Start by reading all of the lookup info
47        let mut lookup = File::open(lookup_path)?;
48        let diff = File::open(diff_path)?;
49
50        let meta = lookup.metadata()?;
51        let lookup_table = generate_lookup_table(&mut lookup, meta.len() as u32)?;
52
53        Ok(MapDiffReader { lookup_table, diff })
54    }
55}
56
57impl<T: Read + Seek> MapDiffReader<T> {
58    /// Create a MapDiffReader from existing lookup and data readers
59    pub fn from_readable<U: Read + Seek>(
60        mut lookup_reader: U,
61        data_reader: T,
62        lookup_file_length: u32,
63    ) -> MulReaderResult<MapDiffReader<T>> {
64        let lookup_table = generate_lookup_table(&mut lookup_reader, lookup_file_length)?;
65
66        Ok(MapDiffReader {
67            lookup_table,
68            diff: data_reader,
69        })
70    }
71
72    /// Read a map block, if one exists
73    pub fn read(&mut self, idx: u32) -> Option<MulReaderResult<Block>> {
74        match self.lookup_table.get(&idx) {
75            Some(block_idx) => Some(read_block(&mut self.diff, *block_idx)),
76            None => None,
77        }
78    }
79
80    /// Read all map blocks
81    pub fn read_all(&mut self) -> HashMap<u32, MulReaderResult<Block>> {
82        let mut out = HashMap::new();
83        let keys = self.lookup_table.keys().copied().collect::<Vec<u32>>();
84        for map_idx in keys {
85            out.insert(
86                map_idx,
87                self.read(map_idx)
88                    .expect("Tried to read cached lookup that no longer exists"),
89            );
90        }
91        out
92    }
93}
94
95/// A struct to help read out static locations for a block from a diff file
96#[derive(Debug)]
97pub struct StaticLocationDiffReader<T: Read + Seek> {
98    mul_reader: MulReader<T>,
99    lookup_table: HashMap<u32, u32>,
100}
101
102impl StaticLocationDiffReader<File> {
103    /// Create a new StaticLocationDiffReader from a lookup path, an index path and mul path
104    pub fn new(
105        lookup_path: &Path,
106        diff_idx_path: &Path,
107        diff_path: &Path,
108    ) -> MulReaderResult<StaticLocationDiffReader<File>> {
109        let mut lookup = File::open(lookup_path)?;
110
111        let meta = lookup.metadata()?;
112        let lookup_table = generate_lookup_table(&mut lookup, meta.len() as u32)?;
113        let mul_reader = MulReader::new(diff_idx_path, diff_path)?;
114
115        Ok(StaticLocationDiffReader {
116            mul_reader,
117            lookup_table,
118        })
119    }
120}
121
122impl<T: Read + Seek> StaticLocationDiffReader<T> {
123    /// Create a StaticLocationDiffReader from existing lookup and mul readers
124    pub fn from_mul_reader<U: Read + Seek>(
125        mut lookup_reader: U,
126        mul_reader: MulReader<T>,
127        lookup_file_length: u32,
128    ) -> MulReaderResult<StaticLocationDiffReader<T>> {
129        let lookup_table = generate_lookup_table(&mut lookup_reader, lookup_file_length)?;
130
131        Ok(StaticLocationDiffReader {
132            mul_reader,
133            lookup_table,
134        })
135    }
136
137    /// Read statics for a map block, if they exist
138    pub fn read(&mut self, idx: u32) -> Option<MulReaderResult<Vec<StaticLocation>>> {
139        match self.lookup_table.get(&idx) {
140            Some(block_idx) => Some(read_block_statics(&mut self.mul_reader, *block_idx)),
141            None => None,
142        }
143    }
144
145    /// Read all static locations
146    pub fn read_all(&mut self) -> HashMap<u32, MulReaderResult<Vec<StaticLocation>>> {
147        let mut out = HashMap::new();
148        let keys = self.lookup_table.keys().copied().collect::<Vec<u32>>();
149        for map_idx in keys {
150            out.insert(
151                map_idx,
152                self.read(map_idx)
153                    .expect("Tried to read cached lookup that no longer exists"),
154            );
155        }
156        out
157    }
158}