wow_cdbc/
header.rs

1//! DBC header structure and parsing functionality.
2
3use crate::{Error, Result};
4use std::io::{Read, Seek, SeekFrom};
5
6/// The magic signature at the beginning of a DBC file
7pub const DBC_MAGIC: [u8; 4] = *b"WDBC";
8
9/// Represents a DBC file header
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct DbcHeader {
12    /// The magic signature, should be "WDBC"
13    pub magic: [u8; 4],
14    /// Number of records in the file
15    pub record_count: u32,
16    /// Number of fields in each record
17    pub field_count: u32,
18    /// Size of each record in bytes
19    pub record_size: u32,
20    /// Size of the string block in bytes
21    pub string_block_size: u32,
22}
23
24impl DbcHeader {
25    /// The size of a DBC header in bytes
26    pub const SIZE: usize = 20;
27
28    /// Parse a DBC header from a reader
29    pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
30        // Ensure we're at the beginning of the file
31        reader.seek(SeekFrom::Start(0))?;
32
33        // Read the magic signature
34        let mut magic = [0u8; 4];
35        reader.read_exact(&mut magic)?;
36
37        // Validate the magic signature
38        if magic != DBC_MAGIC {
39            return Err(Error::InvalidHeader(format!(
40                "Invalid magic signature: {magic:?}, expected: {DBC_MAGIC:?}"
41            )));
42        }
43
44        // Read the rest of the header
45        let mut buf = [0u8; 4];
46
47        reader.read_exact(&mut buf)?;
48        let record_count = u32::from_le_bytes(buf);
49
50        reader.read_exact(&mut buf)?;
51        let field_count = u32::from_le_bytes(buf);
52
53        reader.read_exact(&mut buf)?;
54        let record_size = u32::from_le_bytes(buf);
55
56        reader.read_exact(&mut buf)?;
57        let string_block_size = u32::from_le_bytes(buf);
58
59        // Perform basic validation
60        if record_size == 0 && record_count > 0 {
61            return Err(Error::InvalidHeader(
62                "Record size cannot be 0 if record count is greater than 0".to_string(),
63            ));
64        }
65
66        if field_count == 0 && record_count > 0 {
67            return Err(Error::InvalidHeader(
68                "Field count cannot be 0 if record count is greater than 0".to_string(),
69            ));
70        }
71
72        Ok(Self {
73            magic,
74            record_count,
75            field_count,
76            record_size,
77            string_block_size,
78        })
79    }
80
81    /// Calculates the offset to the string block
82    pub fn string_block_offset(&self) -> u64 {
83        DbcHeader::SIZE as u64 + (self.record_count as u64 * self.record_size as u64)
84    }
85
86    /// Calculates the total size of the DBC file
87    pub fn total_size(&self) -> u64 {
88        self.string_block_offset() + self.string_block_size as u64
89    }
90}