wow_vanilla_dbc/
lib.rs

1//! DEPRECATED. USE [wow_dbc](https://github.com/gtker/wow_vanilla_dbc) INSTEAD.
2
3#![forbid(unsafe_code)]
4// This requires fields knowing about the sizes of enums
5#![allow(clippy::useless_conversion)]
6#![warn(
7    clippy::perf,
8    clippy::correctness,
9    clippy::style,
10    clippy::missing_const_for_fn,
11    clippy::missing_errors_doc,
12    clippy::missing_panics_doc,
13    clippy::doc_markdown,
14    clippy::unseparated_literal_suffix,
15    missing_docs
16)]
17
18use std::io::{Read, Write};
19
20pub(crate) mod error;
21pub(crate) mod header;
22#[allow(missing_docs)]
23pub mod tables;
24mod util;
25
26pub use error::*;
27
28/// DBCs from the English version of the game will only have English version strings, while other localizations will have other languages.
29///
30/// You are most likely interested in, [`LocalizedString::en_gb`], the English version.
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct LocalizedString {
33    /// English, Great Britain
34    pub en_gb: String,
35    /// Korean, Korea
36    pub ko_kr: String,
37    /// French, France
38    pub fr_fr: String,
39    /// German, Germany
40    pub de_de: String,
41    /// English, China
42    pub en_cn: String,
43    /// English, Taiwan
44    pub en_tw: String,
45    /// Spanish, Spain
46    pub es_es: String,
47    /// Spanish, Mexico
48    pub es_mx: String,
49    /// Unknown flags.
50    pub flags: u32,
51}
52
53impl LocalizedString {
54    #[allow(clippy::too_many_arguments)]
55    pub(crate) const fn new(
56        en_gb: String,
57        ko_kr: String,
58        fr_fr: String,
59        de_de: String,
60        en_cn: String,
61        en_tw: String,
62        es_es: String,
63        es_mx: String,
64        flags: u32,
65    ) -> Self {
66        Self {
67            en_gb,
68            ko_kr,
69            fr_fr,
70            de_de,
71            en_cn,
72            en_tw,
73            es_es,
74            es_mx,
75            flags,
76        }
77    }
78
79    pub(crate) fn string_indices_as_array(&self, string_index: &mut usize) -> [u8; 36] {
80        let mut arr = [0_u8; 36];
81        let mut index = 0;
82
83        for s in self.strings() {
84            let value = (if !s.is_empty() {
85                let v = *string_index;
86                *string_index += s.len() + 1;
87                v
88            } else {
89                0
90            } as u32)
91                .to_le_bytes();
92
93            arr[index] = value[0];
94            arr[index + 1] = value[1];
95            arr[index + 2] = value[2];
96            arr[index + 3] = value[3];
97            index += 4;
98        }
99
100        let value = &self.flags.to_le_bytes();
101        arr[index] = value[0];
102        arr[index + 1] = value[1];
103        arr[index + 2] = value[2];
104        arr[index + 3] = value[3];
105
106        arr
107    }
108
109    pub(crate) fn string_block_as_array(&self, b: &mut impl Write) -> Result<(), std::io::Error> {
110        for s in self.strings() {
111            if !s.is_empty() {
112                b.write_all(s.as_bytes())?;
113                b.write_all(&[0])?;
114            };
115        }
116
117        Ok(())
118    }
119
120    pub(crate) fn string_block_size(&self) -> usize {
121        let mut sum = 0;
122
123        for s in self.strings() {
124            if !s.is_empty() {
125                sum += s.len() + 1;
126            }
127        }
128
129        sum
130    }
131
132    pub(crate) const fn strings(&self) -> [&String; 8] {
133        [
134            &self.en_gb,
135            &self.ko_kr,
136            &self.fr_fr,
137            &self.de_de,
138            &self.en_cn,
139            &self.en_tw,
140            &self.es_es,
141            &self.es_mx,
142        ]
143    }
144}
145
146#[allow(missing_docs)]
147#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
148pub enum Gender {
149    Male,
150    Female,
151}
152
153impl TryFrom<i32> for Gender {
154    type Error = InvalidEnumError;
155    fn try_from(value: i32) -> Result<Self, Self::Error> {
156        TryFrom::try_from(value as i8)
157    }
158}
159
160impl TryFrom<i8> for Gender {
161    type Error = InvalidEnumError;
162    fn try_from(value: i8) -> Result<Self, Self::Error> {
163        Ok(match value {
164            0 => Self::Male,
165            1 => Self::Female,
166            val => return Err(InvalidEnumError::new("Gender", val as i64)),
167        })
168    }
169}
170
171impl Gender {
172    const fn as_int(&self) -> i32 {
173        match self {
174            Self::Male => 0,
175            Self::Female => 1,
176        }
177    }
178}
179
180impl Default for Gender {
181    fn default() -> Self {
182        Self::Male
183    }
184}
185
186#[allow(missing_docs)]
187#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
188pub enum SizeClass {
189    None,
190    Small,
191    Medium,
192    Large,
193    Giant,
194    Colossal,
195}
196
197impl TryFrom<i32> for SizeClass {
198    type Error = InvalidEnumError;
199    fn try_from(value: i32) -> Result<Self, Self::Error> {
200        Ok(match value {
201            -1 => Self::None,
202            0 => Self::Small,
203            1 => Self::Medium,
204            2 => Self::Large,
205            3 => Self::Giant,
206            4 => Self::Colossal,
207            val => return Err(InvalidEnumError::new("SizeClass", val as i64)),
208        })
209    }
210}
211
212impl SizeClass {
213    const fn as_int(&self) -> i32 {
214        match self {
215            Self::None => -1,
216            Self::Small => 0,
217            Self::Medium => 1,
218            Self::Large => 2,
219            Self::Giant => 3,
220            Self::Colossal => 4,
221        }
222    }
223}
224
225impl Default for SizeClass {
226    fn default() -> Self {
227        Self::None
228    }
229}
230
231/// Main trait for the crate. Implemented by all tables in [`tables`].
232pub trait DbcTable: Sized {
233    /// Will be the name of the implementing type suffixed with `Row`.
234    type Row;
235
236    /// The name of the DBC file _with_ `.dbc` at the end.
237    fn filename() -> &'static str;
238
239    /// Array of all rows. Are not guaranteed to be in any order.
240    fn rows(&self) -> &[Self::Row];
241    /// Mutable array of all rows. Are not guaranteed to be in any order.
242    fn rows_mut(&mut self) -> &mut [Self::Row];
243
244    /// Read table from bytes.
245    ///
246    /// Will error with [`InvalidHeaderError`] if the magic numbers (`0x43424457`) at the start of the file do not match.
247    fn read(b: &mut impl Read) -> Result<Self, DbcError>;
248    /// Write to bytes.
249    ///
250    /// The string block will always start with a zero byte so that a string index of 0 is always an empty string.
251    ///
252    /// This is not guaranteed to create the exact same binary as is shipped with the game, but it will be semantically the same.
253    fn write(&self, w: &mut impl Write) -> Result<(), std::io::Error>;
254}
255
256/// Implemented by tables that have a primary key.
257///
258/// This is a separate trait instead of just implementing [`std::ops::Index`] and [`std::ops::IndexMut`] since
259/// those traits do not return [`Option`]s and only have the possibility of panicking on invalid keys.
260///
261/// The original DBCs do not really respect primary/foreign keys, so this just seemed like it would make everything more annoying.
262pub trait Indexable: DbcTable {
263    /// Key used to index into the table. Same name as the table suffixed with `Key`.
264    type PrimaryKey;
265
266    /// Gets the primary key, if present. Internally this is just [`std::iter::Iterator::find`] since the
267    /// items are not guaranteed to be ordered nor even be present.
268    fn get(&self, key: &Self::PrimaryKey) -> Option<&Self::Row>;
269
270    /// Gets the primary key, if present. Internally this is just [`std::iter::Iterator::find`] since the
271    /// items are not guaranteed to be ordered nor even be present.
272    fn get_mut(&mut self, key: &Self::PrimaryKey) -> Option<&mut Self::Row>;
273}