1
0
Fork 0
mirror of https://codeberg.org/icewind/vbsp.git synced 2026-06-03 10:44:07 +02:00

split off common types into a separate crate for easier semver management

This commit is contained in:
Robin Appelman 2025-03-02 18:25:36 +01:00
commit 76918206da
19 changed files with 394 additions and 316 deletions

View file

@ -1,6 +1,6 @@
use super::vector::Vector;
use crate::data::try_read_enum;
use crate::error::InvalidNeighbourError;
use crate::Vector;
use binrw::{BinRead, BinResult, Endian};
use bitflags::bitflags;
use num_enum::TryFromPrimitive;

View file

@ -1,11 +1,9 @@
use crate::error::EntityParseError;
use crate::{Angles, Vector};
use serde::de::{Error, Unexpected};
use serde::{Deserialize, Deserializer};
use crate::EntityProp;
use serde::Deserialize;
use std::collections::HashMap;
use std::fmt;
use std::fmt::Debug;
use std::str::FromStr;
use vdf_reader::entry::Entry;
use vdf_reader::VdfError;
@ -130,243 +128,6 @@ impl<'a> RawEntity<'a> {
}
}
pub trait EntityProp<'a>: Sized {
fn parse(raw: &'a str) -> Result<Self, EntityParseError>;
}
trait FromStrProp: FromStr {}
impl FromStrProp for u8 {}
impl FromStrProp for u16 {}
impl FromStrProp for f32 {}
impl FromStrProp for u32 {}
impl FromStrProp for i32 {}
impl FromStrProp for Color {}
impl FromStrProp for Angles {}
impl FromStrProp for Vector {}
impl FromStrProp for LightColor {}
impl<T: FromStrProp> EntityProp<'_> for T
where
EntityParseError: From<<T as FromStr>::Err>,
{
fn parse(raw: &'_ str) -> Result<Self, EntityParseError> {
Ok(raw.parse()?)
}
}
impl<T: FromStrProp, const N: usize> EntityProp<'_> for [T; N]
where
EntityParseError: From<<T as FromStr>::Err>,
[T; N]: Default,
{
fn parse(raw: &'_ str) -> Result<Self, EntityParseError> {
let mut values = raw.split_whitespace().map(T::from_str);
let mut result = <[T; N]>::default();
for item in result.iter_mut() {
*item = values.next().ok_or(EntityParseError::ElementCount)??;
}
Ok(result)
}
}
impl<'a> EntityProp<'a> for &'a str {
fn parse(raw: &'a str) -> Result<Self, EntityParseError> {
Ok(raw)
}
}
impl EntityProp<'_> for bool {
fn parse(raw: &'_ str) -> Result<Self, EntityParseError> {
Ok(raw != "0")
}
}
impl<'a, T: EntityProp<'a>> EntityProp<'a> for Option<T> {
fn parse(raw: &'a str) -> Result<Self, EntityParseError> {
Ok(Some(T::parse(raw)?))
}
}
#[derive(Debug, Clone)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl FromStr for Color {
type Err = EntityParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut floats = s.split_whitespace().map(u8::from_str);
let r = floats.next().ok_or(EntityParseError::ElementCount)??;
let g = floats.next().ok_or(EntityParseError::ElementCount)??;
let b = floats.next().ok_or(EntityParseError::ElementCount)??;
if floats.next().is_some() {
return Err(EntityParseError::ElementCount);
}
Ok(Self { r, g, b })
}
}
impl<'de> Deserialize<'de> for Color {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let str = <&str>::deserialize(deserializer)?;
let [r, g, b] = <[u8; 3]>::parse(str)
.map_err(|_| D::Error::invalid_value(Unexpected::Other(str), &"a list of 3 numbers"))?;
Ok(Color { r, g, b })
}
}
#[derive(Debug, Clone)]
pub struct LightColor {
pub r: u8,
pub g: u8,
pub b: u8,
pub intensity: u16,
}
impl FromStr for LightColor {
type Err = EntityParseError;
fn from_str(str: &str) -> Result<Self, Self::Err> {
let mut values = str.split_whitespace();
let r = values
.next()
.ok_or(EntityParseError::ElementCount)?
.parse()
.map_err(EntityParseError::Int)?;
let g = values
.next()
.ok_or(EntityParseError::ElementCount)?
.parse()
.map_err(EntityParseError::Int)?;
let b = values
.next()
.ok_or(EntityParseError::ElementCount)?
.parse()
.map_err(EntityParseError::Int)?;
let intensity = values
.next()
.ok_or(EntityParseError::ElementCount)?
.parse()
.map_err(EntityParseError::Int)?;
if values.next().is_some() {
return Err(EntityParseError::ElementCount);
}
Ok(LightColor { r, g, b, intensity })
}
}
impl<'de> Deserialize<'de> for LightColor {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let str = <&str>::deserialize(deserializer)?;
str.parse()
.map_err(|_| D::Error::invalid_value(Unexpected::Str(str), &"a list of 4 integers"))
}
}
#[derive(Debug, Clone)]
pub enum Negated {
Yes,
No,
MatchingCriteria,
}
pub struct NegatedParseErr;
impl FromStr for Negated {
type Err = NegatedParseErr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"1" => Ok(Negated::Yes),
"0" => Ok(Negated::No),
"allow entities that match criteria" => Ok(Negated::MatchingCriteria),
_ => Err(NegatedParseErr),
}
}
}
impl FromStrProp for Negated {}
struct NegatedVisitor;
impl serde::de::Visitor<'_> for NegatedVisitor {
type Value = Negated;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "Negated value")
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: Error,
{
match v {
0 => Ok(Negated::No),
1 => Ok(Negated::Yes),
_ => Err(E::invalid_value(Unexpected::Signed(v), &"0 or 1")),
}
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
v.parse()
.map_err(|_| E::invalid_value(Unexpected::Str(v), &"Negated"))
}
}
impl<'de> Deserialize<'de> for Negated {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(NegatedVisitor)
}
}
struct BoolVisitor;
impl serde::de::Visitor<'_> for BoolVisitor {
type Value = bool;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "bool value")
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: Error,
{
match v {
0 => Ok(false),
1 => Ok(true),
_ => Err(E::invalid_value(Unexpected::Signed(v), &"0 or 1")),
}
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
match v {
"0" | "no" => Ok(false),
"1" | "yes" => Ok(true),
other => Err(E::invalid_value(Unexpected::Str(other), &"bool")),
}
}
}
#[allow(dead_code)]
pub fn deserialize_bool<'de, D: Deserializer<'de>>(deserializer: D) -> Result<bool, D::Error> {
deserializer.deserialize_any(BoolVisitor)
}
#[derive(Clone, Debug, Deserialize)]
pub struct GenericEntity {
#[serde(rename = "classname")]

View file

@ -3,26 +3,19 @@ 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;
use crate::error::EntityParseError;
use crate::{BspResult, StringError};
use arrayvec::ArrayString;
use binrw::error::CustomError;
use binrw::{BinRead, BinResult, Endian};
use bitflags::bitflags;
use bv::BitVec;
use cgmath::{Deg, Quaternion, Rotation3};
use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
use serde::de::{Error, Unexpected};
use serde::{Deserialize, Deserializer};
use std::borrow::Cow;
use std::cmp::min;
use std::fmt;
@ -30,8 +23,8 @@ use std::fmt::{Debug, Display, Formatter};
use std::io::{Cursor, Read, Seek};
use std::mem::size_of;
use std::ops::Index;
use std::str::FromStr;
use std::sync::Mutex;
pub use vbsp_common::{Angles, Color, EntityProp, LightColor, Negated, PropPlacement, Vector};
use zip::result::ZipError;
use zip::ZipArchive;
@ -535,42 +528,3 @@ where
err: Box::new(e),
})
}
#[derive(Debug, Copy, Clone, BinRead, Default)]
pub struct Angles {
pitch: f32,
yaw: f32,
roll: f32,
}
impl<'de> Deserialize<'de> for Angles {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let str = <&str>::deserialize(deserializer)?;
str.parse()
.map_err(|_| D::Error::invalid_value(Unexpected::Other(str), &"a list of angles"))
}
}
impl FromStr for Angles {
type Err = EntityParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut floats = s.split_whitespace().map(f32::from_str);
let pitch = floats.next().ok_or(EntityParseError::ElementCount)??;
let yaw = floats.next().ok_or(EntityParseError::ElementCount)??;
let roll = floats.next().ok_or(EntityParseError::ElementCount)??;
Ok(Angles { pitch, yaw, roll })
}
}
impl Angles {
fn as_quaternion(&self) -> Quaternion<f32> {
// angles are applied in roll, pitch, yaw order
Quaternion::from_angle_y(Deg(self.yaw))
* Quaternion::from_angle_x(Deg(self.pitch))
* Quaternion::from_angle_z(Deg(self.roll))
}
}

