mirror of
https://codeberg.org/icewind/vbsp.git
synced 2026-06-03 18:54:05 +02:00
game lump/static props
This commit is contained in:
parent
d628f898d0
commit
15c1c4e6eb
9 changed files with 423 additions and 50 deletions
287
src/data/game.rs
Normal file
287
src/data/game.rs
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
use crate::error::UnsupportedLumpVersion;
|
||||
use crate::{lzma_decompress_with_header, BspError, FixedString, Vector};
|
||||
use binrw::{BinRead, BinReaderExt, BinResult, ReadOptions};
|
||||
use bitflags::bitflags;
|
||||
use std::borrow::Cow;
|
||||
use std::io::{Cursor, Read, Seek};
|
||||
use std::mem::size_of;
|
||||
|
||||
#[derive(Debug, Clone, BinRead)]
|
||||
pub struct GameLumpHeader {
|
||||
pub count: i32,
|
||||
#[br(count = count)]
|
||||
pub lumps: Vec<GameLump>,
|
||||
}
|
||||
|
||||
impl GameLumpHeader {
|
||||
pub fn find<T: GameLumpType<Args = (u16,)>>(&self, data: &[u8]) -> Option<Result<T, BspError>> {
|
||||
let (i, lump) = self
|
||||
.lumps
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, lump)| lump.id == T::ID)?;
|
||||
|
||||
let data = match self.get_game_lump_data(i, lump, data) {
|
||||
Ok(data) => data,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let mut reader = Cursor::new(data);
|
||||
Some(reader.read_le_args((lump.version,)).map_err(BspError::from))
|
||||
}
|
||||
|
||||
fn get_game_lump_data<'a>(
|
||||
&self,
|
||||
i: usize,
|
||||
lump: &GameLump,
|
||||
data: &'a [u8],
|
||||
) -> Result<Cow<'a, [u8]>, BspError> {
|
||||
if lump.flags.contains(GameLumpFlags::COMPRESSED) {
|
||||
let next_lump = self
|
||||
.lumps
|
||||
.get(i + 1)
|
||||
.ok_or_else(|| BspError::GameLumpOutOfBounds(lump.clone()))?;
|
||||
let compressed_size = next_lump.offset - lump.offset;
|
||||
let raw_data = data
|
||||
.get(lump.offset as usize..(lump.offset + compressed_size) as usize)
|
||||
.ok_or_else(|| BspError::GameLumpOutOfBounds(lump.clone()))?;
|
||||
let mut output = lzma_decompress_with_header(raw_data, lump.length as usize)?;
|
||||
// some compressed lumps are a bit to small for some reason
|
||||
output.extend_from_slice(&[0; 8]);
|
||||
Ok(Cow::Owned(output))
|
||||
} else {
|
||||
let data = data
|
||||
.get(lump.offset as usize..(lump.offset + lump.length) as usize)
|
||||
.ok_or_else(|| BspError::GameLumpOutOfBounds(lump.clone()))?;
|
||||
Ok(Cow::Borrowed(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, BinRead)]
|
||||
pub struct GameLump {
|
||||
pub id: i32,
|
||||
pub flags: GameLumpFlags,
|
||||
pub version: u16,
|
||||
pub offset: i32,
|
||||
pub length: i32,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(BinRead)]
|
||||
pub struct GameLumpFlags: u16 {
|
||||
const COMPRESSED = 0b0000_0000_0000_0000_0001;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GameLumpType: BinRead {
|
||||
const ID: i32;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, BinRead)]
|
||||
#[br(import(version: u16))]
|
||||
pub struct PropStaticGameLump {
|
||||
pub dict: StaticPropDictLump,
|
||||
pub leaf: StaticPropLeafLump,
|
||||
#[br(args(version))]
|
||||
pub props: StaticPropLumps,
|
||||
}
|
||||
|
||||
impl GameLumpType for PropStaticGameLump {
|
||||
const ID: i32 = i32::from_be_bytes(*b"sprp");
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, BinRead)]
|
||||
pub struct StaticPropDictLump {
|
||||
pub entries: i32,
|
||||
#[br(count = entries)]
|
||||
pub name: Vec<FixedString<128>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, BinRead)]
|
||||
pub struct StaticPropLeafLump {
|
||||
pub entries: i32,
|
||||
#[br(count = entries)]
|
||||
pub leaves: Vec<u16>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, BinRead)]
|
||||
#[br(import(version: u16))]
|
||||
pub struct StaticPropLumps {
|
||||
pub entries: i32,
|
||||
#[br(args_raw = binrw::VecArgs{count: entries as usize, inner: (version,)})]
|
||||
pub props: Vec<StaticPropLump>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StaticPropLump {
|
||||
pub origin: Vector,
|
||||
pub angles: [f32; 3],
|
||||
pub prop_type: u16,
|
||||
pub first_leaf: u16,
|
||||
pub leaf_count: u16,
|
||||
pub solid: u8,
|
||||
pub skin: i32,
|
||||
pub fade_min_distance: f32,
|
||||
pub fade_max_distance: f32,
|
||||
pub lighting_origin: Vector,
|
||||
pub forced_fade_scale: f32,
|
||||
pub min_dx_level: u16,
|
||||
pub max_dx_level: u16,
|
||||
pub flags: StaticPropLumpFlags,
|
||||
pub lightmap_resolution: [u16; 2],
|
||||
}
|
||||
|
||||
impl BinRead for StaticPropLump {
|
||||
type Args = (u16,);
|
||||
|
||||
fn read_options<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
options: &ReadOptions,
|
||||
args: Self::Args,
|
||||
) -> BinResult<Self> {
|
||||
match args.0 {
|
||||
6 => StaticPropLumpV6::read_options(reader, options, ()).map(StaticPropLump::from),
|
||||
7 | 10 => {
|
||||
StaticPropLumpV10::read_options(reader, options, ()).map(StaticPropLump::from)
|
||||
}
|
||||
version => Err(binrw::Error::Custom {
|
||||
err: Box::new(UnsupportedLumpVersion {
|
||||
lump_type: "static props",
|
||||
version,
|
||||
}),
|
||||
pos: reader.stream_position().unwrap(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(BinRead)]
|
||||
pub struct StaticPropLumpFlags: u32 {
|
||||
const FLAG_FADES = 0x1;
|
||||
const USE_LIGHTING_ORIGIN = 0x2;
|
||||
const NO_DRAW = 0x4;
|
||||
const IGNORE_NORMALS = 0x8;
|
||||
const NO_SHADOW = 0x10;
|
||||
const SCREEN_SPACE_FADE = 0x20;
|
||||
const NO_PER_VERTEX_LIGHTING = 0x40;
|
||||
const NO_SELF_SHADOWING = 0x80;
|
||||
const NO_PER_TEXEL_LIGHTING = 0x100;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StaticPropLumpFlagsV6> for StaticPropLumpFlags {
|
||||
fn from(v6: StaticPropLumpFlagsV6) -> Self {
|
||||
StaticPropLumpFlags::from_bits_truncate(v6.bits().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct StaticPropLumpV6 {
|
||||
pub origin: Vector,
|
||||
pub angles: [f32; 3],
|
||||
pub prop_type: u16,
|
||||
pub first_leaf: u16,
|
||||
pub leaf_count: u16,
|
||||
pub solid: u8,
|
||||
pub flags: StaticPropLumpFlagsV6,
|
||||
pub skin: i32,
|
||||
pub fade_min_distance: f32,
|
||||
pub fade_max_distance: f32,
|
||||
pub lighting_origin: Vector,
|
||||
pub forced_fade_scale: f32,
|
||||
pub min_dx_level: u16,
|
||||
pub max_dx_level: u16,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_static_prop_lump_v6_bytes() {
|
||||
super::test_read_bytes::<StaticPropLumpV6>();
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(BinRead)]
|
||||
struct StaticPropLumpFlagsV6: u8 {
|
||||
const FLAG_FADES = 0x1;
|
||||
const USE_LIGHTING_ORIGIN = 0x2;
|
||||
const NO_DRAW = 0x4;
|
||||
const IGNORE_NORMALS = 0x8;
|
||||
const NO_SHADOW = 0x10;
|
||||
const SCREEN_SPACE_FADE = 0x20;
|
||||
const NO_PER_VERTEX_LIGHTING = 0x40;
|
||||
const NO_SELF_SHADOWING = 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
// same as StaticPropLump but with derived BinRead
|
||||
#[derive(BinRead)]
|
||||
struct StaticPropLumpV10 {
|
||||
pub origin: Vector,
|
||||
pub angles: [f32; 3],
|
||||
pub prop_type: u16,
|
||||
pub first_leaf: u16,
|
||||
pub leaf_count: u16,
|
||||
// pad, not align
|
||||
#[br(pad_after = 1)]
|
||||
pub solid: u8,
|
||||
pub skin: i32,
|
||||
pub fade_min_distance: f32,
|
||||
pub fade_max_distance: f32,
|
||||
pub lighting_origin: Vector,
|
||||
pub forced_fade_scale: f32,
|
||||
pub min_dx_level: u16,
|
||||
pub max_dx_level: u16,
|
||||
pub flags: StaticPropLumpFlags,
|
||||
pub lightmap_resolution: [u16; 2],
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_static_prop_lump_bytes() {
|
||||
super::test_read_bytes::<StaticPropLumpV10>();
|
||||
}
|
||||
|
||||
static_assertions::const_assert_eq!(size_of::<StaticPropLumpV10>(), size_of::<StaticPropLump>());
|
||||
|
||||
impl From<StaticPropLumpV6> for StaticPropLump {
|
||||
fn from(from: StaticPropLumpV6) -> Self {
|
||||
StaticPropLump {
|
||||
origin: from.origin,
|
||||
angles: from.angles,
|
||||
prop_type: from.prop_type,
|
||||
first_leaf: from.first_leaf,
|
||||
leaf_count: from.leaf_count,
|
||||
solid: from.solid,
|
||||
skin: from.skin,
|
||||
fade_min_distance: from.fade_min_distance,
|
||||
fade_max_distance: from.fade_max_distance,
|
||||
lighting_origin: from.lighting_origin,
|
||||
forced_fade_scale: from.forced_fade_scale,
|
||||
min_dx_level: from.min_dx_level,
|
||||
max_dx_level: from.max_dx_level,
|
||||
flags: from.flags.into(),
|
||||
lightmap_resolution: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StaticPropLumpV10> for StaticPropLump {
|
||||
fn from(from: StaticPropLumpV10) -> Self {
|
||||
StaticPropLump {
|
||||
origin: from.origin,
|
||||
angles: from.angles,
|
||||
prop_type: from.prop_type,
|
||||
first_leaf: from.first_leaf,
|
||||
leaf_count: from.leaf_count,
|
||||
solid: from.solid,
|
||||
skin: from.skin,
|
||||
fade_min_distance: from.fade_min_distance,
|
||||
fade_max_distance: from.fade_max_distance,
|
||||
lighting_origin: from.lighting_origin,
|
||||
forced_fade_scale: from.forced_fade_scale,
|
||||
min_dx_level: from.min_dx_level,
|
||||
max_dx_level: from.max_dx_level,
|
||||
flags: from.flags,
|
||||
lightmap_resolution: from.lightmap_resolution,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
mod displacement;
|
||||
mod entity;
|
||||
mod game;
|
||||
mod vector;
|
||||
|
||||
pub use self::displacement::*;
|
||||
pub use self::entity::*;
|
||||
pub use self::game::*;
|
||||
pub use self::vector::*;
|
||||
use crate::bspfile::LumpType;
|
||||
use crate::StringError;
|
||||
|
|
@ -101,6 +103,18 @@ bitflags! {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct FixedString<const LEN: usize>(ArrayString<LEN>);
|
||||
|
||||
impl<const N: usize> AsRef<str> for FixedString<N> {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FixedString<N> {
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEN: usize> Display for FixedString<LEN> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
Display::fmt(&self.0, f)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue