wow_dbc/vanilla_tables/
map.rs

1use crate::{
2    DbcTable, Indexable, LocalizedString,
3};
4use crate::header::{
5    DbcHeader, HEADER_SIZE, parse_header,
6};
7use crate::vanilla_tables::area_table::AreaTableKey;
8use crate::vanilla_tables::loading_screens::LoadingScreensKey;
9use std::io::Write;
10use wow_world_base::vanilla::InstanceType;
11
12#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct Map {
14    pub rows: Vec<MapRow>,
15}
16
17impl DbcTable for Map {
18    type Row = MapRow;
19
20    const FILENAME: &'static str = "Map.dbc";
21
22    fn rows(&self) -> &[Self::Row] { &self.rows }
23    fn rows_mut(&mut self) -> &mut [Self::Row] { &mut self.rows }
24
25    fn read(b: &mut impl std::io::Read) -> Result<Self, crate::DbcError> {
26        let mut header = [0_u8; HEADER_SIZE];
27        b.read_exact(&mut header)?;
28        let header = parse_header(&header)?;
29
30        if header.record_size != 168 {
31            return Err(crate::DbcError::InvalidHeader(
32                crate::InvalidHeaderError::RecordSize {
33                    expected: 168,
34                    actual: header.record_size,
35                },
36            ));
37        }
38
39        if header.field_count != 42 {
40            return Err(crate::DbcError::InvalidHeader(
41                crate::InvalidHeaderError::FieldCount {
42                    expected: 42,
43                    actual: header.field_count,
44                },
45            ));
46        }
47
48        let mut r = vec![0_u8; (header.record_count * header.record_size) as usize];
49        b.read_exact(&mut r)?;
50        let mut string_block = vec![0_u8; header.string_block_size as usize];
51        b.read_exact(&mut string_block)?;
52
53        let mut rows = Vec::with_capacity(header.record_count as usize);
54
55        for mut chunk in r.chunks(header.record_size as usize) {
56            let chunk = &mut chunk;
57
58            // id: primary_key (Map) uint32
59            let id = MapKey::new(crate::util::read_u32_le(chunk)?);
60
61            // internal_name: string_ref
62            let internal_name = {
63                let s = crate::util::get_string_as_vec(chunk, &string_block)?;
64                String::from_utf8(s)?
65            };
66
67            // instance_type: InstanceType
68            let instance_type = crate::util::read_i32_le(chunk)?.try_into()?;
69
70            // battleground: bool32
71            let battleground = crate::util::read_u32_le(chunk)? != 0;
72
73            // map_name: string_ref_loc
74            let map_name = crate::util::read_localized_string(chunk, &string_block)?;
75
76            // min_level: int32
77            let min_level = crate::util::read_i32_le(chunk)?;
78
79            // max_level: int32
80            let max_level = crate::util::read_i32_le(chunk)?;
81
82            // max_players: int32
83            let max_players = crate::util::read_i32_le(chunk)?;
84
85            // unknown: int32[3]
86            let unknown = crate::util::read_array_i32::<3>(chunk)?;
87
88            // area_table: foreign_key (AreaTable) uint32
89            let area_table = AreaTableKey::new(crate::util::read_u32_le(chunk)?.into());
90
91            // map_description_horde: string_ref_loc
92            let map_description_horde = crate::util::read_localized_string(chunk, &string_block)?;
93
94            // map_description_alliance: string_ref_loc
95            let map_description_alliance = crate::util::read_localized_string(chunk, &string_block)?;
96
97            // loading_screen: foreign_key (LoadingScreens) uint32
98            let loading_screen = LoadingScreensKey::new(crate::util::read_u32_le(chunk)?.into());
99
100            // raid_offset: int32
101            let raid_offset = crate::util::read_i32_le(chunk)?;
102
103            // unknown_2: int32[2]
104            let unknown_2 = crate::util::read_array_i32::<2>(chunk)?;
105
106
107            rows.push(MapRow {
108                id,
109                internal_name,
110                instance_type,
111                battleground,
112                map_name,
113                min_level,
114                max_level,
115                max_players,
116                unknown,
117                area_table,
118                map_description_horde,
119                map_description_alliance,
120                loading_screen,
121                raid_offset,
122                unknown_2,
123            });
124        }
125
126        Ok(Map { rows, })
127    }
128
129    fn write(&self, b: &mut impl Write) -> Result<(), std::io::Error> {
130        let header = DbcHeader {
131            record_count: self.rows.len() as u32,
132            field_count: 42,
133            record_size: 168,
134            string_block_size: self.string_block_size(),
135        };
136
137        b.write_all(&header.write_header())?;
138
139        let mut string_index = 1;
140        for row in &self.rows {
141            // id: primary_key (Map) uint32
142            b.write_all(&row.id.id.to_le_bytes())?;
143
144            // internal_name: string_ref
145            if !row.internal_name.is_empty() {
146                b.write_all(&(string_index as u32).to_le_bytes())?;
147                string_index += row.internal_name.len() + 1;
148            }
149            else {
150                b.write_all(&(0_u32).to_le_bytes())?;
151            }
152
153            // instance_type: InstanceType
154            b.write_all(&(row.instance_type.as_int() as i32).to_le_bytes())?;
155
156            // battleground: bool32
157            b.write_all(&u32::from(row.battleground).to_le_bytes())?;
158
159            // map_name: string_ref_loc
160            b.write_all(&row.map_name.string_indices_as_array(&mut string_index))?;
161
162            // min_level: int32
163            b.write_all(&row.min_level.to_le_bytes())?;
164
165            // max_level: int32
166            b.write_all(&row.max_level.to_le_bytes())?;
167
168            // max_players: int32
169            b.write_all(&row.max_players.to_le_bytes())?;
170
171            // unknown: int32[3]
172            for i in row.unknown {
173                b.write_all(&i.to_le_bytes())?;
174            }
175
176
177            // area_table: foreign_key (AreaTable) uint32
178            b.write_all(&(row.area_table.id as u32).to_le_bytes())?;
179
180            // map_description_horde: string_ref_loc
181            b.write_all(&row.map_description_horde.string_indices_as_array(&mut string_index))?;
182
183            // map_description_alliance: string_ref_loc
184            b.write_all(&row.map_description_alliance.string_indices_as_array(&mut string_index))?;
185
186            // loading_screen: foreign_key (LoadingScreens) uint32
187            b.write_all(&(row.loading_screen.id as u32).to_le_bytes())?;
188
189            // raid_offset: int32
190            b.write_all(&row.raid_offset.to_le_bytes())?;
191
192            // unknown_2: int32[2]
193            for i in row.unknown_2 {
194                b.write_all(&i.to_le_bytes())?;
195            }
196
197
198        }
199
200        self.write_string_block(b)?;
201
202        Ok(())
203    }
204
205}
206
207impl Indexable for Map {
208    type PrimaryKey = MapKey;
209    fn get(&self, key: impl TryInto<Self::PrimaryKey>) -> Option<&Self::Row> {
210        let key = key.try_into().ok()?;
211        self.rows.iter().find(|a| a.id.id == key.id)
212    }
213
214    fn get_mut(&mut self, key: impl TryInto<Self::PrimaryKey>) -> Option<&mut Self::Row> {
215        let key = key.try_into().ok()?;
216        self.rows.iter_mut().find(|a| a.id.id == key.id)
217    }
218}
219
220impl Map {
221    fn write_string_block(&self, b: &mut impl Write) -> Result<(), std::io::Error> {
222        b.write_all(&[0])?;
223
224        for row in &self.rows {
225            if !row.internal_name.is_empty() { b.write_all(row.internal_name.as_bytes())?; b.write_all(&[0])?; };
226            row.map_name.string_block_as_array(b)?;
227            row.map_description_horde.string_block_as_array(b)?;
228            row.map_description_alliance.string_block_as_array(b)?;
229        }
230
231        Ok(())
232    }
233
234    fn string_block_size(&self) -> u32 {
235        let mut sum = 1;
236        for row in &self.rows {
237            if !row.internal_name.is_empty() { sum += row.internal_name.len() + 1; };
238            sum += row.map_name.string_block_size();
239            sum += row.map_description_horde.string_block_size();
240            sum += row.map_description_alliance.string_block_size();
241        }
242
243        sum as u32
244    }
245
246}
247
248#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, Default)]
249pub struct MapKey {
250    pub id: u32
251}
252
253impl MapKey {
254    pub const fn new(id: u32) -> Self {
255        Self { id }
256    }
257
258}
259
260impl From<u8> for MapKey {
261    fn from(v: u8) -> Self {
262        Self::new(v.into())
263    }
264}
265
266impl From<u16> for MapKey {
267    fn from(v: u16) -> Self {
268        Self::new(v.into())
269    }
270}
271
272impl From<u32> for MapKey {
273    fn from(v: u32) -> Self {
274        Self::new(v)
275    }
276}
277
278impl TryFrom<u64> for MapKey {
279    type Error = u64;
280    fn try_from(v: u64) -> Result<Self, Self::Error> {
281        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
282    }
283}
284
285impl TryFrom<usize> for MapKey {
286    type Error = usize;
287    fn try_from(v: usize) -> Result<Self, Self::Error> {
288        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
289    }
290}
291
292impl TryFrom<i8> for MapKey {
293    type Error = i8;
294    fn try_from(v: i8) -> Result<Self, Self::Error> {
295        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
296    }
297}
298
299impl TryFrom<i16> for MapKey {
300    type Error = i16;
301    fn try_from(v: i16) -> Result<Self, Self::Error> {
302        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
303    }
304}
305
306impl TryFrom<i32> for MapKey {
307    type Error = i32;
308    fn try_from(v: i32) -> Result<Self, Self::Error> {
309        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
310    }
311}
312
313impl TryFrom<i64> for MapKey {
314    type Error = i64;
315    fn try_from(v: i64) -> Result<Self, Self::Error> {
316        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
317    }
318}
319
320impl TryFrom<isize> for MapKey {
321    type Error = isize;
322    fn try_from(v: isize) -> Result<Self, Self::Error> {
323        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
324    }
325}
326
327#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
328pub struct MapRow {
329    pub id: MapKey,
330    pub internal_name: String,
331    pub instance_type: InstanceType,
332    pub battleground: bool,
333    pub map_name: LocalizedString,
334    pub min_level: i32,
335    pub max_level: i32,
336    pub max_players: i32,
337    pub unknown: [i32; 3],
338    pub area_table: AreaTableKey,
339    pub map_description_horde: LocalizedString,
340    pub map_description_alliance: LocalizedString,
341    pub loading_screen: LoadingScreensKey,
342    pub raid_offset: i32,
343    pub unknown_2: [i32; 2],
344}
345