1#![forbid(unsafe_code)]
4#![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#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct LocalizedString {
33 pub en_gb: String,
35 pub ko_kr: String,
37 pub fr_fr: String,
39 pub de_de: String,
41 pub en_cn: String,
43 pub en_tw: String,
45 pub es_es: String,
47 pub es_mx: String,
49 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
231pub trait DbcTable: Sized {
233 type Row;
235
236 fn filename() -> &'static str;
238
239 fn rows(&self) -> &[Self::Row];
241 fn rows_mut(&mut self) -> &mut [Self::Row];
243
244 fn read(b: &mut impl Read) -> Result<Self, DbcError>;
248 fn write(&self, w: &mut impl Write) -> Result<(), std::io::Error>;
254}
255
256pub trait Indexable: DbcTable {
263 type PrimaryKey;
265
266 fn get(&self, key: &Self::PrimaryKey) -> Option<&Self::Row>;
269
270 fn get_mut(&mut self, key: &Self::PrimaryKey) -> Option<&mut Self::Row>;
273}