Compare commits
10 Commits
a9504a192b
...
d5af08730b
Author | SHA1 | Date | |
---|---|---|---|
d5af08730b | |||
14e7e4df29 | |||
040b607792 | |||
5ea8a2db5f | |||
2d420d7676 | |||
6f22aaeec4 | |||
134e3fc79a | |||
b2673f1732 | |||
ec60086109 | |||
fc1c9e843f |
86
Cargo.lock
generated
86
Cargo.lock
generated
@ -2,6 +2,92 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "array-init"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc"
|
||||
|
||||
[[package]]
|
||||
name = "binrw"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "173901312e9850391d4d7c1318c4e099fdc037d61870fca427429830efdb4e5f"
|
||||
dependencies = [
|
||||
"array-init",
|
||||
"binrw_derive",
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binrw_derive"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb515fdd6f8d3a357c8e19b8ec59ef53880807864329b1cb1cba5c53bf76557e"
|
||||
dependencies = [
|
||||
"either",
|
||||
"owo-colors",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snf"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"binrw",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
@ -6,3 +6,4 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
binrw = "0.13.3"
|
||||
|
69
src/bot.rs
69
src/bot.rs
@ -1,12 +1,16 @@
|
||||
use binrw::{BinReaderExt, binrw};
|
||||
|
||||
pub enum Error{
|
||||
|
||||
InvalidHeader,
|
||||
InvalidSegment(binrw::Error),
|
||||
InvalidSegmentId(u64),
|
||||
File(crate::file::Error),
|
||||
}
|
||||
|
||||
/* block types
|
||||
|
||||
BLOCK_BOT_HEADER:
|
||||
u128 map_resource_uuid //which map is this bot running
|
||||
u128 time_resource_uuid //resource database time
|
||||
//don't include style info in bot header because it's in the simulation state
|
||||
//blocks are laid out in chronological order, but indices may jump around.
|
||||
u64 num_segments
|
||||
@ -28,6 +32,63 @@ loop{
|
||||
|
||||
*/
|
||||
|
||||
pub struct Bot{
|
||||
//
|
||||
//error hiding mock code
|
||||
mod simulation{
|
||||
#[super::binrw]
|
||||
#[brw(little)]
|
||||
pub struct State{}
|
||||
#[super::binrw]
|
||||
#[brw(little)]
|
||||
pub struct Instruction{}
|
||||
}
|
||||
mod instruction{
|
||||
#[super::binrw]
|
||||
#[brw(little)]
|
||||
pub struct TimedInstruction<Instruction:binrw::BinRead+binrw::BinWrite>{
|
||||
time:u64,
|
||||
instruction:Instruction
|
||||
}
|
||||
}
|
||||
mod timeline{
|
||||
#[super::binrw]
|
||||
#[brw(little)]
|
||||
pub struct Timeline<Instruction:binrw::BinRead+binrw::BinWrite>{
|
||||
#[bw(try_calc(u32::try_from(instructions.len())))]
|
||||
instruction_count:u32,
|
||||
#[br(count=instruction_count)]
|
||||
instructions:Vec<super::instruction::TimedInstruction<Instruction>>
|
||||
}
|
||||
}
|
||||
|
||||
//serious code
|
||||
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
struct SegmentId(u64);
|
||||
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
pub struct Segment{
|
||||
state:simulation::State,
|
||||
#[bw(try_calc(u32::try_from(instructions.len())))]
|
||||
instruction_count:u32,
|
||||
#[br(count=instruction_count)]
|
||||
instructions:Vec<instruction::TimedInstruction<simulation::Instruction>>
|
||||
}
|
||||
|
||||
pub struct StreamableBot<R:BinReaderExt>{
|
||||
file:crate::file::File<R>,
|
||||
timeline:timeline::Timeline<SegmentId>,
|
||||
segment_id_to_block_id:Vec<crate::file::BlockId>,
|
||||
}
|
||||
impl<R:BinReaderExt> StreamableBot<R>{
|
||||
pub(crate) fn new(file:crate::file::File<R>)->Result<Self,Error>{
|
||||
Err(Error::InvalidHeader)
|
||||
}
|
||||
pub fn load_segment(&mut self,segment_id:SegmentId)->Result<Segment,Error>{
|
||||
let block_id=*self.segment_id_to_block_id.get(segment_id.0 as usize).ok_or(Error::InvalidSegmentId(segment_id.0))?;
|
||||
let mut block=self.file.take_block(block_id).map_err(|e|Error::File(e))?;
|
||||
let segment=block.read_le().map_err(|e|Error::InvalidSegment(e))?;
|
||||
Ok(segment)
|
||||
}
|
||||
}
|
27
src/demo.rs
27
src/demo.rs
@ -1,13 +1,30 @@
|
||||
use binrw::BinReaderExt;
|
||||
|
||||
pub enum Error{
|
||||
|
||||
InvalidHeader,
|
||||
}
|
||||
|
||||
/*
|
||||
BLOCK_DEMO_HEADER:
|
||||
//timeline of loading maps, bots
|
||||
u128 map_resource_id
|
||||
u64 map_header_block_id
|
||||
u32 num_bots
|
||||
for bot_id in 0..num_bots{
|
||||
u128 bot_resource_id
|
||||
u64 bot_header_block_id
|
||||
}
|
||||
|
||||
//bot loading timeline
|
||||
how to do worldstate for deathrun!?
|
||||
|
||||
*/
|
||||
|
||||
pub struct Demo{
|
||||
map:Box<crate::map::Map>,
|
||||
bots:Vec<crate::bot::Bot>,
|
||||
pub struct StreamableDemo<R:BinReaderExt>{
|
||||
map:Box<crate::map::StreamableMap<R>>,
|
||||
bots:Vec<crate::bot::StreamableBot<R>>,
|
||||
}
|
||||
impl<R:BinReaderExt> StreamableDemo<R>{
|
||||
pub(crate) fn new(file:crate::file::File<R>)->Result<Self,Error>{
|
||||
Err(Error::InvalidHeader)
|
||||
}
|
||||
}
|
85
src/file.rs
85
src/file.rs
@ -1,24 +1,12 @@
|
||||
//file format "sniff"
|
||||
|
||||
use binrw::{binrw, BinReaderExt, io::TakeSeekExt};
|
||||
|
||||
pub enum Error{
|
||||
Header(HeaderError),
|
||||
Map(crate::map::Error),
|
||||
Bot(crate::bot::Error),
|
||||
Demo(crate::demo::Error),
|
||||
}
|
||||
|
||||
pub enum MissingData{
|
||||
NeedExactly(usize),//missing exactly this many bytes
|
||||
NeedAtLeast(usize),//missing at least this many bytes
|
||||
}
|
||||
|
||||
pub enum HeaderError{
|
||||
InvalidMagic,
|
||||
InvalidVersion,
|
||||
UnexpectedEOF{
|
||||
eof:usize,
|
||||
missing_data:MissingData,
|
||||
},//file ends in the middle of the header
|
||||
InvalidHeader(binrw::Error),
|
||||
UnexpectedEOF,
|
||||
InvalidBlockId(usize),
|
||||
Seek(std::io::Error),
|
||||
}
|
||||
|
||||
/* spec
|
||||
@ -48,32 +36,59 @@ for block_id in 0..num_blocks{
|
||||
//each block is compressed with zstd or gz or something
|
||||
|
||||
*/
|
||||
|
||||
pub enum Magic{
|
||||
Map, //"SNFM"
|
||||
Bot, //"SNFB"
|
||||
Demo, //"SNFD"
|
||||
PlaylistDemo,
|
||||
#[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,
|
||||
}
|
||||
|
||||
pub struct Header{
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
struct Header{
|
||||
/// Type of file
|
||||
magic:Magic,
|
||||
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,
|
||||
#[bw(try_calc(u64::try_from(block_location.len()-1)))]
|
||||
block_count:u64,
|
||||
#[br(count=block_count+1)]
|
||||
block_location:Vec<u64>,
|
||||
}
|
||||
|
||||
pub struct BlockLayout{
|
||||
count:u64,
|
||||
location:Vec<u64>,
|
||||
pub struct BlockId(usize);
|
||||
|
||||
pub(crate) struct File<R:BinReaderExt>{
|
||||
header:Header,
|
||||
//reference to the data
|
||||
data:R,
|
||||
}
|
||||
|
||||
pub enum SNF{
|
||||
Map(crate::map::Map),
|
||||
Bot(crate::bot::Bot),
|
||||
Demo(crate::demo::Demo),
|
||||
}
|
||||
impl<R:BinReaderExt> File<R>{
|
||||
pub(crate) fn new(mut input:R)->Result<File<R>,Error>{
|
||||
Ok(File{
|
||||
header:input.read_le().map_err(|e|Error::InvalidHeader(e))?,
|
||||
data:input,
|
||||
})
|
||||
}
|
||||
pub(crate) fn take_block(&mut self,block_id:BlockId)->Result<binrw::io::TakeSeek<&mut R>,Error>{
|
||||
if self.header.block_location.len()<=block_id.0{
|
||||
return Err(Error::InvalidBlockId(block_id.0))
|
||||
}
|
||||
let block_start=self.header.block_location[block_id.0];
|
||||
let block_end=self.header.block_location[block_id.0+1];
|
||||
self.data.seek(std::io::SeekFrom::Start(block_start)).map_err(|e|Error::Seek(e))?;
|
||||
Ok((&mut self.data).take_seek(block_end-block_start))
|
||||
}
|
||||
pub(crate) fn fourcc(&self)->FourCC{
|
||||
self.header.fourcc
|
||||
}
|
||||
}
|
||||
|
54
src/lib.rs
54
src/lib.rs
@ -1,12 +1,52 @@
|
||||
use std::io::Read;
|
||||
use binrw::BinReaderExt;
|
||||
|
||||
mod file;
|
||||
mod map;
|
||||
mod bot;
|
||||
mod demo;
|
||||
pub mod file;
|
||||
pub mod map;
|
||||
pub mod bot;
|
||||
pub mod demo;
|
||||
|
||||
pub fn read<R:Read>(input:R)->Result<crate::file::SNF,crate::file::Error>{
|
||||
Err(file::Error::Header(file::HeaderError::InvalidMagic))
|
||||
pub enum Error{
|
||||
UnexpectedFourCC,
|
||||
Header(file::Error),
|
||||
Map(map::Error),
|
||||
Bot(bot::Error),
|
||||
Demo(demo::Error),
|
||||
}
|
||||
|
||||
pub enum SNF<R:BinReaderExt>{
|
||||
Map(map::StreamableMap<R>),
|
||||
Bot(bot::StreamableBot<R>),
|
||||
Demo(demo::StreamableDemo<R>),
|
||||
}
|
||||
|
||||
pub fn read_snf<R:BinReaderExt>(input:R)->Result<SNF<R>,Error>{
|
||||
let file=file::File::new(input).map_err(|e|Error::Header(e))?;
|
||||
Ok(match file.fourcc(){
|
||||
file::FourCC::Map=>SNF::Map(map::StreamableMap::new(file).map_err(|e|Error::Map(e))?),
|
||||
file::FourCC::Bot=>SNF::Bot(bot::StreamableBot::new(file).map_err(|e|Error::Bot(e))?),
|
||||
file::FourCC::Demo=>SNF::Demo(demo::StreamableDemo::new(file).map_err(|e|Error::Demo(e))?),
|
||||
})
|
||||
}
|
||||
pub fn read_map<R:BinReaderExt>(input:R)->Result<map::StreamableMap<R>,Error>{
|
||||
let file=file::File::new(input).map_err(|e|Error::Header(e))?;
|
||||
match file.fourcc(){
|
||||
file::FourCC::Map=>Ok(map::StreamableMap::new(file).map_err(|e|Error::Map(e))?),
|
||||
_=>Err(Error::UnexpectedFourCC)
|
||||
}
|
||||
}
|
||||
pub fn read_bot<R:BinReaderExt>(input:R)->Result<bot::StreamableBot<R>,Error>{
|
||||
let file=file::File::new(input).map_err(|e|Error::Header(e))?;
|
||||
match file.fourcc(){
|
||||
file::FourCC::Bot=>Ok(bot::StreamableBot::new(file).map_err(|e|Error::Bot(e))?),
|
||||
_=>Err(Error::UnexpectedFourCC)
|
||||
}
|
||||
}
|
||||
pub fn read_demo<R:BinReaderExt>(input:R)->Result<demo::StreamableDemo<R>,Error>{
|
||||
let file=file::File::new(input).map_err(|e|Error::Header(e))?;
|
||||
match file.fourcc(){
|
||||
file::FourCC::Demo=>Ok(demo::StreamableDemo::new(file).map_err(|e|Error::Demo(e))?),
|
||||
_=>Err(Error::UnexpectedFourCC)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
90
src/map.rs
90
src/map.rs
@ -1,5 +1,10 @@
|
||||
use binrw::{BinReaderExt, binrw};
|
||||
|
||||
pub enum Error{
|
||||
|
||||
InvalidHeader,
|
||||
InvalidNodeId(u64),
|
||||
InvalidRegion(binrw::Error),
|
||||
File(crate::file::Error),
|
||||
}
|
||||
|
||||
/* block types
|
||||
@ -12,16 +17,28 @@ u64 num_nodes
|
||||
for node_id in 1..num_nodes{
|
||||
u64 parent_node
|
||||
}
|
||||
|
||||
//NOTE: alternate realities are not necessary.
|
||||
//portals/wormholes simply use an in-model and and out-model.
|
||||
//skyboxes are inverted models with a special shader.
|
||||
|
||||
//ideally spacial blocks are sorted from distance to start zone
|
||||
//texture blocks are inserted before the first spacial block they are used in
|
||||
u64 num_spacial_blocks
|
||||
for spacial_block_id in 0..num_spacial_blocks{
|
||||
u64 node_id
|
||||
u64 block_id //data block
|
||||
Aabb block_extents
|
||||
}
|
||||
//ideally spacial blocks are sorted from distance to start zone
|
||||
//texture blocks are inserted before the first spacial block they are used in
|
||||
//if the map file references external resources, num_resources = 0
|
||||
u64 num_resources
|
||||
for resource_id in 0..num_resources{
|
||||
u64 block_id
|
||||
u128 resource_id
|
||||
}
|
||||
|
||||
BLOCK_MAP_RESOURCE:
|
||||
Resource resource_type
|
||||
//an individual one of the following:
|
||||
- model (IndexedModel)
|
||||
- shader (compiled SPIR-V)
|
||||
@ -30,15 +47,68 @@ BLOCK_MAP_RESOURCE:
|
||||
- video (AV1)
|
||||
- animation (Trey thing)
|
||||
|
||||
BLOCK_MAP_OBJECT:
|
||||
//an individual one of the following:
|
||||
- model instance
|
||||
- located resource
|
||||
//for a list of resources, parse the object.
|
||||
//alternatively, BLOCK_MAP_REGION lists a group of objects to be decoded all at once
|
||||
BLOCK_MAP_REGION:
|
||||
u64 num_models
|
||||
for model_id in 0..num_models{
|
||||
u128 model_resource_uuid
|
||||
ModelInstance mode_instance
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
pub struct Map{
|
||||
|
||||
//error hiding mock code
|
||||
mod physics{
|
||||
pub struct StyleModifiers{}
|
||||
}
|
||||
mod model{
|
||||
pub struct IndexedModel{}
|
||||
#[super::binrw]
|
||||
#[brw(little)]
|
||||
pub struct ModelInstance{}
|
||||
}
|
||||
mod image{
|
||||
pub struct Image{}
|
||||
}
|
||||
|
||||
//serious code
|
||||
|
||||
struct ModelUuid(u128);
|
||||
struct ImageUuid(u128);
|
||||
pub struct BvhNodeId(u64);
|
||||
struct BvhNode{
|
||||
//
|
||||
}
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
struct Region{
|
||||
#[bw(try_calc(u32::try_from(models.len())))]
|
||||
model_count:u32,
|
||||
#[br(count=model_count)]
|
||||
models:Vec<model::ModelInstance>,
|
||||
}
|
||||
|
||||
pub struct StreamableMap<R:BinReaderExt>{
|
||||
file:crate::file::File<R>,
|
||||
style:physics::StyleModifiers,//probably should move this out of physics
|
||||
bvh:BvhNode,
|
||||
node_id_to_block_id:Vec<crate::file::BlockId>,
|
||||
//do not need this? return only new data with load_node
|
||||
resource_model:std::collections::HashMap<ModelUuid,model::IndexedModel>,
|
||||
resource_image:std::collections::HashMap<ImageUuid,image::Image>,
|
||||
}
|
||||
impl<R:BinReaderExt> StreamableMap<R>{
|
||||
pub(crate) fn new(file:crate::file::File<R>)->Result<Self,Error>{
|
||||
Err(Error::InvalidHeader)
|
||||
}
|
||||
pub fn load_node(&mut self,node_id:BvhNodeId)->Result<Vec<model::ModelInstance>,Error>{
|
||||
//load region from disk
|
||||
//parse the models and determine what resources need to be loaded
|
||||
//load resources into self.resources
|
||||
//return Region
|
||||
let block_id=*self.node_id_to_block_id.get(node_id.0 as usize).ok_or(Error::InvalidNodeId(node_id.0))?;
|
||||
let mut block=self.file.take_block(block_id).map_err(|e|Error::File(e))?;
|
||||
let region:Region=block.read_le().map_err(|e|Error::InvalidRegion(e))?;
|
||||
Ok(region.models)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user