wow_dbc/tbc_tables/
creature_display_info.rs

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