wow_dbc/tbc_tables/
creature_model_data.rs

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