wow_dbc/vanilla_tables/
chr_classes.rs1use 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 let id = ChrClassesKey::new(crate::util::read_u32_le(chunk)?);
58
59 let player_class = crate::util::read_u32_le(chunk)?;
61
62 let damage_bonus_stat = crate::util::read_i32_le(chunk)?;
64
65 let power_type = crate::util::read_i32_le(chunk)?.try_into()?;
67
68 let pet_name_token = {
70 let s = crate::util::get_string_as_vec(chunk, &string_block)?;
71 String::from_utf8(s)?
72 };
73
74 let name = crate::util::read_localized_string(chunk, &string_block)?;
76
77 let filename = {
79 let s = crate::util::get_string_as_vec(chunk, &string_block)?;
80 String::from_utf8(s)?
81 };
82
83 let class_mask = crate::util::read_i32_le(chunk)?;
85
86 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 b.write_all(&row.id.id.to_le_bytes())?;
120
121 b.write_all(&row.player_class.to_le_bytes())?;
123
124 b.write_all(&row.damage_bonus_stat.to_le_bytes())?;
126
127 b.write_all(&(row.power_type.as_int() as i32).to_le_bytes())?;
129
130 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 b.write_all(&row.name.string_indices_as_array(&mut string_index))?;
141
142 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 b.write_all(&row.class_mask.to_le_bytes())?;
153
154 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