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}