2024-01-16 03:09:34 +00:00
|
|
|
//file format "sniff"
|
|
|
|
|
2024-01-19 03:28:01 +00:00
|
|
|
use binrw::{binrw, BinReaderExt, io::TakeSeekExt};
|
2024-01-19 00:59:00 +00:00
|
|
|
|
2024-07-26 00:47:00 +00:00
|
|
|
#[derive(Debug)]
|
2024-01-16 03:09:34 +00:00
|
|
|
pub enum Error{
|
2024-01-19 00:59:00 +00:00
|
|
|
InvalidHeader(binrw::Error),
|
2024-01-16 03:30:26 +00:00
|
|
|
UnexpectedEOF,
|
2024-02-07 09:01:23 +00:00
|
|
|
InvalidBlockId(BlockId),
|
2024-01-19 03:28:01 +00:00
|
|
|
Seek(std::io::Error),
|
2024-01-16 03:09:34 +00:00
|
|
|
}
|
2024-07-26 00:47:00 +00:00
|
|
|
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{}
|
2024-01-16 03:09:34 +00:00
|
|
|
|
|
|
|
/* 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
|
2024-07-23 20:12:54 +00:00
|
|
|
//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{
|
2024-01-16 03:09:34 +00:00
|
|
|
u64 first_byte
|
|
|
|
}
|
|
|
|
|
|
|
|
//end global header
|
|
|
|
|
|
|
|
//begin blocks
|
|
|
|
|
|
|
|
//each block is compressed with zstd or gz or something
|
|
|
|
|
|
|
|
*/
|
2024-01-19 00:59:00 +00:00
|
|
|
#[binrw]
|
|
|
|
#[brw(little)]
|
2024-07-26 00:47:00 +00:00
|
|
|
#[derive(Clone,Copy,Debug)]
|
2024-01-19 00:39:58 +00:00
|
|
|
pub(crate) enum FourCC{
|
2024-01-19 00:59:00 +00:00
|
|
|
#[brw(magic=b"SNFM")]
|
2024-01-19 00:39:58 +00:00
|
|
|
Map,
|
2024-01-19 00:59:00 +00:00
|
|
|
#[brw(magic=b"SNFB")]
|
2024-01-19 00:39:58 +00:00
|
|
|
Bot,
|
2024-01-19 00:59:00 +00:00
|
|
|
#[brw(magic=b"SNFD")]
|
2024-01-19 00:39:58 +00:00
|
|
|
Demo,
|
2024-01-16 03:09:34 +00:00
|
|
|
}
|
2024-01-19 00:59:00 +00:00
|
|
|
#[binrw]
|
|
|
|
#[brw(little)]
|
2024-07-26 00:47:00 +00:00
|
|
|
#[derive(Debug)]
|
2024-07-25 23:07:26 +00:00
|
|
|
pub struct Header{
|
2024-01-16 03:09:34 +00:00
|
|
|
/// Type of file
|
2024-07-25 23:07:26 +00:00
|
|
|
pub fourcc:FourCC,
|
|
|
|
/// File format version
|
|
|
|
pub version:u32,
|
2024-01-16 03:09:34 +00:00
|
|
|
/// Minimum data required to know the location of all streamable resources for this specific file
|
2024-07-25 23:07:26 +00:00
|
|
|
pub priming:u64,
|
2024-01-16 03:09:34 +00:00
|
|
|
/// uuid for this file
|
2024-07-25 23:07:26 +00:00
|
|
|
pub resource:u128,
|
2024-07-24 00:18:10 +00:00
|
|
|
//don't need try_calc: the struct will force field initialization anyways and it can be calculated there
|
2024-07-25 23:07:26 +00:00
|
|
|
pub block_count:u32,
|
2024-01-19 03:28:01 +00:00
|
|
|
#[br(count=block_count+1)]
|
2024-07-25 23:07:26 +00:00
|
|
|
pub block_location:Vec<u64>,
|
2024-01-16 03:09:34 +00:00
|
|
|
}
|
|
|
|
|
2024-02-07 09:01:23 +00:00
|
|
|
#[binrw]
|
|
|
|
#[brw(little)]
|
2024-07-26 23:13:51 +00:00
|
|
|
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,Ord,PartialEq,PartialOrd)]
|
2024-02-07 09:01:23 +00:00
|
|
|
pub struct BlockId(u32);
|
2024-01-17 04:07:11 +00:00
|
|
|
|
2024-01-19 03:28:01 +00:00
|
|
|
pub(crate) struct File<R:BinReaderExt>{
|
2024-01-19 00:39:58 +00:00
|
|
|
header:Header,
|
|
|
|
//reference to the data
|
2024-01-19 03:28:01 +00:00
|
|
|
data:R,
|
2024-01-19 00:39:58 +00:00
|
|
|
}
|
|
|
|
|
2024-01-19 03:28:01 +00:00
|
|
|
impl<R:BinReaderExt> File<R>{
|
|
|
|
pub(crate) fn new(mut input:R)->Result<File<R>,Error>{
|
|
|
|
Ok(File{
|
2024-02-07 09:01:23 +00:00
|
|
|
header:input.read_le().map_err(Error::InvalidHeader)?,
|
2024-01-19 03:28:01 +00:00
|
|
|
data:input,
|
2024-01-19 00:59:00 +00:00
|
|
|
})
|
2024-01-19 00:39:58 +00:00
|
|
|
}
|
2024-07-25 17:48:04 +00:00
|
|
|
pub(crate) fn as_mut(&mut self)->&mut R{
|
|
|
|
&mut self.data
|
|
|
|
}
|
2024-07-24 00:18:03 +00:00
|
|
|
pub(crate) fn block_reader(&mut self,block_id:BlockId)->Result<binrw::io::TakeSeek<&mut R>,Error>{
|
2024-02-07 09:01:23 +00:00
|
|
|
if self.header.block_location.len() as u32<=block_id.get(){
|
|
|
|
return Err(Error::InvalidBlockId(block_id))
|
2024-01-19 03:28:01 +00:00
|
|
|
}
|
2024-02-07 09:01:23 +00:00
|
|
|
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)?;
|
2024-07-25 17:48:04 +00:00
|
|
|
Ok(self.as_mut().take_seek(block_end-block_start))
|
2024-01-19 00:39:58 +00:00
|
|
|
}
|
|
|
|
pub(crate) fn fourcc(&self)->FourCC{
|
|
|
|
self.header.fourcc
|
|
|
|
}
|
2024-01-17 04:07:11 +00:00
|
|
|
}
|