//file format "sniff" use binrw::{binrw,BinReaderExt,io::TakeSeekExt}; #[derive(Debug)] pub enum Error{ InvalidHeader(binrw::Error), UnexpectedEOF, InvalidBlockId(BlockId), Seek(std::io::Error), } impl std::fmt::Display for Error{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ write!(f,"{self:?}") } } impl std::error::Error for Error{} /* spec //begin global header //global metadata (32 bytes) b"SNFB" u32 format_version u64 priming_bytes //how many bytes of the file must be read to guarantee all of the expected //format-specific metadata is available to facilitate streaming the remaining contents //used by the database to guarantee that it serves at least the bare minimum u128 resource_uuid //identifies the file from anywhere for any other file //global block layout (variable size) u64 num_blocks //the start of the first block is implicitly after the global header (32) //num_blocks+1 used in Header.block_location is implicitly the end of the file for block_id in 1..num_blocks{ u64 first_byte } //end global header //begin blocks //each block is compressed with zstd or gz or something */ #[binrw] #[brw(little)] #[derive(Clone,Copy,Debug)] pub(crate) enum FourCC{ #[brw(magic=b"SNFM")] Map, #[brw(magic=b"SNFB")] Bot, #[brw(magic=b"SNFD")] Demo, } #[binrw] #[brw(little)] #[derive(Debug)] pub struct Header{ /// Type of file pub fourcc:FourCC, /// File format version pub version:u32, /// Minimum data required to know the location of all streamable resources for this specific file pub priming:u64, /// uuid for this file pub resource:u128, //don't need try_calc: the struct will force field initialization anyways and it can be calculated there pub block_count:u32, #[br(count=block_count+1)] pub block_location:Vec, } #[binrw] #[brw(little)] #[derive(Clone,Copy,Debug,Hash,id::Id,Eq,Ord,PartialEq,PartialOrd)] pub struct BlockId(u32); pub(crate) struct File{ header:Header, //reference to the data data:R, } impl File{ pub(crate) fn new(mut input:R)->Result,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,Error>{ if self.header.block_location.len() as u32<=block_id.get(){ 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 } }