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