1
0
Fork 0
mirror of https://codeberg.org/icewind/vbsp.git synced 2026-06-03 18:54:05 +02:00

Merge pull request #14 from krakow10/version19

Add LeafV0 + Leaves Version Decoding
This commit is contained in:
Robin Appelman 2025-02-22 01:14:27 +01:00 committed by GitHub
commit ebc4e8b3ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 359 additions and 171 deletions

View file

@ -34,12 +34,16 @@ impl<'a> BspFile<'a> {
}
pub fn lump_reader(&self, lump: LumpType) -> BspResult<LumpReader<Cursor<Cow<[u8]>>>> {
let data = self.get_lump(lump)?;
Ok(LumpReader::new(data, lump))
let entry = self.get_lump_entry(lump);
let data = self.get_lump(entry)?;
Ok(LumpReader::new(data, lump, entry.version))
}
pub fn get_lump(&self, lump: LumpType) -> BspResult<Cow<[u8]>> {
let lump = &self.directories[lump];
pub fn get_lump_entry(&self, lump: LumpType) -> &LumpEntry {
&self.directories[lump]
}
pub fn get_lump(&self, lump: &LumpEntry) -> BspResult<Cow<[u8]>> {
let raw_data = self
.data
.get(lump.offset as usize..lump.offset as usize + lump.length as usize)

327
src/data/leaves.rs Normal file
View file

@ -0,0 +1,327 @@
use std::io::{Read, Seek};
use std::mem::{align_of, size_of};
use std::ops::Deref;
use binrw::{BinRead, BinResult, Endian};
use crate::bspfile::LumpType;
use crate::BspError;
use super::LumpArgs;
#[derive(Debug, Clone)]
pub struct Leaves {
leaves: Vec<Leaf>,
}
impl Leaves {
pub fn new(mut leaves: Vec<Leaf>) -> Self {
leaves.sort_unstable_by_key(|leaf| leaf.cluster);
Leaves { leaves }
}
pub fn iter(&self) -> impl Iterator<Item = &Leaf> {
self.into_iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Leaf> {
self.into_iter()
}
pub fn into_inner(self) -> Vec<Leaf> {
self.leaves
}
pub fn clusters(&self) -> impl Iterator<Item = impl Iterator<Item = &Leaf>> {
LeafClusters {
leaves: &self.leaves,
index: 0,
}
}
}
impl BinRead for Leaves {
type Args<'a> = LumpArgs;
fn read_options<R: Read + Seek>(
reader: &mut R,
endian: Endian,
args: Self::Args<'_>,
) -> BinResult<Self> {
let item_size = match args.version {
0 => size_of::<LeafV0>(),
1 => size_of::<LeafV1>(),
version => {
return Err(binrw::Error::Custom {
err: Box::new(BspError::LumpVersion(
crate::error::UnsupportedLumpVersion {
lump_type: "leaves",
version: version as u16,
},
)),
pos: reader.stream_position().unwrap(),
})
}
};
if args.length % item_size != 0 {
return Err(binrw::Error::Custom {
err: Box::new(BspError::InvalidLumpSize {
lump: LumpType::Leaves,
element_size: item_size,
lump_size: args.length,
}),
pos: reader.stream_position().unwrap(),
});
}
let num_entries = args.length / item_size;
let mut entries = Vec::with_capacity(num_entries);
for _ in 0..num_entries {
entries.push(Leaf::read_options(
reader,
endian,
LeafArgs {
version: args.version,
},
)?);
}
Ok(Self { leaves: entries })
}
}
struct LeafClusters<'a> {
leaves: &'a [Leaf],
index: usize,
}
impl<'a> Iterator for LeafClusters<'a> {
type Item = <&'a [Leaf] as IntoIterator>::IntoIter;
fn next(&mut self) -> Option<Self::Item> {
let cluster = self.leaves.get(self.index)?.cluster;
let remaining_leaves = self.leaves.get(self.index..)?;
let cluster_size = remaining_leaves
.iter()
.take_while(|leaf| leaf.cluster == cluster)
.count();
self.index += cluster_size;
Some(remaining_leaves[0..cluster_size].iter())
}
}
#[test]
fn test_leaf_clusters() {
let leaves: Leaves = vec![
Leaf {
contents: 0,
cluster: 0,
..Default::default()
},
Leaf {
contents: 1,
cluster: 0,
..Default::default()
},
Leaf {
contents: 2,
cluster: 1,
..Default::default()
},
Leaf {
contents: 3,
cluster: 2,
..Default::default()
},
Leaf {
contents: 4,
cluster: 2,
..Default::default()
},
]
.into();
let clustered: Vec<Vec<i32>> = leaves
.clusters()
.map(|cluster| cluster.map(|leaf| leaf.contents).collect())
.collect();
assert_eq!(vec![vec![0, 1], vec![2], vec![3, 4]], clustered);
}
impl From<Vec<Leaf>> for Leaves {
fn from(other: Vec<Leaf>) -> Self {
Self::new(other)
}
}
impl Deref for Leaves {
type Target = [Leaf];
fn deref(&self) -> &Self::Target {
&self.leaves
}
}
impl IntoIterator for Leaves {
type Item = Leaf;
type IntoIter = <Vec<Leaf> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.leaves.into_iter()
}
}
impl<'a> IntoIterator for &'a Leaves {
type Item = &'a Leaf;
type IntoIter = <&'a [Leaf] as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.leaves[..].iter()
}
}
impl<'a> IntoIterator for &'a mut Leaves {
type Item = &'a mut Leaf;
type IntoIter = <&'a mut [Leaf] as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.leaves.iter_mut()
}
}
#[derive(BinRead, Debug, Default, Clone, Copy)]
pub struct ColorRGBExp32 {
pub r: u8,
pub g: u8,
pub b: u8,
pub exponent: i8,
}
#[derive(BinRead, Debug, Default, Clone, Copy)]
pub struct CompressedLightCube {
pub color: [ColorRGBExp32; 6],
}
#[derive(Default, Debug, Clone, BinRead)]
pub struct LeafV0 {
pub contents: i32,
pub cluster: i16,
pub area_and_flags: i16,
// first 9 bits is area, last 7 bits is flags
pub mins: [i16; 3],
pub maxs: [i16; 3],
pub first_leaf_face: u16,
pub leaf_face_count: u16,
pub first_leaf_brush: u16,
pub leaf_brush_count: u16,
pub leaf_watter_data_id: i16,
#[br(align_after = align_of::< LeafV0 > ())]
pub cube: CompressedLightCube,
}
static_assertions::const_assert_eq!(size_of::<LeafV0>(), 56);
impl From<LeafV0> for Leaf {
fn from(value: LeafV0) -> Self {
Self {
contents: value.contents,
cluster: value.cluster,
area_and_flags: value.area_and_flags,
mins: value.mins,
maxs: value.maxs,
first_leaf_face: value.first_leaf_face,
leaf_face_count: value.leaf_face_count,
first_leaf_brush: value.first_leaf_brush,
leaf_brush_count: value.leaf_brush_count,
leaf_watter_data_id: value.leaf_watter_data_id,
cube: value.cube,
}
}
}
#[derive(Default, Debug, Clone, BinRead)]
pub struct LeafV1 {
pub contents: i32,
pub cluster: i16,
pub area_and_flags: i16,
// first 9 bits is area, last 7 bits is flags
pub mins: [i16; 3],
pub maxs: [i16; 3],
pub first_leaf_face: u16,
pub leaf_face_count: u16,
pub first_leaf_brush: u16,
pub leaf_brush_count: u16,
#[br(align_after = align_of::< LeafV1 > ())]
pub leaf_watter_data_id: i16,
}
static_assertions::const_assert_eq!(size_of::<LeafV1>(), 32);
impl From<LeafV1> for Leaf {
fn from(value: LeafV1) -> Self {
Self {
contents: value.contents,
cluster: value.cluster,
area_and_flags: value.area_and_flags,
mins: value.mins,
maxs: value.maxs,
first_leaf_face: value.first_leaf_face,
leaf_face_count: value.leaf_face_count,
first_leaf_brush: value.first_leaf_brush,
leaf_brush_count: value.leaf_brush_count,
leaf_watter_data_id: value.leaf_watter_data_id,
cube: Default::default(),
}
}
}
#[derive(Default, Debug, Clone)]
pub struct Leaf {
pub contents: i32,
pub cluster: i16,
pub area_and_flags: i16,
// first 9 bits is area, last 7 bits is flags
pub mins: [i16; 3],
pub maxs: [i16; 3],
pub first_leaf_face: u16,
pub leaf_face_count: u16,
pub first_leaf_brush: u16,
pub leaf_brush_count: u16,
pub leaf_watter_data_id: i16,
pub cube: CompressedLightCube,
}
static_assertions::const_assert_eq!(size_of::<Leaf>(), 56);
#[test]
fn test_leaf_bytes() {
super::test_read_bytes::<Leaf>();
}
#[derive(Default, Debug, Clone)]
pub struct LeafArgs {
pub version: u32,
}
impl BinRead for Leaf {
type Args<'a> = LeafArgs;
fn read_options<R: Read + Seek>(
reader: &mut R,
endian: Endian,
args: Self::Args<'_>,
) -> BinResult<Self> {
match args.version {
0 => LeafV0::read_options(reader, endian, ()).map(Leaf::from),
1 => LeafV1::read_options(reader, endian, ()).map(Leaf::from),
version => Err(binrw::Error::Custom {
err: Box::new(crate::error::UnsupportedLumpVersion {
lump_type: "leaves",
version: version as u16,
}),
pos: reader.stream_position().unwrap(),
}),
}
}
}

