Compare commits
2 Commits
graphics-b
...
refactor-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
ce50fcc082
|
|||
|
c830e67ab7
|
@@ -81,6 +81,15 @@ pub struct SegmentId(u32);
|
||||
pub struct Segment{
|
||||
pub instructions:Vec<TimedPhysicsInstruction>
|
||||
}
|
||||
impl Segment{
|
||||
fn append<R:BinReaderExt>(&mut self,segment_info:&SegmentInfo,mut data:R)->Result<(),Error>{
|
||||
for _ in 0..segment_info.instruction_count{
|
||||
let instruction:newtypes::physics::TimedInstruction=data.read_le().map_err(Error::InvalidSegment)?;
|
||||
self.instructions.push(instruction.try_into().map_err(Error::SegmentConvert)?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct SegmentInfo{
|
||||
@@ -94,14 +103,13 @@ pub struct SegmentInfo{
|
||||
block_id:BlockId,
|
||||
}
|
||||
|
||||
pub struct StreamableBot<R:BinReaderExt>{
|
||||
file:crate::file::File<R>,
|
||||
pub struct StreamableBot{
|
||||
segment_map:Vec<SegmentInfo>,
|
||||
}
|
||||
impl<R:BinReaderExt> StreamableBot<R>{
|
||||
pub(crate) fn new(mut file:crate::file::File<R>)->Result<Self,Error>{
|
||||
impl StreamableBot{
|
||||
pub(crate) fn new<R:BinReaderExt>(mut data:R)->Result<Self,Error>{
|
||||
//assume the file seek is in the right place to start reading header
|
||||
let header:Header=file.data_mut().read_le().map_err(Error::InvalidHeader)?;
|
||||
let header:Header=data.read_le().map_err(Error::InvalidHeader)?;
|
||||
let mut instructions_subtotal=0;
|
||||
let segment_map=header.segments.into_iter().map(|SegmentHeader{time,instruction_count,block_id}|{
|
||||
instructions_subtotal+=instruction_count as u64;
|
||||
@@ -113,13 +121,12 @@ impl<R:BinReaderExt> StreamableBot<R>{
|
||||
}
|
||||
}).collect();
|
||||
Ok(Self{
|
||||
file,
|
||||
segment_map,
|
||||
})
|
||||
}
|
||||
#[expect(dead_code)]
|
||||
fn get_segment_info(&self,segment_id:SegmentId)->Result<SegmentInfo,Error>{
|
||||
Ok(*self.segment_map.get(segment_id.get() as usize).ok_or(Error::InvalidSegmentId(segment_id))?)
|
||||
fn get_segment_info(&self,segment_id:SegmentId)->Result<&SegmentInfo,Error>{
|
||||
self.segment_map.get(segment_id.get() as usize).ok_or(Error::InvalidSegmentId(segment_id))
|
||||
}
|
||||
pub fn find_segments_instruction_range(&self,start_instruction:u64,end_instruction:u64)->&[SegmentInfo]{
|
||||
let start=self.segment_map.partition_point(|segment_info|segment_info.instructions_subtotal<start_instruction);
|
||||
@@ -132,28 +139,21 @@ impl<R:BinReaderExt> StreamableBot<R>{
|
||||
// let end=self.segment_map.partition_point(|segment_info|segment_info.time<end_time);
|
||||
// &self.segment_map[start..=end]
|
||||
// }
|
||||
fn append_to_segment(&mut self,segment_info:SegmentInfo,segment:&mut Segment)->Result<(),Error>{
|
||||
let mut block=self.file.block_reader(segment_info.block_id).map_err(Error::File)?;
|
||||
for _ in 0..segment_info.instruction_count{
|
||||
let instruction:newtypes::physics::TimedInstruction=block.read_le().map_err(Error::InvalidSegment)?;
|
||||
segment.instructions.push(instruction.try_into().map_err(Error::SegmentConvert)?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn load_segment(&mut self,segment_info:SegmentInfo)->Result<Segment,Error>{
|
||||
pub fn load_segment<R:BinReaderExt>(&self,segment_info:&SegmentInfo,mut data:R)->Result<Segment,Error>{
|
||||
let mut segment=Segment{
|
||||
instructions:Vec::with_capacity(segment_info.instruction_count as usize),
|
||||
};
|
||||
self.append_to_segment(segment_info,&mut segment)?;
|
||||
segment.append(segment_info,data)?;
|
||||
Ok(segment)
|
||||
}
|
||||
pub fn read_all(&mut self)->Result<Segment,Error>{
|
||||
pub fn read_all<R:BinReaderExt>(self,header:&crate::file::Header,mut data:R)->Result<Segment,Error>{
|
||||
let mut segment=Segment{
|
||||
instructions:Vec::new(),
|
||||
};
|
||||
for i in 0..self.segment_map.len(){
|
||||
let segment_info=self.segment_map[i];
|
||||
self.append_to_segment(segment_info,&mut segment)?;
|
||||
for segment_info in &self.segment_map{
|
||||
let block_info=header.block_info(segment_info.block_id).map_err(Error::File)?;
|
||||
let block=block_info.take_seek(&mut data).map_err(Error::IO)?;
|
||||
segment.append(segment_info,block)?;
|
||||
}
|
||||
Ok(segment)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//file format "sniff"
|
||||
|
||||
use binrw::{binrw,BinReaderExt,io::TakeSeekExt};
|
||||
use std::io::{SeekFrom,Error as IoError};
|
||||
use binrw::binrw;
|
||||
use binrw::BinReaderExt;
|
||||
use binrw::io::{TakeSeek,TakeSeekExt};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error{
|
||||
@@ -54,65 +56,69 @@ pub(crate) enum FourCC{
|
||||
#[brw(magic=b"SNFB")]
|
||||
Bot,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,Ord,PartialEq,PartialOrd)]
|
||||
pub struct BlockId(u32);
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone)]
|
||||
struct BlockPosition(u64);
|
||||
|
||||
/// The range of data for a specific Block, relative to the start of the file.
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct BlockInfo(core::ops::Range<u64>);
|
||||
impl BlockInfo{
|
||||
/// Create an adapter which seeks to the block start and reads at most the block length.
|
||||
pub fn take_seek<R:BinReaderExt>(&self,mut data:R)->Result<TakeSeek<R>,IoError>{
|
||||
data.seek(SeekFrom::Start(self.start))?;
|
||||
Ok(data.take_seek(self.end-self.start))
|
||||
}
|
||||
}
|
||||
impl core::ops::Deref for BlockInfo{
|
||||
type Target=core::ops::Range<u64>;
|
||||
fn deref(&self)->&Self::Target{
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug)]
|
||||
pub struct Header{
|
||||
/// Type of file
|
||||
pub fourcc:FourCC,
|
||||
fourcc:FourCC,
|
||||
/// File format version
|
||||
pub version:u32,
|
||||
version:u32,
|
||||
/// Minimum data required to know the location of all streamable resources for this specific file
|
||||
pub priming:u64,
|
||||
priming:u64,
|
||||
/// uuid for this file
|
||||
pub resource:u128,
|
||||
resource:u128,
|
||||
//don't need try_calc: the struct will force field initialization anyways and it can be calculated there
|
||||
pub block_count:u32,
|
||||
block_count:u32,
|
||||
#[br(count=block_count+1)]
|
||||
pub block_location:Vec<u64>,
|
||||
block_positions:Vec<BlockPosition>,
|
||||
}
|
||||
impl Header{
|
||||
pub const fn fourcc(&self)->FourCC{
|
||||
self.fourcc
|
||||
}
|
||||
pub const fn calculate_size(block_count:u32)->usize{
|
||||
4 // fourcc
|
||||
+4 // version
|
||||
+8 // priming
|
||||
+16 // resource
|
||||
+4 // block_count
|
||||
+(block_count as usize+1)*8 // block_location
|
||||
+(block_count as usize+1)*8 // block_positions
|
||||
}
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,Ord,PartialEq,PartialOrd)]
|
||||
pub struct BlockId(u32);
|
||||
|
||||
pub(crate) struct File<R:BinReaderExt>{
|
||||
header:Header,
|
||||
//reference to the data
|
||||
data:R,
|
||||
}
|
||||
|
||||
impl<R:BinReaderExt> File<R>{
|
||||
pub(crate) fn new(mut input:R)->Result<File<R>,Error>{
|
||||
Ok(File{
|
||||
header:input.read_le().map_err(Error::InvalidHeader)?,
|
||||
data:input,
|
||||
})
|
||||
}
|
||||
pub(crate) fn data_mut(&mut self)->&mut R{
|
||||
&mut self.data
|
||||
}
|
||||
pub(crate) fn block_reader(&mut self,block_id:BlockId)->Result<binrw::io::TakeSeek<&mut R>,Error>{
|
||||
if self.header.block_location.len() as u32<=block_id.get(){
|
||||
pub fn block_info(&self,block_id:BlockId)->Result<BlockInfo,Error>{
|
||||
let BlockId(id)=block_id;
|
||||
if self.block_positions.len() as u32<=id{
|
||||
return Err(Error::InvalidBlockId(block_id))
|
||||
}
|
||||
let block_start=self.header.block_location[block_id.get() as usize];
|
||||
let block_end=self.header.block_location[block_id.get() as usize+1];
|
||||
self.data.seek(std::io::SeekFrom::Start(block_start)).map_err(Error::Seek)?;
|
||||
Ok(self.data_mut().take_seek(block_end-block_start))
|
||||
}
|
||||
pub(crate) fn fourcc(&self)->FourCC{
|
||||
self.header.fourcc
|
||||
let BlockPosition(start)=self.block_positions[id as usize];
|
||||
let BlockPosition(end)=self.block_positions[id as usize+1];
|
||||
Ok(BlockInfo(start..end))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,31 +20,34 @@ impl std::fmt::Display for Error{
|
||||
}
|
||||
impl std::error::Error for Error{}
|
||||
|
||||
pub enum SNF<R:BinReaderExt>{
|
||||
Map(map::StreamableMap<R>),
|
||||
Bot(bot::StreamableBot<R>),
|
||||
pub enum SNF{
|
||||
Map(map::StreamableMap),
|
||||
Bot(bot::StreamableBot),
|
||||
}
|
||||
|
||||
pub fn read_snf<R:BinReaderExt>(input:R)->Result<SNF<R>,Error>{
|
||||
let file=file::File::new(input).map_err(Error::Header)?;
|
||||
Ok(match file.fourcc(){
|
||||
file::FourCC::Map=>SNF::Map(map::StreamableMap::new(file).map_err(Error::Map)?),
|
||||
file::FourCC::Bot=>SNF::Bot(bot::StreamableBot::new(file).map_err(Error::Bot)?),
|
||||
})
|
||||
pub fn read_snf<R:BinReaderExt>(mut input:R)->Result<(file::Header,SNF),Error>{
|
||||
let file:file::Header=input.read_le().map_err(|e|Error::Header(file::Error::InvalidHeader(e)))?;
|
||||
let snf=match file.fourcc(){
|
||||
file::FourCC::Map=>SNF::Map(map::StreamableMap::new(input).map_err(Error::Map)?),
|
||||
file::FourCC::Bot=>SNF::Bot(bot::StreamableBot::new(input).map_err(Error::Bot)?),
|
||||
};
|
||||
Ok((file,snf))
|
||||
}
|
||||
pub fn read_map<R:BinReaderExt>(input:R)->Result<map::StreamableMap<R>,Error>{
|
||||
let file=file::File::new(input).map_err(Error::Header)?;
|
||||
match file.fourcc(){
|
||||
file::FourCC::Map=>Ok(map::StreamableMap::new(file).map_err(Error::Map)?),
|
||||
_=>Err(Error::UnexpectedFourCC)
|
||||
}
|
||||
pub fn read_map<R:BinReaderExt>(mut input:R)->Result<(file::Header,map::StreamableMap),Error>{
|
||||
let file:file::Header=input.read_le().map_err(|e|Error::Header(file::Error::InvalidHeader(e)))?;
|
||||
let map=match file.fourcc(){
|
||||
file::FourCC::Map=>map::StreamableMap::new(input).map_err(Error::Map)?,
|
||||
_=>return Err(Error::UnexpectedFourCC)
|
||||
};
|
||||
Ok((file,map))
|
||||
}
|
||||
pub fn read_bot<R:BinReaderExt>(input:R)->Result<bot::StreamableBot<R>,Error>{
|
||||
let file=file::File::new(input).map_err(Error::Header)?;
|
||||
match file.fourcc(){
|
||||
file::FourCC::Bot=>Ok(bot::StreamableBot::new(file).map_err(Error::Bot)?),
|
||||
_=>Err(Error::UnexpectedFourCC)
|
||||
}
|
||||
pub fn read_bot<R:BinReaderExt>(mut input:R)->Result<(file::Header,bot::StreamableBot),Error>{
|
||||
let file:file::Header=input.read_le().map_err(|e|Error::Header(file::Error::InvalidHeader(e)))?;
|
||||
let bot=match file.fourcc(){
|
||||
file::FourCC::Bot=>bot::StreamableBot::new(input).map_err(Error::Bot)?,
|
||||
_=>return Err(Error::UnexpectedFourCC)
|
||||
};
|
||||
Ok((file,bot))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -155,31 +155,27 @@ struct Region{
|
||||
}
|
||||
|
||||
//code deduplication reused in into_complete_map
|
||||
fn read_region<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)->Result<Vec<(model::ModelId,model::Model)>,Error>{
|
||||
pub fn read_region<R:BinReaderExt>(mut data:R)->Result<Vec<(model::ModelId,model::Model)>,Error>{
|
||||
//load region from disk
|
||||
//parse the models and determine what resources need to be loaded
|
||||
//load resources into self.resources
|
||||
//return Region
|
||||
let mut block=file.block_reader(block_id).map_err(Error::File)?;
|
||||
let region:Region=block.read_le().map_err(Error::InvalidData)?;
|
||||
let region:Region=data.read_le().map_err(Error::InvalidData)?;
|
||||
Ok(region.models.into_iter().map(|(model_id,model)|
|
||||
(model::ModelId::new(model_id),model.into())
|
||||
).collect())
|
||||
}
|
||||
fn read_mesh<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)->Result<model::Mesh,Error>{
|
||||
let mut block=file.block_reader(block_id).map_err(Error::File)?;
|
||||
let mesh:newtypes::model::Mesh=block.read_le().map_err(Error::InvalidData)?;
|
||||
pub fn read_mesh<R:BinReaderExt>(mut data:R)->Result<model::Mesh,Error>{
|
||||
let mesh:newtypes::model::Mesh=data.read_le().map_err(Error::InvalidData)?;
|
||||
Ok(mesh.into())
|
||||
}
|
||||
fn read_texture<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)->Result<Vec<u8>,Error>{
|
||||
let mut block=file.block_reader(block_id).map_err(Error::File)?;
|
||||
pub fn read_texture<R:BinReaderExt>(mut data:R)->Result<Vec<u8>,Error>{
|
||||
let mut texture=Vec::new();
|
||||
block.read_to_end(&mut texture).map_err(Error::IO)?;
|
||||
data.read_to_end(&mut texture).map_err(Error::IO)?;
|
||||
Ok(texture)
|
||||
}
|
||||
|
||||
pub struct StreamableMap<R:BinReaderExt>{
|
||||
file:crate::file::File<R>,
|
||||
pub struct StreamableMap{
|
||||
//this includes every platform... move the unconstrained datas to their appropriate data block?
|
||||
modes:gameplay_modes::NormalizedModes,
|
||||
//this is every possible attribute... need some sort of streaming system
|
||||
@@ -192,10 +188,10 @@ pub struct StreamableMap<R:BinReaderExt>{
|
||||
resource_blocks:ResourceMap<BlockId>,
|
||||
//resource_external:ResourceMap<ResourceId>,
|
||||
}
|
||||
impl<R:BinReaderExt> StreamableMap<R>{
|
||||
pub(crate) fn new(mut file:crate::file::File<R>)->Result<Self,Error>{
|
||||
impl StreamableMap{
|
||||
pub(crate) fn new<R:BinReaderExt>(mut data:R)->Result<Self,Error>{
|
||||
//assume the file seek is in the right place to start reading a map header
|
||||
let header:MapHeader=file.data_mut().read_le().map_err(Error::InvalidHeader)?;
|
||||
let header:MapHeader=data.read_le().map_err(Error::InvalidHeader)?;
|
||||
let modes=header.modes.into_iter().map(TryInto::try_into).collect::<Result<_,_>>().map_err(Error::InvalidMode)?;
|
||||
let attributes=header.attributes.into_iter().map(Into::into).collect();
|
||||
let render_configs=header.render_configs.into_iter().map(Into::into).collect();
|
||||
@@ -222,7 +218,6 @@ impl<R:BinReaderExt> StreamableMap<R>{
|
||||
}
|
||||
}
|
||||
Ok(Self{
|
||||
file,
|
||||
modes:gameplay_modes::NormalizedModes::new(modes),
|
||||
attributes,
|
||||
render_configs,
|
||||
@@ -236,18 +231,13 @@ impl<R:BinReaderExt> StreamableMap<R>{
|
||||
self.bvh.sample_aabb(aabb,&mut |&block_id|block_ids.push(block_id));
|
||||
block_ids
|
||||
}
|
||||
pub fn load_region(&mut self,block_id:BlockId)->Result<Vec<(model::ModelId,model::Model)>,Error>{
|
||||
read_region(&mut self.file,block_id)
|
||||
pub fn get_mesh_block_id<R:BinReaderExt>(&self,mesh_id:model::MeshId)->Result<&BlockId,Error>{
|
||||
self.resource_blocks.meshes.get(&mesh_id).ok_or(Error::InvalidMeshId(mesh_id))
|
||||
}
|
||||
pub fn load_mesh(&mut self,mesh_id:model::MeshId)->Result<model::Mesh,Error>{
|
||||
let block_id=*self.resource_blocks.meshes.get(&mesh_id).ok_or(Error::InvalidMeshId(mesh_id))?;
|
||||
read_mesh(&mut self.file,block_id)
|
||||
pub fn get_texture_block_id<R:BinReaderExt>(&self,texture_id:model::TextureId)->Result<&BlockId,Error>{
|
||||
self.resource_blocks.textures.get(&texture_id).ok_or(Error::InvalidTextureId(texture_id))
|
||||
}
|
||||
pub fn load_texture(&mut self,texture_id:model::TextureId)->Result<Vec<u8>,Error>{
|
||||
let block_id=*self.resource_blocks.textures.get(&texture_id).ok_or(Error::InvalidTextureId(texture_id))?;
|
||||
read_texture(&mut self.file,block_id)
|
||||
}
|
||||
pub fn into_complete_map(mut self)->Result<strafesnet_common::map::CompleteMap,Error>{
|
||||
pub fn into_complete_map<R:BinReaderExt>(self,header:&crate::file::Header,mut data:R)->Result<strafesnet_common::map::CompleteMap,Error>{
|
||||
let mut block_ids=Vec::new();
|
||||
self.bvh.into_visitor(&mut |block_id|block_ids.push(block_id));
|
||||
//count on reading the file in sequential order being fastest
|
||||
@@ -255,7 +245,9 @@ impl<R:BinReaderExt> StreamableMap<R>{
|
||||
//load all regions
|
||||
let mut model_pairs=HashMap::new();
|
||||
for block_id in block_ids{
|
||||
model_pairs.extend(read_region(&mut self.file,block_id)?);
|
||||
let block_info=header.block_info(block_id).map_err(Error::File)?;
|
||||
let block=block_info.take_seek(&mut data).map_err(Error::IO)?;
|
||||
model_pairs.extend(read_region(block)?);
|
||||
}
|
||||
let mut models=Vec::with_capacity(model_pairs.len());
|
||||
for model_id in 0..model_pairs.len() as u32{
|
||||
@@ -267,14 +259,18 @@ impl<R:BinReaderExt> StreamableMap<R>{
|
||||
for mesh_id in 0..self.resource_blocks.meshes.len() as u32{
|
||||
let mesh_id=model::MeshId::new(mesh_id);
|
||||
let block_id=self.resource_blocks.meshes[&mesh_id];
|
||||
meshes.push(read_mesh(&mut self.file,block_id)?);
|
||||
let block_info=header.block_info(block_id).map_err(Error::File)?;
|
||||
let block=block_info.take_seek(&mut data).map_err(Error::IO)?;
|
||||
meshes.push(read_mesh(block)?);
|
||||
};
|
||||
//load all textures
|
||||
let mut textures=Vec::with_capacity(self.resource_blocks.textures.len());
|
||||
for texture_id in 0..self.resource_blocks.textures.len() as u32{
|
||||
let texture_id=model::TextureId::new(texture_id);
|
||||
let block_id=self.resource_blocks.textures[&texture_id];
|
||||
textures.push(read_texture(&mut self.file,block_id)?);
|
||||
let block_info=header.block_info(block_id).map_err(Error::File)?;
|
||||
let block=block_info.take_seek(&mut data).map_err(Error::IO)?;
|
||||
textures.push(read_texture(block)?);
|
||||
}
|
||||
Ok(strafesnet_common::map::CompleteMap{
|
||||
modes:self.modes,
|
||||
|
||||
Reference in New Issue
Block a user