quake_util/bsp/
repr.rs

1use std::mem::size_of;
2use std::mem::MaybeUninit;
3
4pub const BSP_VERSION: u32 = 29;
5pub const BSP2_VERSION: u32 = u32::from_le_bytes(*b"BSP2");
6pub const ENTRY_COUNT: usize = 15;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9#[repr(usize)]
10pub enum EntryOffset {
11    Entities = 0,
12    Planes,
13    Textures,
14    Vertices,
15    Vis,
16    Nodes,
17    TexInfo,
18    Faces,
19    Light,
20    ClipNodes,
21    Leaves,
22    MarkSurfaces,
23    Edges,
24    SurfEdges,
25    Models,
26}
27
28impl From<EntryOffset> for usize {
29    fn from(offset: EntryOffset) -> Self {
30        offset as usize
31    }
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35#[repr(C, packed)]
36pub struct Entry {
37    pub offset: u32,
38    pub length: u32,
39}
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq)]
42#[repr(C, packed)]
43pub struct Head {
44    version: u32,
45    entries: [Entry; ENTRY_COUNT],
46}
47
48impl Head {
49    pub fn entry(&self, offset: EntryOffset) -> Entry {
50        let idx: usize = offset.into();
51        self.entries[idx]
52    }
53
54    pub fn version(&self) -> u32 {
55        self.version
56    }
57}
58
59impl TryFrom<[u8; size_of::<Head>()]> for Head {
60    type Error = crate::BinParseError;
61
62    fn try_from(bytes: [u8; size_of::<Head>()]) -> crate::BinParseResult<Head> {
63        let version =
64            u32::from_le_bytes(<[u8; 4]>::try_from(&bytes[..4]).unwrap());
65
66        if version != BSP_VERSION && version != BSP2_VERSION {
67            return Err(crate::BinParseError::Parse(format!(
68                "Unrecognized BSP version {} ({:?})",
69                version,
70                version.to_le_bytes(),
71            )));
72        }
73
74        let rest = &bytes[4..];
75        let mut entries = [MaybeUninit::<Entry>::uninit(); ENTRY_COUNT];
76
77        for (idx, chunk) in rest.chunks(size_of::<Entry>()).enumerate() {
78            entries[idx] = MaybeUninit::new(Entry {
79                offset: u32::from_le_bytes(
80                    <[u8; 4]>::try_from(&chunk[..4]).unwrap(),
81                ),
82                length: u32::from_le_bytes(
83                    <[u8; 4]>::try_from(&chunk[4..]).unwrap(),
84                ),
85            });
86        }
87
88        let entries = entries.map(|entry| unsafe { entry.assume_init() });
89
90        Ok(Head { version, entries })
91    }
92}