1mod bspfile;
2pub mod data;
3pub mod error;
4mod handle;
5mod reader;
6
7use crate::bspfile::LumpType;
8pub use crate::data::TextureFlags;
9pub use crate::data::*;
10use crate::error::ValidationError;
11pub use crate::handle::Handle;
12use binrw::io::Cursor;
13use binrw::{BinRead, BinReaderExt};
14use bspfile::BspFile;
15pub use error::{BspError, StringError};
16use lzma_rs::decompress::{Options, UnpackedSize};
17use reader::LumpReader;
18use std::cmp::min;
19use std::io::Read;
20pub use vbsp_common::{deserialize_bool, AsPropPlacement};
21
22pub type BspResult<T> = Result<T, BspError>;
23
24#[derive(Debug)]
27#[non_exhaustive]
28pub struct Bsp {
29 pub header: Header,
30 pub entities: Entities,
31 pub textures_data: Vec<TextureData>,
32 pub textures_info: Vec<TextureInfo>,
33 pub texture_string_tables: Vec<i32>,
34 pub texture_string_data: String,
35 pub planes: Vec<Plane>,
36 pub nodes: Vec<Node>,
37 pub leaves: Leaves,
38 pub leaf_faces: Vec<LeafFace>,
39 pub leaf_brushes: Vec<LeafBrush>,
40 pub models: Vec<Model>,
41 pub brushes: Vec<Brush>,
42 pub brush_sides: Vec<BrushSide>,
43 pub vertices: Vec<Vertex>,
44 pub edges: Vec<Edge>,
45 pub surface_edges: Vec<SurfaceEdge>,
46 pub faces: Vec<Face>,
47 pub original_faces: Vec<Face>,
48 pub vis_data: VisData,
49 pub displacements: Vec<DisplacementInfo>,
50 pub displacement_vertices: Vec<DisplacementVertex>,
51 pub displacement_triangles: Vec<DisplacementTriangle>,
52 vertex_normals: Vec<VertNormal>,
53 vertex_normal_indices: Vec<VertNormalIndex>,
54 pub static_props: PropStaticGameLump,
55 pub pack: Packfile,
56}
57
58impl Bsp {
59 pub fn read(data: &[u8]) -> BspResult<Self> {
60 let bsp_file = BspFile::new(data)?;
61
62 let entities = bsp_file.lump_reader(LumpType::Entities)?.read_entities()?;
63 let textures_data = bsp_file
64 .lump_reader(LumpType::TextureData)?
65 .read_vec(|r| r.read())?;
66 let textures_info = bsp_file
67 .lump_reader(LumpType::TextureInfo)?
68 .read_vec(|r| r.read())?;
69 let texture_string_tables = bsp_file
70 .lump_reader(LumpType::TextureDataStringTable)?
71 .read_vec(|r| r.read())?;
72 let texture_string_data = String::from_utf8(
73 bsp_file
74 .get_lump(bsp_file.get_lump_entry(LumpType::TextureDataStringData))?
75 .into_owned(),
76 )
77 .map_err(|e| BspError::String(StringError::NonUTF8(e.utf8_error())))?;
78 let planes = bsp_file
79 .lump_reader(LumpType::Planes)?
80 .read_vec(|r| r.read())?;
81 let nodes = bsp_file
82 .lump_reader(LumpType::Nodes)?
83 .read_vec(|r| r.read())?;
84 let leaves = bsp_file.lump_reader(LumpType::Leaves)?.read_args()?;
85 let leaf_faces = bsp_file
86 .lump_reader(LumpType::LeafFaces)?
87 .read_vec(|r| r.read())?;
88 let leaf_brushes = bsp_file
89 .lump_reader(LumpType::LeafBrushes)?
90 .read_vec(|r| r.read())?;
91 let models = bsp_file
92 .lump_reader(LumpType::Models)?
93 .read_vec(|r| r.read())?;
94 let brushes = bsp_file
95 .lump_reader(LumpType::Brushes)?
96 .read_vec(|r| r.read())?;
97 let brush_sides = bsp_file
98 .lump_reader(LumpType::BrushSides)?
99 .read_vec(|r| r.read())?;
100 let vertices = bsp_file
101 .lump_reader(LumpType::Vertices)?
102 .read_vec(|r| r.read())?;
103 let edges = bsp_file
104 .lump_reader(LumpType::Edges)?
105 .read_vec(|r| r.read())?;
106 let surface_edges = bsp_file
107 .lump_reader(LumpType::SurfaceEdges)?
108 .read_vec(|r| r.read())?;
109 let faces = bsp_file
110 .lump_reader(LumpType::Faces)?
111 .read_vec(|r| r.read())?;
112 let original_faces = bsp_file
113 .lump_reader(LumpType::OriginalFaces)?
114 .read_vec(|r| r.read())?;
115 let vis_data = bsp_file.lump_reader(LumpType::Visibility)?.read_visdata()?;
116 let displacements = bsp_file
117 .lump_reader(LumpType::DisplacementInfo)?
118 .read_vec(|r| r.read())?;
119 let displacement_vertices = bsp_file
120 .lump_reader(LumpType::DisplacementVertices)?
121 .read_vec(|r| r.read())?;
122 let displacement_triangles = bsp_file
123 .lump_reader(LumpType::DisplacementTris)?
124 .read_vec(|r| r.read())?;
125 let vertex_normals = bsp_file
126 .lump_reader(LumpType::VertNormals)?
127 .read_vec(|r| r.read())?;
128 let vertex_normal_indices = bsp_file
129 .lump_reader(LumpType::VertNormalIndices)?
130 .read_vec(|r| r.read())?;
131 let game_lumps: GameLumpHeader = bsp_file.lump_reader(LumpType::GameLump)?.read()?;
132 let pack = Packfile::read(bsp_file.lump_reader(LumpType::PakFile)?.into_data())?;
133
134 let static_props = game_lumps
135 .find(data)
136 .ok_or(ValidationError::NoStaticPropLump)??;
137
138 let bsp = Bsp {
139 header: bsp_file.header().clone(),
140 entities,
141 textures_data,
142 textures_info,
143 texture_string_tables,
144 texture_string_data,
145 planes,
146 nodes,
147 leaves,
148 leaf_faces,
149 leaf_brushes,
150 models,
151 brushes,
152 brush_sides,
153 vertices,
154 edges,
155 surface_edges,
156 faces,
157 original_faces,
158 vis_data,
159 displacements,
160 displacement_vertices,
161 displacement_triangles,
162 vertex_normals,
163 vertex_normal_indices,
164 static_props,
165 pack,
166 };
167 bsp.validate()?;
168 Ok(bsp)
169 }
170
171 pub fn leaf(&self, n: usize) -> Option<Handle<'_, Leaf>> {
172 self.leaves.get(n).map(|leaf| Handle::new(self, leaf))
173 }
174
175 pub fn plane(&self, n: usize) -> Option<Handle<'_, Plane>> {
176 self.planes.get(n).map(|plane| Handle::new(self, plane))
177 }
178
179 pub fn face(&self, n: usize) -> Option<Handle<'_, Face>> {
180 self.faces.get(n).map(|face| Handle::new(self, face))
181 }
182
183 pub fn node(&self, n: usize) -> Option<Handle<'_, Node>> {
184 self.nodes.get(n).map(|node| Handle::new(self, node))
185 }
186
187 pub fn texture_info(&self, n: usize) -> Option<Handle<'_, TextureInfo>> {
188 self.textures_info
189 .get(n)
190 .map(|texture_info| Handle::new(self, texture_info))
191 }
192
193 pub fn displacement(&self, n: usize) -> Option<Handle<'_, DisplacementInfo>> {
194 self.displacements
195 .get(n)
196 .map(|displacement| Handle::new(self, displacement))
197 }
198
199 fn displacement_vertex(&self, n: usize) -> Option<Handle<'_, DisplacementVertex>> {
200 self.displacement_vertices
201 .get(n)
202 .map(|vert| Handle::new(self, vert))
203 }
204
205 pub fn root_node(&self) -> Handle<'_, Node> {
207 self.node(0).unwrap()
208 }
209
210 pub fn models(&self) -> impl Iterator<Item = Handle<'_, Model>> {
212 self.models.iter().map(move |m| Handle::new(self, m))
213 }
214
215 pub fn textures(&self) -> impl Iterator<Item = Handle<'_, TextureInfo>> {
217 self.textures_info.iter().map(move |m| Handle::new(self, m))
218 }
219
220 pub fn leaf_at(&self, point: Vector) -> Handle<'_, Leaf> {
222 let mut current = self.root_node();
223
224 loop {
225 let plane = current.plane();
226 let dot: f32 = point
227 .iter()
228 .zip(plane.normal.iter())
229 .map(|(a, b)| a * b)
230 .sum();
231
232 let [front, back] = current.children;
233
234 let next = if dot < plane.dist { back } else { front };
235
236 if next < 0 {
237 return self.leaf((!next) as usize).unwrap();
238 } else {
239 current = self.node(next as usize).unwrap();
240 }
241 }
242 }
243
244 pub fn static_props(&self) -> impl Iterator<Item = Handle<'_, StaticPropLump>> {
245 self.static_props
246 .props
247 .props
248 .iter()
249 .map(|lump| Handle::new(self, lump))
250 }
251
252 pub fn original_faces(&self) -> impl Iterator<Item = Handle<Face>> {
254 self.faces.iter().map(move |face| Handle::new(self, face))
255 }
256
257 fn validate(&self) -> BspResult<()> {
258 self.validate_indexes(
259 self.faces
260 .iter()
261 .filter_map(|face| face.displacement_index()),
262 &self.displacements,
263 "face",
264 "displacement",
265 )?;
266 self.validate_indexes(
267 self.displacements
268 .iter()
269 .map(|displacement| displacement.map_face),
270 &self.faces,
271 "displacement",
272 "face",
273 )?;
274 self.validate_indexes(
275 self.faces
276 .iter()
277 .map(|face| face.first_edge + face.num_edges as i32 - 1),
278 &self.surface_edges,
279 "face",
280 "surface_edge",
281 )?;
282 self.validate_indexes(
283 self.surface_edges.iter().map(|edge| edge.edge_index()),
284 &self.edges,
285 "surface_edge",
286 "edge",
287 )?;
288 self.validate_indexes(
289 self.edges
290 .iter()
291 .flat_map(|edge| [edge.start_index, edge.end_index]),
292 &self.vertices,
293 "edge",
294 "vertex",
295 )?;
296 self.validate_indexes(
297 self.displacements
298 .iter()
299 .flat_map(|displacement| &displacement.corner_neighbours)
300 .flat_map(|corner| corner.neighbours()),
301 &self.displacements,
302 "displacement",
303 "displacement",
304 )?;
305 self.validate_indexes(
306 self.displacements
307 .iter()
308 .flat_map(|displacement| &displacement.edge_neighbours)
309 .flat_map(|edge| edge.iter())
310 .map(|sub| sub.neighbour_index),
311 &self.displacements,
312 "displacement",
313 "displacement",
314 )?;
315 self.validate_indexes(
316 self.faces.iter().map(|face| face.texture_info),
317 &self.textures_info,
318 "face",
319 "texture_info",
320 )?;
321 self.validate_indexes(
322 self.textures_info
323 .iter()
324 .map(|texture| texture.texture_data_index),
325 &self.textures_data,
326 "texture_info",
327 "texture_data",
328 )?;
329 self.validate_indexes(
330 self.textures_data
331 .iter()
332 .map(|texture| texture.name_string_table_id),
333 &self.texture_string_tables,
334 "textures_data",
335 "texture_string_tables",
336 )?;
337 self.validate_indexes(
338 self.texture_string_tables.iter().copied(),
339 self.texture_string_data.as_bytes(),
340 "texture_string_tables",
341 "texture_string_data",
342 )?;
343 self.validate_indexes(
344 self.nodes.iter().map(|node| node.plane_index),
345 &self.planes,
346 "node",
347 "plane",
348 )?;
349 self.validate_indexes(
350 self.nodes
351 .iter()
352 .flat_map(|node| node.children)
353 .filter(|index| *index >= 0),
354 &self.nodes,
355 "node",
356 "node",
357 )?;
358 self.validate_indexes(
359 self.nodes
360 .iter()
361 .flat_map(|node| node.children)
362 .filter_map(|index| (index < 0).then_some(!index)),
363 &self.leaves,
364 "node",
365 "leaf",
366 )?;
367 self.validate_indexes(
368 self.static_props().map(|prop| prop.prop_type),
369 &self.static_props.dict.name,
370 "static props",
371 "static prop models",
372 )?;
373 self.validate_indexes(
374 self.vertex_normal_indices.iter().map(|i| i.index),
375 &self.vertex_normals,
376 "vertex normal indices",
377 "vertex normals",
378 )?;
379
380 if self.nodes.is_empty() {
381 return Err(ValidationError::NoRootNode.into());
382 }
383
384 for face in &self.faces {
385 if face.displacement_index().is_some() && face.num_edges != 4 {
386 return Err(ValidationError::NonSquareDisplacement(face.num_edges).into());
387 }
388 }
389
390 Ok(())
391 }
392
393 fn validate_indexes<
394 'b,
395 Index: TryInto<usize> + Into<i64> + Copy + Ord + Default,
396 Indexes: Iterator<Item = Index>,
397 T: 'b,
398 >(
399 &'b self,
400 indexes: Indexes,
401 list: &[T],
402 source: &'static str,
403 target: &'static str,
404 ) -> BspResult<()> {
405 let max = match indexes.max() {
406 Some(max) => max,
407 None => return Ok(()),
408 };
409 max.try_into()
410 .ok()
411 .and_then(|index| list.get(index))
412 .ok_or_else(|| ValidationError::ReferenceOutOfRange {
413 source_: source,
414 target,
415 index: max.into(),
416 size: list.len(),
417 })?;
418 Ok(())
419 }
420}
421
422fn lzma_decompress_with_header(data: &[u8], expected_length: usize) -> Result<Vec<u8>, BspError> {
424 let mut output: Vec<u8> = Vec::with_capacity(min(expected_length + 8, 8 * 1024 * 1024));
426 let mut cursor = Cursor::new(data);
427 if b"LZMA" != &<[u8; 4]>::read(&mut cursor)? {
428 return Err(BspError::LumpDecompressError(
429 lzma_rs::error::Error::LzmaError("Invalid lzma header".into()),
430 ));
431 }
432 let actual_size: u32 = cursor.read_le()?;
433 let lzma_size: u32 = cursor.read_le()?;
434 if data.len() < lzma_size as usize + 12 {
435 return Err(BspError::UnexpectedCompressedLumpSize {
436 got: data.len() as u32,
437 expected: lzma_size,
438 });
439 }
440 lzma_rs::lzma_decompress_with_options(
441 &mut cursor,
442 &mut output,
443 &Options {
444 unpacked_size: UnpackedSize::UseProvided(Some(actual_size as u64)),
445 allow_incomplete: false,
446 memlimit: None,
447 },
448 )
449 .map_err(BspError::LumpDecompressError)?;
450 if output.len() != expected_length {
451 return Err(BspError::UnexpectedUncompressedLumpSize {
452 got: output.len() as u32,
453 expected: expected_length as u32,
454 });
455 }
456 Ok(output)
457}
458
459#[cfg(test)]
460mod tests {
461 use super::Bsp;
462
463 #[test]
464 fn tf2_file() {
465 use std::fs::read;
466
467 let data = read("koth_bagel_rc2a.bsp").unwrap();
468
469 Bsp::read(&data).unwrap();
470 }
471}