wow_dbc/vanilla_tables/
sound_entries.rs

1use crate::{
2    DbcTable, Indexable,
3};
4use crate::header::{
5    DbcHeader, HEADER_SIZE, parse_header,
6};
7use std::io::Write;
8use wow_world_base::vanilla::SoundType;
9
10#[derive(Debug, Clone, PartialEq, PartialOrd)]
11pub struct SoundEntries {
12    pub rows: Vec<SoundEntriesRow>,
13}
14
15impl DbcTable for SoundEntries {
16    type Row = SoundEntriesRow;
17
18    const FILENAME: &'static str = "SoundEntries.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 != 116 {
29            return Err(crate::DbcError::InvalidHeader(
30                crate::InvalidHeaderError::RecordSize {
31                    expected: 116,
32                    actual: header.record_size,
33                },
34            ));
35        }
36
37        if header.field_count != 29 {
38            return Err(crate::DbcError::InvalidHeader(
39                crate::InvalidHeaderError::FieldCount {
40                    expected: 29,
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 (SoundEntries) uint32
57            let id = SoundEntriesKey::new(crate::util::read_u32_le(chunk)?);
58
59            // sound_type: SoundType
60            let sound_type = crate::util::read_i32_le(chunk)?.try_into()?;
61
62            // name: string_ref
63            let name = {
64                let s = crate::util::get_string_as_vec(chunk, &string_block)?;
65                String::from_utf8(s)?
66            };
67
68            // files: string_ref[10]
69            let files = {
70                let mut arr = Vec::with_capacity(10);
71                for _ in 0..10 {
72                    let i ={
73                        let s = crate::util::get_string_as_vec(chunk, &string_block)?;
74                        String::from_utf8(s)?
75                    };
76                    arr.push(i);
77                }
78
79                arr.try_into().unwrap()
80            };
81
82            // frequency: uint32[10]
83            let frequency = crate::util::read_array_u32::<10>(chunk)?;
84
85            // directory_base: string_ref
86            let directory_base = {
87                let s = crate::util::get_string_as_vec(chunk, &string_block)?;
88                String::from_utf8(s)?
89            };
90
91            // volume: float
92            let volume = crate::util::read_f32_le(chunk)?;
93
94            // flags: int32
95            let flags = crate::util::read_i32_le(chunk)?;
96
97            // min_distance: float
98            let min_distance = crate::util::read_f32_le(chunk)?;
99
100            // distance_cutoff: float
101            let distance_cutoff = crate::util::read_f32_le(chunk)?;
102
103            // sound_entries_advanced: int32
104            let sound_entries_advanced = crate::util::read_i32_le(chunk)?;
105
106
107            rows.push(SoundEntriesRow {
108                id,
109                sound_type,
110                name,
111                files,
112                frequency,
113                directory_base,
114                volume,
115                flags,
116                min_distance,
117                distance_cutoff,
118                sound_entries_advanced,
119            });
120        }
121
122        Ok(SoundEntries { rows, })
123    }
124
125    fn write(&self, b: &mut impl Write) -> Result<(), std::io::Error> {
126        let header = DbcHeader {
127            record_count: self.rows.len() as u32,
128            field_count: 29,
129            record_size: 116,
130            string_block_size: self.string_block_size(),
131        };
132
133        b.write_all(&header.write_header())?;
134
135        let mut string_index = 1;
136        for row in &self.rows {
137            // id: primary_key (SoundEntries) uint32
138            b.write_all(&row.id.id.to_le_bytes())?;
139
140            // sound_type: SoundType
141            b.write_all(&(row.sound_type.as_int() as i32).to_le_bytes())?;
142
143            // name: string_ref
144            if !row.name.is_empty() {
145                b.write_all(&(string_index as u32).to_le_bytes())?;
146                string_index += row.name.len() + 1;
147            }
148            else {
149                b.write_all(&(0_u32).to_le_bytes())?;
150            }
151
152            // files: string_ref[10]
153            for i in &row.files {
154                if !i.is_empty() {
155                    b.write_all(&(string_index as u32).to_le_bytes())?;
156                    string_index += i.len() + 1;
157                }
158                else {
159                    b.write_all(&(0_u32).to_le_bytes())?;
160                }
161            }
162
163
164            // frequency: uint32[10]
165            for i in row.frequency {
166                b.write_all(&i.to_le_bytes())?;
167            }
168
169
170            // directory_base: string_ref
171            if !row.directory_base.is_empty() {
172                b.write_all(&(string_index as u32).to_le_bytes())?;
173                string_index += row.directory_base.len() + 1;
174            }
175            else {
176                b.write_all(&(0_u32).to_le_bytes())?;
177            }
178
179            // volume: float
180            b.write_all(&row.volume.to_le_bytes())?;
181
182            // flags: int32
183            b.write_all(&row.flags.to_le_bytes())?;
184
185            // min_distance: float
186            b.write_all(&row.min_distance.to_le_bytes())?;
187
188            // distance_cutoff: float
189            b.write_all(&row.distance_cutoff.to_le_bytes())?;
190
191            // sound_entries_advanced: int32
192            b.write_all(&row.sound_entries_advanced.to_le_bytes())?;
193
194        }
195
196        self.write_string_block(b)?;
197
198        Ok(())
199    }
200
201}
202
203impl Indexable for SoundEntries {
204    type PrimaryKey = SoundEntriesKey;
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 SoundEntries {
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            if !row.name.is_empty() { b.write_all(row.name.as_bytes())?; b.write_all(&[0])?; };
222            for s in &row.files {
223                if !s.is_empty() { b.write_all(s.as_bytes())?; b.write_all(&[0])?; };
224            }
225
226            if !row.directory_base.is_empty() { b.write_all(row.directory_base.as_bytes())?; b.write_all(&[0])?; };
227        }
228
229        Ok(())
230    }
231
232    fn string_block_size(&self) -> u32 {
233        let mut sum = 1;
234        for row in &self.rows {
235            if !row.name.is_empty() { sum += row.name.len() + 1; };
236            for s in &row.files {
237                if !s.is_empty() { sum += s.len() + 1; };
238            }
239
240            if !row.directory_base.is_empty() { sum += row.directory_base.len() + 1; };
241        }
242
243        sum as u32
244    }
245
246}
247
248#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, Default)]
249pub struct SoundEntriesKey {
250    pub id: u32
251}
252
253impl SoundEntriesKey {
254    pub const fn new(id: u32) -> Self {
255        Self { id }
256    }
257
258}
259
260impl From<u8> for SoundEntriesKey {
261    fn from(v: u8) -> Self {
262        Self::new(v.into())
263    }
264}
265
266impl From<u16> for SoundEntriesKey {
267    fn from(v: u16) -> Self {
268        Self::new(v.into())
269    }
270}
271
272impl From<u32> for SoundEntriesKey {
273    fn from(v: u32) -> Self {
274        Self::new(v)
275    }
276}
277
278impl TryFrom<u64> for SoundEntriesKey {
279    type Error = u64;
280    fn try_from(v: u64) -> Result<Self, Self::Error> {
281        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
282    }
283}
284
285impl TryFrom<usize> for SoundEntriesKey {
286    type Error = usize;
287    fn try_from(v: usize) -> Result<Self, Self::Error> {
288        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
289    }
290}
291
292impl TryFrom<i8> for SoundEntriesKey {
293    type Error = i8;
294    fn try_from(v: i8) -> Result<Self, Self::Error> {
295        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
296    }
297}
298
299impl TryFrom<i16> for SoundEntriesKey {
300    type Error = i16;
301    fn try_from(v: i16) -> Result<Self, Self::Error> {
302        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
303    }
304}
305
306impl TryFrom<i32> for SoundEntriesKey {
307    type Error = i32;
308    fn try_from(v: i32) -> Result<Self, Self::Error> {
309        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
310    }
311}
312
313impl TryFrom<i64> for SoundEntriesKey {
314    type Error = i64;
315    fn try_from(v: i64) -> Result<Self, Self::Error> {
316        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
317    }
318}
319
320impl TryFrom<isize> for SoundEntriesKey {
321    type Error = isize;
322    fn try_from(v: isize) -> Result<Self, Self::Error> {
323        Ok(TryInto::<u32>::try_into(v).ok().ok_or(v)?.into())
324    }
325}
326
327#[derive(Debug, Clone, PartialEq, PartialOrd)]
328pub struct SoundEntriesRow {
329    pub id: SoundEntriesKey,
330    pub sound_type: SoundType,
331    pub name: String,
332    pub files: [String; 10],
333    pub frequency: [u32; 10],
334    pub directory_base: String,
335    pub volume: f32,
336    pub flags: i32,
337    pub min_distance: f32,
338    pub distance_cutoff: f32,
339    pub sound_entries_advanced: i32,
340}
341