wow_dbc/vanilla_tables/
chr_classes.rs

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