1use bitflags::bitflags;
27use byteorder::{LittleEndian, ReadBytesExt};
28use std::fs::File;
29use std::io::{Read, Seek, SeekFrom};
30use std::path::Path;
31use std::str::from_utf8;
32
33use crate::error::MulReaderResult;
34
35bitflags! {
36 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38 pub struct Flags: u32 {
39 const BackgroundFlag = 0x00000001;
40 const WeaponFlag = 0x00000002;
41 const TransparentFlag = 0x00000004;
42 const TranslucentFlag = 0x00000008;
43 const WallFlag = 0x00000010;
44 const DamagingFlag = 0x00000020;
45 const ImpassableFlag = 0x00000040;
46 const WetFlag = 0x00000080;
47 const UnknownFlag = 0x00000100;
48 const SurfaceFlag = 0x00000200;
49 const BridgeFlag = 0x00000400;
50 const StackableFlag = 0x00000800;
51 const WindowFlag = 0x00001000;
52 const NoShootFlag = 0x00002000;
53 const PrefixAFlag = 0x00004000;
54 const PrefixAnFlag = 0x00008000;
55 const InternalFlag = 0x00010000;
56 const FoliageFlag = 0x00020000;
57 const PartialHueFlag = 0x00040000;
58 const Unknown1Flag = 0x00080000;
59 const MapFlag = 0x00100000;
60 const ContainerFlag = 0x00200000;
61 const WearableFlag = 0x00400000;
62 const LightSourceFlag = 0x00800000;
63 const AnimatedFlag = 0x01000000;
64 const NoDiagonalFlag = 0x02000000;
65 const Unknown2Flag = 0x04000000;
66 const ArmorFlag = 0x08000000;
67 const RoofFlag = 0x10000000;
68 const DoorFlag = 0x20000000;
69 const StairBackFlag = 0x40000000;
70 const StairRightFlag = 0x80000000;
71 }
72}
73
74const GROUP_HEADER_SIZE: u32 = 4;
77const MAP_TILE_SIZE: u32 = 26;
78const STATIC_TILE_SIZE: u32 = 37;
79const STATIC_OFFSET: u32 = 428032;
80
81#[derive(Debug, PartialEq, Eq, Clone)]
83pub struct MapTileData {
84 pub flags: Flags,
85 pub texture_id: u16,
87 pub name: String,
88}
89
90#[derive(Debug, PartialEq, Eq, Clone)]
92pub struct StaticTileData {
93 pub flags: Flags,
94 pub weight: u8,
95 pub quality_layer_light_id: u8,
97 pub quantity_weapon_class_armor_class: u8,
99 pub anim_id: u16,
100 pub hue: u8,
101 pub height_capacity: u8,
103 pub name: String,
104}
105
106#[derive(Debug)]
108pub struct TileDataReader<T: Read + Seek> {
109 data_reader: T,
110}
111
112impl TileDataReader<File> {
113 pub fn new(mul_path: &Path) -> MulReaderResult<TileDataReader<File>> {
115 let data_reader = File::open(mul_path)?;
116
117 Ok(TileDataReader { data_reader })
118 }
119}
120
121impl<T: Read + Seek> TileDataReader<T> {
122 pub fn from_readable(reader: T) -> TileDataReader<T> {
124 TileDataReader {
125 data_reader: reader,
126 }
127 }
128
129 pub fn read_map_tile_data(&mut self, idx: u32) -> MulReaderResult<MapTileData> {
133 let offset = self.calculate_map_tile_offset(idx);
134 self.data_reader.seek(SeekFrom::Start(offset))?;
135 let flags = Flags::from_bits(self.data_reader.read_u32::<LittleEndian>()?)
136 .unwrap_or(Flags::empty());
137 let texture_id = self.data_reader.read_u16::<LittleEndian>()?;
138
139 let mut raw_name = vec![];
140 loop {
141 match self.data_reader.read_u8()? {
142 0 => break,
143 x => raw_name.push(x),
144 }
145 }
146
147 Ok(MapTileData {
148 flags,
149 texture_id,
150 name: String::from(from_utf8(&raw_name).unwrap_or("ERROR")),
151 })
152 }
153
154 fn calculate_map_tile_offset(&self, idx: u32) -> u64 {
155 let group_header_jumps = ((idx / 32) + 1) * GROUP_HEADER_SIZE;
157 ((idx * MAP_TILE_SIZE) + group_header_jumps) as u64
158 }
159
160 pub fn read_static_tile_data(&mut self, idx: u32) -> MulReaderResult<StaticTileData> {
164 let offset = self.calculate_static_tile_offset(idx);
165 self.data_reader.seek(SeekFrom::Start(offset))?;
166
167 let flags = Flags::from_bits(self.data_reader.read_u32::<LittleEndian>()?)
168 .unwrap_or(Flags::empty());
169 let weight = self.data_reader.read_u8()?;
170 let quality = self.data_reader.read_u8()?;
171 let _unknown = self.data_reader.read_u16::<LittleEndian>()?;
172 let _unknown1 = self.data_reader.read_u8()?;
173 let quantity = self.data_reader.read_u8()?;
174 let anim_id = self.data_reader.read_u16::<LittleEndian>()?;
175 let _unknown2 = self.data_reader.read_u8()?;
176 let hue = self.data_reader.read_u8()?;
177 let _unknown3 = self.data_reader.read_u16::<LittleEndian>()?;
178 let height = self.data_reader.read_u8()?;
179
180 let mut raw_name = vec![];
181 loop {
182 match self.data_reader.read_u8()? {
183 0 => break,
184 x => raw_name.push(x),
185 }
186 }
187
188 Ok(StaticTileData {
189 flags,
190 weight,
191 quality_layer_light_id: quality,
192 quantity_weapon_class_armor_class: quantity,
193 anim_id,
194 hue,
195 height_capacity: height,
196 name: String::from(from_utf8(&raw_name).unwrap_or("ERROR")),
197 })
198 }
199
200 fn calculate_static_tile_offset(&self, idx: u32) -> u64 {
201 let group_header_jumps = ((idx / 32) + 1) * GROUP_HEADER_SIZE;
203 ((idx * STATIC_TILE_SIZE) + group_header_jumps + STATIC_OFFSET) as u64
204 }
205}