Compare commits

...

2 Commits

Author SHA1 Message Date
ce50fcc082 the more 2026-02-26 17:27:44 -08:00
c830e67ab7 wip 2026-02-26 10:30:52 -08:00
4 changed files with 117 additions and 112 deletions

View File

@@ -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)
}

View File

@@ -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))
}
}

View File

@@ -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)]

View File

@@ -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,