fully switch to bytemuck for reading

This commit is contained in:
Robin Appelman 2022-03-19 16:46:32 +01:00
commit bb4dfddde2
11 changed files with 38 additions and 130 deletions

View file

@ -6,7 +6,6 @@ exclude = ["data"]
[dependencies] [dependencies]
arrayvec = "0.7.2" arrayvec = "0.7.2"
binrw = "0.8.0"
thiserror = "1.0.30" thiserror = "1.0.30"
static_assertions = "1.1.0" static_assertions = "1.1.0"
bitflags = "1.0.4" bitflags = "1.0.4"

View file

@ -7,31 +7,10 @@ pub enum ModelError {
IO(#[from] std::io::Error), IO(#[from] std::io::Error),
#[error(transparent)] #[error(transparent)]
String(#[from] StringError), String(#[from] StringError),
#[error("Malformed field found while parsing: {0:#}")]
MalformedData(binrw::Error),
#[error("referenced data to {data} is out of bounds at {offset}")] #[error("referenced data to {data} is out of bounds at {offset}")]
OutOfBounds { data: &'static str, offset: usize }, OutOfBounds { data: &'static str, offset: usize },
} }
impl From<binrw::Error> for ModelError {
fn from(e: binrw::Error) -> Self {
use binrw::Error;
// only a few error types should be generated by our code
match e {
Error::Io(e) => ModelError::IO(e),
Error::Custom { err, .. } => {
if err.is::<StringError>() {
ModelError::String(*err.downcast::<StringError>().unwrap())
} else {
panic!("unexpected custom error")
}
}
e => ModelError::MalformedData(e),
}
}
}
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum StringError { pub enum StringError {
#[error(transparent)] #[error(transparent)]

View file

@ -8,13 +8,11 @@ pub mod vvd;
use crate::mdl::Mdl; use crate::mdl::Mdl;
use crate::vtx::Vtx; use crate::vtx::Vtx;
use crate::vvd::{Vertex, Vvd}; use crate::vvd::{Vertex, Vvd};
use binrw::{BinRead, BinReaderExt};
use bytemuck::{pod_read_unaligned, Pod}; use bytemuck::{pod_read_unaligned, Pod};
pub use error::*; pub use error::*;
pub use handle::Handle; pub use handle::Handle;
pub use shared::*; pub use shared::*;
use std::any::type_name; use std::any::type_name;
use std::io::Cursor;
use std::mem::size_of; use std::mem::size_of;
pub struct Model { pub struct Model {
@ -82,13 +80,10 @@ impl Model {
} }
} }
fn read_indexes<'a, I: Iterator<Item = usize> + 'static, T: BinRead>( fn read_indexes<'a, I: Iterator<Item = usize> + 'static, T: Readable>(
indexes: I, indexes: I,
data: &'a [u8], data: &'a [u8],
) -> impl Iterator<Item = Result<T, ModelError>> + 'a ) -> impl Iterator<Item = Result<T, ModelError>> + 'a {
where
T::Args: Default,
{
indexes indexes
.map(|index| { .map(|index| {
data.get(index..).ok_or_else(|| ModelError::OutOfBounds { data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
@ -96,12 +91,7 @@ where
offset: index, offset: index,
}) })
}) })
.map(|data| { .map(|data| data.and_then(|data| T::read(data)))
data.and_then(|data| {
let mut cursor = Cursor::new(data);
cursor.read_le().map_err(ModelError::from)
})
})
} }
fn index_range(index: i32, count: i32, size: usize) -> impl Iterator<Item = usize> { fn index_range(index: i32, count: i32, size: usize) -> impl Iterator<Item = usize> {

View file

@ -5,8 +5,6 @@ pub use raw::header2::*;
use crate::mdl::raw::{BodyPartHeader, Bone, MeshHeader, ModelHeader}; use crate::mdl::raw::{BodyPartHeader, Bone, MeshHeader, ModelHeader};
use crate::{read_indexes, read_relative, FixedString, ModelError, ReadRelative, Readable}; use crate::{read_indexes, read_relative, FixedString, ModelError, ReadRelative, Readable};
use binrw::BinReaderExt;
use std::io::Cursor;
type Result<T> = std::result::Result<T, ModelError>; type Result<T> = std::result::Result<T, ModelError>;
@ -19,8 +17,7 @@ pub struct Mdl {
impl Mdl { impl Mdl {
pub fn read(data: &[u8]) -> Result<Self> { pub fn read(data: &[u8]) -> Result<Self> {
let mut reader = Cursor::new(data); let header = <StudioHeader as Readable>::read(data)?;
let header: StudioHeader = reader.read_le()?;
let bones = read_indexes(header.bone_indexes(), data).collect::<Result<_>>()?; let bones = read_indexes(header.bone_indexes(), data).collect::<Result<_>>()?;
Ok(Mdl { Ok(Mdl {
bones, bones,

View file

@ -1,18 +1,18 @@
use crate::mdl::raw::*; use crate::mdl::raw::*;
use crate::mdl::Bone; use crate::mdl::Bone;
use crate::{index_range, FixedString, Vector}; use crate::{index_range, Vector};
use binrw::BinRead;
use std::mem::size_of; use std::mem::size_of;
pub const FILETYPE_ID: i32 = i32::from_be_bytes(*b"IDST"); pub const FILETYPE_ID: i32 = i32::from_be_bytes(*b"IDST");
pub const MDL_VERSION: i32 = 48; pub const MDL_VERSION: i32 = 48;
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct StudioHeader { pub struct StudioHeader {
pub id: i32, pub id: i32,
pub version: i32, pub version: i32,
pub checksum: [u8; 4], // This has to be the same in the phy and vtx files to load! pub checksum: [u8; 4], // This has to be the same in the phy and vtx files to load!
pub name: FixedString<64>, pub name: [u8; 64],
pub data_length: i32, pub data_length: i32,
pub eye_position: Vector, // Position of player viewpoint relative to model origin pub eye_position: Vector, // Position of player viewpoint relative to model origin
@ -175,7 +175,8 @@ pub struct StudioHeader {
} }
bitflags! { bitflags! {
#[derive(BinRead)] #[derive(Zeroable, Pod)]
#[repr(C)]
pub struct ModelFlags: u32 { pub struct ModelFlags: u32 {
const AUTOGENERATED_HITBOX = 0x00000001; const AUTOGENERATED_HITBOX = 0x00000001;
const USES_ENV_CUBEMAP = 0x00000002; const USES_ENV_CUBEMAP = 0x00000002;
@ -308,4 +309,4 @@ impl StudioHeader {
} }
} }
static_assertions::const_assert_eq!(size_of::<StudioHeader>() - size_of::<FixedString<0>>(), 408); static_assertions::const_assert_eq!(size_of::<StudioHeader>(), 408);

View file

@ -1,14 +1,14 @@
use crate::{index_range, FixedString, Pod}; use crate::{index_range, FixedString};
use crate::{Quaternion, RadianEuler, Vector}; use crate::{Quaternion, RadianEuler, Vector};
use binrw::BinRead;
use bitflags::bitflags; use bitflags::bitflags;
use bytemuck::Zeroable; use bytemuck::{Pod, Zeroable};
use std::mem::size_of; use std::mem::size_of;
pub mod header; pub mod header;
pub mod header2; pub mod header2;
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct Bone { pub struct Bone {
pub sz_name_index: i32, pub sz_name_index: i32,
pub parent: i32, // parent bone pub parent: i32, // parent bone
@ -34,7 +34,8 @@ pub struct Bone {
} }
bitflags! { bitflags! {
#[derive(BinRead)] #[derive(Zeroable, Pod)]
#[repr(C)]
pub struct BoneFlags: u32 { pub struct BoneFlags: u32 {
const BONE_PHYSICALLY_SIMULATED = 0x00000001; const BONE_PHYSICALLY_SIMULATED = 0x00000001;
const BONE_PHYSICS_PROCEDURAL = 0x00000002; const BONE_PHYSICS_PROCEDURAL = 0x00000002;

View file

@ -1,12 +1,11 @@
use crate::{BinRead, ModelError, StringError}; use crate::{ModelError, StringError};
use arrayvec::ArrayString; use arrayvec::ArrayString;
use binrw::{BinResult, ReadOptions};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use std::fmt; use std::fmt;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::ops::Add; use std::ops::Add;
#[derive(Debug, Clone, Copy, BinRead, Zeroable, Pod)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)] #[repr(C)]
pub struct Vector { pub struct Vector {
pub x: f32, pub x: f32,
@ -54,7 +53,8 @@ impl Add<Vector> for Vector {
} }
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct Quaternion { pub struct Quaternion {
pub x: f32, pub x: f32,
pub y: f32, pub y: f32,
@ -62,7 +62,8 @@ pub struct Quaternion {
pub w: f32, pub w: f32,
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct RadianEuler { pub struct RadianEuler {
pub x: f32, pub x: f32,
pub y: f32, pub y: f32,
@ -107,38 +108,3 @@ impl<const LEN: usize> Display for FixedString<LEN> {
Display::fmt(&self.0, f) Display::fmt(&self.0, f)
} }
} }
impl<const LEN: usize> BinRead for FixedString<LEN> {
type Args = ();
fn read_options<R: binrw::io::Read + binrw::io::Seek>(
reader: &mut R,
options: &ReadOptions,
args: Self::Args,
) -> BinResult<Self> {
use std::str;
let name_buf = <[u8; LEN]>::read_options(reader, options, args)?;
let zero_pos =
name_buf
.iter()
.position(|c| *c == 0)
.ok_or_else(|| binrw::Error::Custom {
pos: reader.stream_position().unwrap(),
err: Box::new(StringError::NotNullTerminated),
})?;
let name = &name_buf[..zero_pos];
Ok(FixedString(
ArrayString::from(
str::from_utf8(name)
.map_err(StringError::NonUTF8)
.map_err(|e| binrw::Error::Custom {
pos: reader.stream_position().unwrap(),
err: Box::new(e),
})?,
)
.unwrap(),
))
}
}

View file

@ -1,11 +1,9 @@
mod raw; mod raw;
use crate::{read_relative, ModelError, ReadRelative}; use crate::{read_relative, ModelError, ReadRelative, Readable};
use binrw::BinReaderExt;
use itertools::Either; use itertools::Either;
use raw::*; use raw::*;
pub use raw::{MeshFlags, StripFlags, StripGroupFlags, Vertex}; pub use raw::{MeshFlags, StripFlags, StripGroupFlags, Vertex};
use std::io::Cursor;
use std::ops::Range; use std::ops::Range;
pub const MDL_VERSION: i32 = 7; pub const MDL_VERSION: i32 = 7;
@ -20,8 +18,7 @@ pub struct Vtx {
impl Vtx { impl Vtx {
pub fn read(data: &[u8]) -> Result<Self> { pub fn read(data: &[u8]) -> Result<Self> {
let mut reader = Cursor::new(data); let header = <VtxHeader as Readable>::read(data)?;
let header: VtxHeader = reader.read_le()?;
Ok(Vtx { Ok(Vtx {
body_parts: read_relative(data, header.body_indexes())?, body_parts: read_relative(data, header.body_indexes())?,
header, header,

View file

@ -1,11 +1,11 @@
use crate::{index_range, Pod}; use crate::{index_range, Pod};
use binrw::BinRead;
use bitflags::bitflags; use bitflags::bitflags;
use bytemuck::Zeroable; use bytemuck::Zeroable;
use std::mem::size_of; use std::mem::size_of;
use std::ops::Range; use std::ops::Range;
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct VtxHeader { pub struct VtxHeader {
pub version: i32, pub version: i32,
pub vertex_cache_size: i32, pub vertex_cache_size: i32,
@ -103,7 +103,7 @@ impl MeshHeader {
} }
bitflags! { bitflags! {
#[derive(BinRead, Zeroable, Pod)] #[derive(Zeroable, Pod)]
#[repr(C)] #[repr(C)]
pub struct MeshFlags: u8 { pub struct MeshFlags: u8 {
const IS_TEETH = 0x01; const IS_TEETH = 0x01;
@ -150,7 +150,7 @@ impl StripGroupHeader {
} }
bitflags! { bitflags! {
#[derive(BinRead, Zeroable, Pod)] #[derive(Zeroable, Pod)]
#[repr(C)] #[repr(C)]
pub struct StripGroupFlags: u8 { pub struct StripGroupFlags: u8 {
const IS_FLEXED = 0x01; const IS_FLEXED = 0x01;
@ -177,7 +177,7 @@ pub struct StripHeader {
static_assertions::const_assert_eq!(size_of::<StripHeader>(), 27); static_assertions::const_assert_eq!(size_of::<StripHeader>(), 27);
bitflags! { bitflags! {
#[derive(BinRead, Zeroable, Pod)] #[derive(Zeroable, Pod)]
#[repr(C)] #[repr(C)]
pub struct StripFlags: u8 { pub struct StripFlags: u8 {
const IS_TRI_LIST = 0x01; const IS_TRI_LIST = 0x01;

View file

@ -1,10 +1,8 @@
mod raw; mod raw;
use crate::vvd::raw::{VertexFileFixup, VvdHeader}; use crate::vvd::raw::{VertexFileFixup, VvdHeader};
use crate::{read_relative, read_relative_iter, ModelError}; use crate::{read_relative, read_relative_iter, ModelError, Readable};
use binrw::BinReaderExt;
pub use raw::{BoneWeight, Tangent, Vertex}; pub use raw::{BoneWeight, Tangent, Vertex};
use std::io::Cursor;
type Result<T> = std::result::Result<T, ModelError>; type Result<T> = std::result::Result<T, ModelError>;
@ -16,8 +14,7 @@ pub struct Vvd {
impl Vvd { impl Vvd {
pub fn read(data: &[u8]) -> Result<Self> { pub fn read(data: &[u8]) -> Result<Self> {
let mut reader = Cursor::new(data); let header = <VvdHeader as Readable>::read(data)?;
let header: VvdHeader = reader.read_le()?;
let source_vertices = read_relative(data, header.vertex_indexes(0).unwrap())?; let source_vertices = read_relative(data, header.vertex_indexes(0).unwrap())?;
let vertices = if !header.has_fixups() { let vertices = if !header.has_fixups() {
source_vertices source_vertices

View file

@ -1,10 +1,9 @@
use crate::{index_range, Vector}; use crate::{index_range, Vector};
use binrw::{BinRead, BinResult, ReadOptions}; use bytemuck::{Pod, Zeroable};
use bytemuck::{cast, Pod, Zeroable};
use std::io::{Read, Seek};
use std::mem::size_of; use std::mem::size_of;
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct VvdHeader { pub struct VvdHeader {
pub id: i32, pub id: i32,
pub version: i32, pub version: i32,
@ -44,7 +43,7 @@ impl VvdHeader {
} }
} }
#[derive(Debug, Clone, BinRead, Zeroable, Pod, Copy)] #[derive(Debug, Clone, Zeroable, Pod, Copy)]
#[repr(C)] #[repr(C)]
pub struct VertexFileFixup { pub struct VertexFileFixup {
pub lod: i32, pub lod: i32,
@ -62,26 +61,8 @@ pub struct Vertex {
} }
static_assertions::const_assert_eq!(size_of::<Vertex>(), 48); static_assertions::const_assert_eq!(size_of::<Vertex>(), 48);
// binread_for_pod!(Vertex);
impl BinRead for Vertex { #[derive(Debug, Clone, Zeroable, Pod, Copy)]
type Args = ();
fn read_options<R: Read + Seek>(
reader: &mut R,
_options: &ReadOptions,
_args: Self::Args,
) -> BinResult<Self> {
let mut bytes = unsafe {
std::mem::MaybeUninit::<[u8; std::mem::size_of::<Self>()]>::uninit().assume_init()
};
reader.read(&mut bytes)?;
Ok(cast(bytes))
}
}
#[derive(Debug, Clone, BinRead, Zeroable, Pod, Copy)]
#[repr(C)] #[repr(C)]
pub struct BoneWeight { pub struct BoneWeight {
pub weight: [f32; 3], pub weight: [f32; 3],
@ -91,7 +72,7 @@ pub struct BoneWeight {
static_assertions::const_assert_eq!(size_of::<BoneWeight>(), 16); static_assertions::const_assert_eq!(size_of::<BoneWeight>(), 16);
#[derive(Debug, Clone, BinRead, Zeroable, Pod, Copy)] #[derive(Debug, Clone, Zeroable, Pod, Copy)]
#[repr(C)] #[repr(C)]
pub struct Tangent { pub struct Tangent {
pub x: f32, pub x: f32,