View file

@ -1,17 +1,8 @@
use crate::{Handle, StaticPropLump, Vector};
use cgmath::Quaternion;
use crate::{Handle, StaticPropLump};
use vbsp_common::{AsPropPlacement, PropPlacement};
#[derive(Debug, Clone)]
pub struct PropPlacement<'a> {
pub model: &'a str,
pub rotation: Quaternion<f32>,
pub scale: f32,
pub origin: Vector,
pub skin: i32,
}
impl<'a> Handle<'a, StaticPropLump> {
pub fn as_prop_placement(&self) -> PropPlacement<'a> {
impl<'a> AsPropPlacement<'a> for Handle<'a, StaticPropLump> {
fn as_prop_placement(&self) -> PropPlacement<'a> {
PropPlacement {
model: self.model(),
rotation: self.rotation(),

View file

@ -1,128 +0,0 @@
use crate::error::EntityParseError;
use binrw::BinRead;
use cgmath::Vector3;
use serde::de::{Error, Unexpected};
use serde::{Deserialize, Deserializer};
use std::cmp::Ordering;
use std::fmt::Debug;
use std::ops::{Add, Mul, Sub};
use std::str::FromStr;
#[derive(Debug, Clone, Copy, BinRead, Default)]
pub struct Vector {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Vector {
pub fn iter(&self) -> impl Iterator<Item = f32> {
[self.x, self.y, self.z].into_iter()
}
pub fn length_squared(&self) -> f32 {
self.x.powf(2.0) + self.y.powf(2.0) + self.z.powf(2.0)
}
}
impl Add<Vector> for Vector {
type Output = Vector;
fn add(self, rhs: Vector) -> Self::Output {
Vector {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl Sub<Vector> for Vector {
type Output = Vector;
fn sub(self, rhs: Vector) -> Self::Output {
Vector {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
}
}
impl Mul<f32> for Vector {
type Output = Vector;
fn mul(self, rhs: f32) -> Self::Output {
Vector {
x: self.x * rhs,
y: self.y * rhs,
z: self.z * rhs,
}
}
}
impl PartialEq for Vector {
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y && self.z == other.z
}
}
impl PartialOrd for Vector {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.length_squared().partial_cmp(&other.length_squared())
}
}
impl From<Vector> for [f32; 3] {
fn from(vector: Vector) -> Self {
[vector.x, vector.y, vector.z]
}
}
impl From<[f32; 3]> for Vector {
fn from(vector: [f32; 3]) -> Self {
Vector {
x: vector[0],
y: vector[1],
z: vector[2],
}
}
}
impl From<&Vector> for [f32; 3] {
fn from(vector: &Vector) -> Self {
[vector.x, vector.y, vector.z]
}
}
impl FromStr for Vector {
type Err = EntityParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut floats = s.split_whitespace().map(f32::from_str);
let x = floats.next().ok_or(EntityParseError::ElementCount)??;
let y = floats.next().ok_or(EntityParseError::ElementCount)??;
let z = floats.next().ok_or(EntityParseError::ElementCount)??;
if floats.next().is_some() {
return Err(EntityParseError::ElementCount);
}
Ok(Vector { x, y, z })
}
}
impl From<Vector> for Vector3<f32> {
fn from(v: Vector) -> Self {
Vector3::new(v.x, v.y, v.z)
}
}
impl<'de> Deserialize<'de> for Vector {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let str = <&str>::deserialize(deserializer)?;
str.parse()
.map_err(|_| D::Error::invalid_value(Unexpected::Other(str), &"a vector"))
}
}

View file

@ -1,7 +1,7 @@
use crate::bspfile::LumpType;
use crate::data::*;
use std::num::{ParseFloatError, ParseIntError};
use thiserror::Error;
pub use vbsp_common::EntityParseError;
use zip::result::ZipError;
#[non_exhaustive]
@ -121,13 +121,3 @@ pub enum InvalidNeighbourError {
#[error("Invalid neighbour orientation")]
InvalidNeighbourOrientation(u8),
}
#[derive(Debug, Error)]
pub enum EntityParseError {
#[error("wrong number of elements")]
ElementCount,
#[error(transparent)]
Float(#[from] ParseFloatError),
#[error(transparent)]
Int(#[from] ParseIntError),
}

View file

@ -6,7 +6,6 @@ mod reader;
use crate::bspfile::LumpType;
pub use crate::data::TextureFlags;
pub use crate::data::Vector;
pub use crate::data::*;
use crate::error::ValidationError;
pub use crate::handle::Handle;
@ -18,6 +17,7 @@ use lzma_rs::decompress::{Options, UnpackedSize};
use reader::LumpReader;
use std::cmp::min;
use std::io::Read;
pub use vbsp_common::deserialize_bool;
pub type BspResult<T> = Result<T, BspError>;