//file format "sniff" use binrw::{binrw, BinReaderExt, io::TakeSeekExt}; pub enum Error{ InvalidHeader(binrw::Error), UnexpectedEOF, InvalidBlockId(BlockId), Seek(std::io::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)] pub(crate) enum FourCC{ #[brw(magic=b"SNFM")] Map, #[brw(magic=b"SNFB")] Bot, #[brw(magic=b"SNFD")] Demo, } #[binrw] #[brw(little)] struct Header{ /// Type of file fourcc:FourCC, /// Type version version:u32, /// Minimum data required to know the location of all streamable resources for this specific file priming:u64, /// uuid for this file resource:u128, //don't need try_calc: the struct will force field initialization anyways and it can be calculated there block_count:u32, #[br(count=block_count+1)] block_location:Vec, } #[binrw] #[brw(little)] #[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)] 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 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((&mut self.data).take_seek(block_end-block_start)) } pub(crate) fn fourcc(&self)->FourCC{ self.header.fourcc } }