1use crate::{DbcHeader, Error, Result};
4use std::io::{Read, Seek, SeekFrom};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum DbcVersion {
9 WDBC,
11 WDB2,
13 WDB3,
15 WDB4,
17 WDB5,
19}
20
21impl DbcVersion {
22 pub fn detect<R: Read + Seek>(reader: &mut R) -> Result<Self> {
24 reader.seek(SeekFrom::Start(0))?;
25
26 let mut magic = [0u8; 4];
27 reader.read_exact(&mut magic)?;
28
29 match &magic {
30 b"WDBC" => Ok(DbcVersion::WDBC),
31 b"WDB2" => Ok(DbcVersion::WDB2),
32 b"WDB3" => Ok(DbcVersion::WDB3),
33 b"WDB4" => Ok(DbcVersion::WDB4),
34 b"WDB5" => Ok(DbcVersion::WDB5),
35 _ => Err(Error::InvalidHeader(format!(
36 "Unknown DBC version: {:?}",
37 std::str::from_utf8(&magic).unwrap_or("Invalid UTF-8")
38 ))),
39 }
40 }
41
42 pub fn magic(&self) -> [u8; 4] {
44 match self {
45 DbcVersion::WDBC => *b"WDBC",
46 DbcVersion::WDB2 => *b"WDB2",
47 DbcVersion::WDB3 => *b"WDB3",
48 DbcVersion::WDB4 => *b"WDB4",
49 DbcVersion::WDB5 => *b"WDB5",
50 }
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct Wdb2Header {
64 pub magic: [u8; 4],
66 pub record_count: u32,
68 pub field_count: u32,
70 pub record_size: u32,
72 pub string_block_size: u32,
74 pub table_hash: u32,
76 pub build: u32,
78 pub timestamp: u32,
80 pub min_index: i32,
83 pub max_index: i32,
85 pub locale: i32,
87 pub copy_table_size: u32,
89 pub has_extended_header: bool,
91 pub index_array_size: u64,
93}
94
95impl Wdb2Header {
96 pub const BASIC_SIZE: usize = 28;
98
99 pub const EXTENDED_SIZE: usize = 48;
101
102 pub const EXTENDED_BUILD_THRESHOLD: u32 = 12880;
104
105 pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
110 reader.seek(SeekFrom::Start(0))?;
112
113 let mut magic = [0u8; 4];
115 reader.read_exact(&mut magic)?;
116
117 if magic != *b"WDB2" {
119 return Err(Error::InvalidHeader(format!(
120 "Invalid magic signature: {:?}, expected: {:?}",
121 magic, b"WDB2"
122 )));
123 }
124
125 let mut buf = [0u8; 4];
127
128 reader.read_exact(&mut buf)?;
129 let record_count = u32::from_le_bytes(buf);
130
131 reader.read_exact(&mut buf)?;
132 let field_count = u32::from_le_bytes(buf);
133
134 reader.read_exact(&mut buf)?;
135 let record_size = u32::from_le_bytes(buf);
136
137 reader.read_exact(&mut buf)?;
138 let string_block_size = u32::from_le_bytes(buf);
139
140 reader.read_exact(&mut buf)?;
141 let table_hash = u32::from_le_bytes(buf);
142
143 reader.read_exact(&mut buf)?;
144 let build = u32::from_le_bytes(buf);
145
146 reader.read_exact(&mut buf)?;
148 let timestamp = u32::from_le_bytes(buf);
149
150 let has_extended_header = build > Self::EXTENDED_BUILD_THRESHOLD;
152
153 let (min_index, max_index, locale, copy_table_size, index_array_size) =
154 if has_extended_header {
155 reader.read_exact(&mut buf)?;
157 let min_index = i32::from_le_bytes(buf);
158
159 reader.read_exact(&mut buf)?;
160 let max_index = i32::from_le_bytes(buf);
161
162 reader.read_exact(&mut buf)?;
163 let locale = i32::from_le_bytes(buf);
164
165 reader.read_exact(&mut buf)?;
166 let copy_table_size = u32::from_le_bytes(buf);
167
168 let index_array_size = if max_index > 0 {
170 let diff = (max_index - min_index + 1) as u64;
171 diff * 4 + diff * 2
174 } else {
175 0
176 };
177
178 if index_array_size > 0 {
180 reader.seek(SeekFrom::Current(index_array_size as i64))?;
181 }
182
183 (
184 min_index,
185 max_index,
186 locale,
187 copy_table_size,
188 index_array_size,
189 )
190 } else {
191 (0, 0, 0, 0, 0)
192 };
193
194 Ok(Self {
195 magic,
196 record_count,
197 field_count,
198 record_size,
199 string_block_size,
200 table_hash,
201 build,
202 timestamp,
203 min_index,
204 max_index,
205 locale,
206 copy_table_size,
207 has_extended_header,
208 index_array_size,
209 })
210 }
211
212 pub fn to_dbc_header(&self) -> DbcHeader {
214 DbcHeader {
215 magic: *b"WDBC", record_count: self.record_count,
217 field_count: self.field_count,
218 record_size: self.record_size,
219 string_block_size: self.string_block_size,
220 }
221 }
222
223 pub fn header_size(&self) -> u64 {
225 if self.has_extended_header {
226 Self::EXTENDED_SIZE as u64 + self.index_array_size
227 } else {
228 Self::BASIC_SIZE as u64
229 }
230 }
231
232 pub fn record_data_offset(&self) -> u64 {
234 self.header_size()
235 }
236
237 pub fn string_block_offset(&self) -> u64 {
239 self.header_size() + (self.record_count as u64 * self.record_size as u64)
240 }
241
242 pub fn total_size(&self) -> u64 {
244 self.string_block_offset() + self.string_block_size as u64
245 }
246}
247
248#[derive(Debug, Clone, Copy, PartialEq, Eq)]
250pub struct Wdb5Header {
251 pub magic: [u8; 4],
253 pub record_count: u32,
255 pub field_count: u32,
257 pub record_size: u32,
259 pub string_block_size: u32,
261 pub table_hash: u32,
263 pub layout_hash: u32,
265 pub min_id: u32,
267 pub max_id: u32,
269 pub locale: u32,
271 pub flags: u16,
273 pub id_index: u16,
275}
276
277impl Wdb5Header {
278 pub const SIZE: usize = 48;
280
281 pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
283 reader.seek(SeekFrom::Start(0))?;
285
286 let mut magic = [0u8; 4];
288 reader.read_exact(&mut magic)?;
289
290 if magic != *b"WDB5" {
292 return Err(Error::InvalidHeader(format!(
293 "Invalid magic signature: {:?}, expected: {:?}",
294 magic, b"WDB5"
295 )));
296 }
297
298 let mut buf4 = [0u8; 4];
300 let mut buf2 = [0u8; 2];
301
302 reader.read_exact(&mut buf4)?;
303 let record_count = u32::from_le_bytes(buf4);
304
305 reader.read_exact(&mut buf4)?;
306 let field_count = u32::from_le_bytes(buf4);
307
308 reader.read_exact(&mut buf4)?;
309 let record_size = u32::from_le_bytes(buf4);
310
311 reader.read_exact(&mut buf4)?;
312 let string_block_size = u32::from_le_bytes(buf4);
313
314 reader.read_exact(&mut buf4)?;
315 let table_hash = u32::from_le_bytes(buf4);
316
317 reader.read_exact(&mut buf4)?;
318 let layout_hash = u32::from_le_bytes(buf4);
319
320 reader.read_exact(&mut buf4)?;
321 let min_id = u32::from_le_bytes(buf4);
322
323 reader.read_exact(&mut buf4)?;
324 let max_id = u32::from_le_bytes(buf4);
325
326 reader.read_exact(&mut buf4)?;
327 let locale = u32::from_le_bytes(buf4);
328
329 reader.read_exact(&mut buf2)?;
330 let flags = u16::from_le_bytes(buf2);
331
332 reader.read_exact(&mut buf2)?;
333 let id_index = u16::from_le_bytes(buf2);
334
335 Ok(Self {
336 magic,
337 record_count,
338 field_count,
339 record_size,
340 string_block_size,
341 table_hash,
342 layout_hash,
343 min_id,
344 max_id,
345 locale,
346 flags,
347 id_index,
348 })
349 }
350
351 pub fn to_dbc_header(&self) -> DbcHeader {
353 DbcHeader {
354 magic: *b"WDBC", record_count: self.record_count,
356 field_count: self.field_count,
357 record_size: self.record_size,
358 string_block_size: self.string_block_size,
359 }
360 }
361
362 pub fn string_block_offset(&self) -> u64 {
364 Self::SIZE as u64 + (self.record_count as u64 * self.record_size as u64)
365 }
366
367 pub fn total_size(&self) -> u64 {
369 self.string_block_offset() + self.string_block_size as u64
370 }
371}