View file

@ -1,12 +1,14 @@
mod displacement;
mod entity;
mod game;
mod leaves;
mod prop;
mod vector;
pub use self::displacement::*;
pub use self::entity::*;
pub use self::game::*;
pub use self::leaves::*;
pub use self::prop::PropPlacement;
pub use self::vector::*;
use crate::bspfile::LumpType;
@ -26,7 +28,7 @@ use std::cmp::min;
use std::fmt;
use std::fmt::{Debug, Display, Formatter};
use std::io::{Cursor, Read, Seek};
use std::mem::{align_of, size_of};
use std::mem::size_of;
use std::ops::Index;
use std::str::FromStr;
use std::sync::Mutex;
@ -83,6 +85,12 @@ pub struct Header {
pub version: BspVersion,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct LumpArgs {
pub length: usize,
pub version: u32,
}
#[derive(Clone, Copy, Debug, Default, BinRead)]
#[br(little)]
pub struct LumpEntry {
@ -225,29 +233,6 @@ pub struct Node {
static_assertions::const_assert_eq!(size_of::<Node>(), 32);
#[derive(Default, Debug, Clone, BinRead)]
pub struct Leaf {
pub contents: i32,
pub cluster: i16,
pub area_and_flags: i16,
// first 9 bits is area, last 7 bits is flags
pub mins: [i16; 3],
pub maxs: [i16; 3],
pub first_leaf_face: u16,
pub leaf_face_count: u16,
pub first_leaf_brush: u16,
pub leaf_brush_count: u16,
#[br(align_after = align_of::< Leaf > ())]
pub leaf_watter_data_id: i16,
}
static_assertions::const_assert_eq!(size_of::<Leaf>(), 32);
#[test]
fn test_leaf_bytes() {
test_read_bytes::<Leaf>();
}
#[derive(Debug, Clone, BinRead)]
pub struct LeafBrush {
pub brush: u16,

View file

@ -17,141 +17,10 @@ pub use error::{BspError, StringError};
use lzma_rs::decompress::{Options, UnpackedSize};
use reader::LumpReader;
use std::cmp::min;
use std::{io::Read, ops::Deref};
use std::io::Read;
pub type BspResult<T> = Result<T, BspError>;
#[derive(Debug, Clone)]
pub struct Leaves {
leaves: Vec<Leaf>,
}
impl Leaves {
pub fn new(mut leaves: Vec<Leaf>) -> Self {
leaves.sort_unstable_by_key(|leaf| leaf.cluster);
Leaves { leaves }
}
pub fn iter(&self) -> impl Iterator<Item = &Leaf> {
self.into_iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Leaf> {
self.into_iter()
}
pub fn into_inner(self) -> Vec<Leaf> {
self.leaves
}
pub fn clusters(&self) -> impl Iterator<Item = impl Iterator<Item = &Leaf>> {
LeafClusters {
leaves: &self.leaves,
index: 0,
}
}
}
struct LeafClusters<'a> {
leaves: &'a [Leaf],
index: usize,
}
impl<'a> Iterator for LeafClusters<'a> {
type Item = <&'a [Leaf] as IntoIterator>::IntoIter;
fn next(&mut self) -> Option<Self::Item> {
let cluster = self.leaves.get(self.index)?.cluster;
let remaining_leaves = self.leaves.get(self.index..)?;
let cluster_size = remaining_leaves
.iter()
.take_while(|leaf| leaf.cluster == cluster)
.count();
self.index += cluster_size;
Some(remaining_leaves[0..cluster_size].iter())
}
}
#[test]
fn test_leaf_clusters() {
let leaves: Leaves = vec![
Leaf {
contents: 0,
cluster: 0,
..Default::default()
},
Leaf {
contents: 1,
cluster: 0,
..Default::default()
},
Leaf {
contents: 2,
cluster: 1,
..Default::default()
},
Leaf {
contents: 3,
cluster: 2,
..Default::default()
},
Leaf {
contents: 4,
cluster: 2,
..Default::default()
},
]
.into();
let clustered: Vec<Vec<i32>> = leaves
.clusters()
.map(|cluster| cluster.map(|leaf| leaf.contents).collect())
.collect();
assert_eq!(vec![vec![0, 1], vec![2], vec![3, 4]], clustered);
}
impl From<Vec<Leaf>> for Leaves {
fn from(other: Vec<Leaf>) -> Self {
Self::new(other)
}
}
impl Deref for Leaves {
type Target = [Leaf];
fn deref(&self) -> &Self::Target {
&self.leaves
}
}
impl IntoIterator for Leaves {
type Item = Leaf;
type IntoIter = <Vec<Leaf> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.leaves.into_iter()
}
}
impl<'a> IntoIterator for &'a Leaves {
type Item = &'a Leaf;
type IntoIter = <&'a [Leaf] as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.leaves[..].iter()
}
}
impl<'a> IntoIterator for &'a mut Leaves {
type Item = &'a mut Leaf;
type IntoIter = <&'a mut [Leaf] as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.leaves.iter_mut()
}
}
// TODO: Store all the allocated objects inline to improve cache usage
/// A parsed bsp file
#[derive(Debug)]
@ -202,7 +71,7 @@ impl Bsp {
.read_vec(|r| r.read())?;
let texture_string_data = String::from_utf8(
bsp_file
.get_lump(LumpType::TextureDataStringData)?
.get_lump(bsp_file.get_lump_entry(LumpType::TextureDataStringData))?
.into_owned(),
)
.map_err(|e| BspError::String(StringError::NonUTF8(e.utf8_error())))?;
@ -212,10 +81,7 @@ impl Bsp {
let nodes = bsp_file
.lump_reader(LumpType::Nodes)?
.read_vec(|r| r.read())?;
let leaves = bsp_file
.lump_reader(LumpType::Leaves)?
.read_vec(|r| r.read())?
.into();
let leaves = bsp_file.lump_reader(LumpType::Leaves)?.read_args()?;
let leaf_faces = bsp_file
.lump_reader(LumpType::LeafFaces)?
.read_vec(|r| r.read())?;

View file

@ -8,16 +8,18 @@ pub struct LumpReader<R> {
inner: R,
length: usize,
lump: LumpType,
version: u32,
}
impl<'a> LumpReader<Cursor<Cow<'a, [u8]>>> {
pub fn new(data: Cow<'a, [u8]>, lump: LumpType) -> Self {
pub fn new(data: Cow<'a, [u8]>, lump: LumpType, version: u32) -> Self {
let length = data.len();
let reader = Cursor::new(data);
LumpReader {
inner: reader,
length,
lump,
version,
}
}
@ -58,11 +60,7 @@ impl<R: BinReaderExt + Read> LumpReader<R> {
Ok(entries)
}
pub fn read<T: BinRead + Debug>(&mut self) -> BspResult<T>
where
T::Args<'static>: Default,
<T as BinRead>::Args<'static>: Clone,
{
pub fn read<'a, T: BinRead<Args<'a> = ()> + Debug>(&mut self) -> BspResult<T> {
// let start = self.inner.stream_position().unwrap() as usize;
let result = self.inner.read_le()?;
// let end = self.inner.stream_position().unwrap() as usize;
@ -76,6 +74,14 @@ impl<R: BinReaderExt + Read> LumpReader<R> {
// );
Ok(result)
}
pub fn read_args<'a, T: BinRead<Args<'a> = LumpArgs> + Debug>(&mut self) -> BspResult<T> {
let args = LumpArgs {
length: self.length,
version: self.version,
};
let result = T::read_options(&mut self.inner, binrw::Endian::Little, args)?;
Ok(result)
}
pub fn read_visdata(&mut self) -> BspResult<VisData> {
if self.length < size_of::<u32>() * 2 {