Skip to main content

uorustlibs/map/
mod.rs

1//! Methods for reading map files from the various map muls
2//!
3//! Maps are stored as single, unindexed files. They contain a sequence of blocks:
4//!
5//! `|checksum:u32|cells:[Block..64]`
6//!
7//! And blocks are stored as
8//!
9//! `|graphic:u16|altitude:i8|`
10//!
11//! You need to know the dimensions of the map to read it correctly;
12//! some of these are stored in `map_size`
13use std::fs::File;
14use std::io::{Read, Seek};
15use std::path::Path;
16
17pub mod diff;
18pub mod radarcol;
19#[cfg(not(test))]
20mod shared;
21#[cfg(test)]
22pub mod shared;
23pub mod static_location;
24
25use crate::error::{MulReaderError, MulReaderResult};
26use crate::map::diff::MapDiffReader;
27use crate::map::shared::read_block;
28pub use crate::map::shared::{Block, Cell, StaticLocation};
29
30/// Constants for map sizes, in blocks
31pub mod map_size {
32    pub const SOSARIA: (u32, u32) = (896, 512);
33    pub const ILSHENAR: (u32, u32) = (288, 200);
34    pub const MALAS: (u32, u32) = (320, 256);
35    pub const TOKUNO: (u32, u32) = (181, 181);
36    pub const TER_MUR: (u32, u32) = (160, 512);
37}
38
39/// A struct to help read out Map data
40///
41/// The methods on this struct optionally take a MapDiffReader,
42/// to make it easier to apply patches to a map
43#[derive(Debug)]
44pub struct MapReader<T: Read + Seek> {
45    data_reader: T,
46    /// Width, in blocks
47    width: u32,
48    /// Height, in blocks
49    height: u32,
50}
51
52impl MapReader<File> {
53    /// Create a new MapReader from a mul path
54    pub fn new(
55        map_path: &Path,
56        width_blocks: u32,
57        height_blocks: u32,
58    ) -> MulReaderResult<MapReader<File>> {
59        let data_reader = File::open(map_path)?;
60
61        Ok(MapReader {
62            data_reader,
63            width: width_blocks,
64            height: height_blocks,
65        })
66    }
67}
68
69impl<T: Read + Seek> MapReader<T> {
70    /// Create a MapReader from an existing readable
71    pub fn from_readable(data_reader: T, width_blocks: u32, height_blocks: u32) -> MapReader<T> {
72        MapReader {
73            data_reader,
74            width: width_blocks,
75            height: height_blocks,
76        }
77    }
78
79    /// Read a block from the map by its id
80    /// Blocks are stored in columns, from top of the map to to bottom
81    pub fn read_block<U: Read + Seek>(
82        &mut self,
83        id: u32,
84        patch: Option<&mut MapDiffReader<U>>,
85    ) -> MulReaderResult<Block> {
86        match patch {
87            Some(reader) => reader
88                .read(id)
89                .unwrap_or_else(|| read_block(&mut self.data_reader, id)),
90            None => read_block(&mut self.data_reader, id),
91        }
92    }
93
94    /// Read a block from the map by its absolute coordinates
95    pub fn read_block_from_coordinates<U: Read + Seek>(
96        &mut self,
97        x: u32,
98        y: u32,
99        patch: Option<&mut MapDiffReader<U>>,
100    ) -> MulReaderResult<Block> {
101        let width = self.width;
102        let height = self.height;
103        if x < width && y < height {
104            self.read_block(y + (x * height), patch)
105        } else {
106            Err(MulReaderError::CoordinatesOutOfBounds { x, y })
107        }
108    }
109}