wow_dbc/wrath_tables/
item_display_info.rs

1use crate::{
2    DbcTable, Indexable,
3};
4use crate::header::{
5    DbcHeader, HEADER_SIZE, parse_header,
6};
7use crate::wrath_tables::particle_color::ParticleColorKey;
8use crate::wrath_tables::spell_visual::SpellVisualKey;
9use std::io::Write;
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct ItemDisplayInfo {
13    pub rows: Vec<ItemDisplayInfoRow>,
14}
15
16impl DbcTable for ItemDisplayInfo {
17    type Row = ItemDisplayInfoRow;
18
19    const FILENAME: &'static str = "ItemDisplayInfo.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 != 100 {
30            return Err(crate::DbcError::InvalidHeader(
31                crate::InvalidHeaderError::RecordSize {
32                    expected: 100,
33                    actual: header.record_size,
34                },
35            ));
36        }
37
38        if header.field_count != 25 {
39            return Err(crate::DbcError::InvalidHeader(
40                crate::InvalidHeaderError::FieldCount {
41                    expected: 25,
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 (ItemDisplayInfo) int32
58            let id = ItemDisplayInfoKey::new(crate::util::read_i32_le(chunk)?);
59
60            // model_name: string_ref[2]
61            let model_name = {
62                let mut arr = Vec::with_capacity(2);
63                for _ in 0..2 {
64                    let i ={
65                        let s = crate::util::get_string_as_vec(chunk, &string_block)?;
66                        String::from_utf8(s)?
67                    };
68                    arr.push(i);
69                }
70
71                arr.try_into().unwrap()
72            };
73
74            // model_texture: string_ref[2]
75            let model_texture = {
76                let mut arr = Vec::with_capacity(2);
77                for _ in 0..2 {
78                    let i ={
79                        let s = crate::util::get_string_as_vec(chunk, &string_block)?;
80                        String::from_utf8(s)?
81                    };
82                    arr.push(i);
83                }
84
85                arr.try_into().unwrap()
86            };
87
88            // inventory_icon: string_ref[2]
89            let inventory_icon = {
90                let mut arr = Vec::with_capacity(2);
91                for _ in 0..2 {
92                    let i ={
93                        let s = crate::util::get_string_as_vec(chunk, &string_block)?;
94                        String::from_utf8(s)?
95                    };
96                    arr.push(i);
97                }
98
99                arr.try_into().unwrap()
100            };
101
102            // geoset_group: int32[3]
103            let geoset_group = crate::util::read_array_i32::<3>(chunk)?;
104
105            // flags: int32
106            let flags = crate::util::read_i32_le(chunk)?;
107
108            // spell_visual_id: foreign_key (SpellVisual) int32
109            let spell_visual_id = SpellVisualKey::new(crate::util::read_i32_le(chunk)?.into());
110
111            // group_sound_index: int32
112            let group_sound_index = crate::util::read_i32_le(chunk)?;
113
114            // helmet_geoset_vis_id: int32[2]
115            let helmet_geoset_vis_id = crate::util::read_array_i32::<2>(chunk)?;
116
117            // texture: string_ref[8]
118            let texture = {
119                let mut arr = Vec::with_capacity(8);
120                for _ in 0..8 {
121                    let i ={
122                        let s = crate::util::get_string_as_vec(chunk, &string_block)?;
123                        String::from_utf8(s)?
124                    };
125                    arr.push(i);
126                }
127
128                arr.try_into().unwrap()
129            };
130
131            // item_visual: int32
132            let item_visual = crate::util::read_i32_le(chunk)?;
133
134            // particle_color_id: foreign_key (ParticleColor) int32
135            let particle_color_id = ParticleColorKey::new(crate::util::read_i32_le(chunk)?.into());
136
137
138            rows.push(ItemDisplayInfoRow {
139                id,
140                model_name,
141                model_texture,
142                inventory_icon,
143                geoset_group,
144                flags,
145                spell_visual_id,
146                group_sound_index,
147                helmet_geoset_vis_id,
148                texture,
149                item_visual,
150                particle_color_id,
151            });
152        }
153
154        Ok(ItemDisplayInfo { rows, })
155    }
156
157    fn write(&self, b: &mut impl Write) -> Result<(), std::io::Error> {
158        let header = DbcHeader {
159            record_count: self.rows.len() as u32,
160            field_count: 25,
161            record_size: 100,
162            string_block_size: self.string_block_size(),
163        };
164
165        b.write_all(&header.write_header())?;
166
167        let mut string_index = 1;
168        for row in &self.rows {
169            // id: primary_key (ItemDisplayInfo) int32
170            b.write_all(&row.id.id.to_le_bytes())?;
171
172            // model_name: string_ref[2]
173            for i in &row.model_name {
174                if !i.is_empty() {
175                    b.write_all(&(string_index as u32).to_le_bytes())?;
176                    string_index += i.len() + 1;
177                }
178                else {
179                    b.write_all(&(0_u32).to_le_bytes())?;
180                }
181            }
182
183
184            // model_texture: string_ref[2]
185            for i in &row.model_texture {
186                if !i.is_empty() {
187                    b.write_all(&(string_index as u32).to_le_bytes())?;
188                    string_index += i.len() + 1;
189                }
190                else {
191                    b.write_all(&(0_u32).to_le_bytes())?;
192                }
193            }
194
195
196            // inventory_icon: string_ref[2]
197            for i in &row.inventory_icon {
198                if !i.is_empty() {
199                    b.write_all(&(string_index as u32).to_le_bytes())?;
200                    string_index += i.len() + 1;
201                }
202                else {
203                    b.write_all(&(0_u32).to_le_bytes())?;
204                }
205            }
206
207
208            // geoset_group: int32[3]
209            for i in row.geoset_group {
210                b.write_all(&i.to_le_bytes())?;
211            }
212
213
214            // flags: int32
215            b.write_all(&row.flags.to_le_bytes())?;
216
217            // spell_visual_id: foreign_key (SpellVisual) int32
218            b.write_all(&(row.spell_visual_id.id as i32).to_le_bytes())?;
219
220            // group_sound_index: int32
221            b.write_all(&row.group_sound_index.to_le_bytes())?;
222
223            // helmet_geoset_vis_id: int32[2]
224            for i in row.helmet_geoset_vis_id {
225                b.write_all(&i.to_le_bytes())?;
226            }
227
228
229            // texture: string_ref[8]
230            for i in &row.texture {
231                if !i.is_empty() {
232                    b.write_all(&(string_index as u32).to_le_bytes())?;
233                    string_index += i.len() + 1;
234                }
235                else {
236                    b.write_all(&(0_u32).to_le_bytes())?;
237                }
238            }
239
240
241            // item_visual: int32
242            b.write_all(&row.item_visual.to_le_bytes())?;
243
244            // particle_color_id: foreign_key (ParticleColor) int32
245            b.write_all(&(row.particle_color_id.id as i32).to_le_bytes())?;
246
247        }
248
249        self.write_string_block(b)?;
250
251        Ok(())
252    }
253
254}
255
256impl Indexable for ItemDisplayInfo {
257    type PrimaryKey = ItemDisplayInfoKey;
258    fn get(&self, key: impl TryInto<Self::PrimaryKey>) -> Option<&Self::Row> {
259        let key = key.try_into().ok()?;
260        self.rows.iter().find(|a| a.id.id == key.id)
261    }
262
263    fn get_mut(&mut self, key: impl TryInto<Self::PrimaryKey>) -> Option<&mut Self::Row> {
264        let key = key.try_into().ok()?;
265        self.rows.iter_mut().find(|a| a.id.id == key.id)
266    }
267}
268
269impl ItemDisplayInfo {
270    fn write_string_block(&self, b: &mut impl Write) -> Result<(), std::io::Error> {
271        b.write_all(&[0])?;
272
273        for row in &self.rows {
274            for s in &row.model_name {
275                if !s.is_empty() { b.write_all(s.as_bytes())?; b.write_all(&[0])?; };
276            }
277
278            for s in &row.model_texture {
279                if !s.is_empty() { b.write_all(s.as_bytes())?; b.write_all(&[0])?; };
280            }
281
282            for s in &row.inventory_icon {
283                if !s.is_empty() { b.write_all(s.as_bytes())?; b.write_all(&[0])?; };
284            }
285
286            for s in &row.texture {
287                if !s.is_empty() { b.write_all(s.as_bytes())?; b.write_all(&[0])?; };
288            }
289
290        }
291
292        Ok(())
293    }
294
295    fn string_block_size(&self) -> u32 {
296        let mut sum = 1;
297        for row in &self.rows {
298            for s in &row.model_name {
299                if !s.is_empty() { sum += s.len() + 1; };
300            }
301
302            for s in &row.model_texture {
303                if !s.is_empty() { sum += s.len() + 1; };
304            }
305
306            for s in &row.inventory_icon {
307                if !s.is_empty() { sum += s.len() + 1; };
308            }
309
310            for s in &row.texture {
311                if !s.is_empty() { sum += s.len() + 1; };
312            }
313
314        }
315
316        sum as u32
317    }
318
319}
320
321#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, Default)]
322pub struct ItemDisplayInfoKey {
323    pub id: i32
324}
325
326impl ItemDisplayInfoKey {
327    pub const fn new(id: i32) -> Self {
328        Self { id }
329    }
330
331}
332
333impl From<u8> for ItemDisplayInfoKey {
334    fn from(v: u8) -> Self {
335        Self::new(v.into())
336    }
337}
338
339impl From<u16> for ItemDisplayInfoKey {
340    fn from(v: u16) -> Self {
341        Self::new(v.into())
342    }
343}
344
345impl From<i8> for ItemDisplayInfoKey {
346    fn from(v: i8) -> Self {
347        Self::new(v.into())
348    }
349}
350
351impl From<i16> for ItemDisplayInfoKey {
352    fn from(v: i16) -> Self {
353        Self::new(v.into())
354    }
355}
356
357impl From<i32> for ItemDisplayInfoKey {
358    fn from(v: i32) -> Self {
359        Self::new(v)
360    }
361}
362
363impl TryFrom<u32> for ItemDisplayInfoKey {
364    type Error = u32;
365    fn try_from(v: u32) -> Result<Self, Self::Error> {
366        Ok(TryInto::<i32>::try_into(v).ok().ok_or(v)?.into())
367    }
368}
369
370impl TryFrom<usize> for ItemDisplayInfoKey {
371    type Error = usize;
372    fn try_from(v: usize) -> Result<Self, Self::Error> {
373        Ok(TryInto::<i32>::try_into(v).ok().ok_or(v)?.into())
374    }
375}
376
377impl TryFrom<u64> for ItemDisplayInfoKey {
378    type Error = u64;
379    fn try_from(v: u64) -> Result<Self, Self::Error> {
380        Ok(TryInto::<i32>::try_into(v).ok().ok_or(v)?.into())
381    }
382}
383
384impl TryFrom<i64> for ItemDisplayInfoKey {
385    type Error = i64;
386    fn try_from(v: i64) -> Result<Self, Self::Error> {
387        Ok(TryInto::<i32>::try_into(v).ok().ok_or(v)?.into())
388    }
389}
390
391impl TryFrom<isize> for ItemDisplayInfoKey {
392    type Error = isize;
393    fn try_from(v: isize) -> Result<Self, Self::Error> {
394        Ok(TryInto::<i32>::try_into(v).ok().ok_or(v)?.into())
395    }
396}
397
398#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
399pub struct ItemDisplayInfoRow {
400    pub id: ItemDisplayInfoKey,
401    pub model_name: [String; 2],
402    pub model_texture: [String; 2],
403    pub inventory_icon: [String; 2],
404    pub geoset_group: [i32; 3],
405    pub flags: i32,
406    pub spell_visual_id: SpellVisualKey,
407    pub group_sound_index: i32,
408    pub helmet_geoset_vis_id: [i32; 2],
409    pub texture: [String; 8],
410    pub item_visual: i32,
411    pub particle_color_id: ParticleColorKey,
412}
413