diff --git a/src/v1.rs b/src/v1.rs index 8b13789..14c75cf 100644 --- a/src/v1.rs +++ b/src/v1.rs @@ -1 +1,333 @@ +use binrw::{binrw,BinReaderExt,io::TakeSeekExt}; +#[binrw] +#[brw(little)] +pub enum Bool{ + #[brw(magic=0u32)] + False, + #[brw(magic=1u32)] + True, +} +#[binrw] +#[brw(little)] +pub struct Vector2{ + pub x:f32, + pub y:f32, +} +#[binrw] +#[brw(little)] +pub struct Vector3{ + pub x:f32, + pub y:f32, + pub z:f32, +} + +// input +#[binrw] +#[brw(little)] +pub struct InputEvent{ + pub game_controls:u32, + pub mouse_pos:Vector2, +} +#[binrw] +#[brw(little)] +pub struct TimedInputEvent{ + pub time:f64, + pub event:InputEvent, +} + +// output +#[binrw] +#[brw(little)] +pub struct OutputEvent{ + pub tick_info:u32, + pub angles:Vector3, + pub position:Vector3, + pub velocity:Vector3, + pub acceleration:Vector3, +} +#[binrw] +#[brw(little)] +pub struct TimedOutputEvent{ + pub time:f64, + pub event:OutputEvent, +} + +// sound +#[binrw] +#[brw(little)] +pub struct SoundEvent{ + pub sound_type:u32, + pub material:u32, +} +#[binrw] +#[brw(little)] +pub struct TimedSoundEvent{ + pub time:f64, + pub event:SoundEvent, +} + +// world +#[binrw] +#[brw(little)] +pub struct WorldEventReset{ + pub position:Vector3, +} +#[binrw] +#[brw(little)] +pub struct WorldEventButton{ + pub button_id:u32, + #[brw(magic=b"quatdata")] + __:(), +} +#[binrw] +#[brw(little)] +pub struct WorldEventSetTime{ + pub time:f64, + #[brw(magic=b"data")] + __:(), +} +#[binrw] +#[brw(little)] +pub struct WorldEventSetPaused{ + pub paused:Bool, + #[brw(magic=b"quatdata")] + __:(), +} +#[binrw] +#[brw(little)] +pub enum WorldEvent{ + #[brw(magic=0u32)] + Reset(WorldEventReset), + #[brw(magic=1u32)] + Button(WorldEventButton), + #[brw(magic=2u32)] + SetTime(WorldEventSetTime), + #[brw(magic=3u32)] + SetPaused(WorldEventSetPaused), +} +#[binrw] +#[brw(little)] +pub struct TimedWorldEvent{ + pub time:f64, + pub event:WorldEvent, +} + +// gravity +#[binrw] +#[brw(little)] +pub struct GravityEvent{ + pub gravity:Vector3, +} +#[binrw] +#[brw(little)] +pub struct TimedGravityEvent{ + pub time:f64, + pub event:GravityEvent, +} + +// run +#[binrw] +#[brw(little)] +pub enum RunEventType{ + #[brw(magic=0u32)] + Prepare, + #[brw(magic=1u32)] + Start, + #[brw(magic=2u32)] + Finish, + #[brw(magic=3u32)] + Clear, + #[brw(magic=4u32)] + Flag, + #[brw(magic=5u32)] + LoadState, + #[brw(magic=6u32)] + SaveState, +} +#[binrw] +#[brw(little)] +pub struct RunEvent{ + run_event_type:RunEventType, + mode_id:i32, + flag_reason_id:u32, +} +#[binrw] +#[brw(little)] +pub struct TimedRunEvent{ + pub time:f64, + pub event:RunEvent, +} + +// camera +#[binrw] +#[brw(little)] +pub struct CameraEvent{ + pub camera_event_id:u32, + pub value:Vector3, +} +#[binrw] +#[brw(little)] +pub struct TimedCameraEvent{ + pub time:f64, + pub event:CameraEvent, +} + +// setting +#[binrw] +#[brw(little)] +pub struct SettingEvent{ + pub setting_id:u32, + pub value:f64, +} +#[binrw] +#[brw(little)] +pub struct TimedSettingEvent{ + pub time:f64, + pub event:SettingEvent, +} + +#[derive(Default)] +pub struct Block{ + pub input_events:Vec, + pub output_events:Vec, + pub sound_events:Vec, + pub world_events:Vec, + pub gravity_events:Vec, + pub run_events:Vec, + pub camera_events:Vec, + pub setting_events:Vec, +} + +#[binrw] +#[brw(little)] +enum EventType{ + #[brw(magic=1u32)] + Input, + #[brw(magic=2u32)] + Output, + #[brw(magic=3u32)] + Sound, + #[brw(magic=4u32)] + World, + #[brw(magic=5u32)] + Gravity, + #[brw(magic=6u32)] + Run, + #[brw(magic=7u32)] + Camera, + #[brw(magic=8u32)] + Setting, +} +#[binrw] +#[brw(little)] +struct EventChunkHeader{ + event_type:EventType, + num_events:u32, +} + +// first time I've managed to write BinRead generics without this stupid T::Args<'a>:Required thing blocking me +fn read_data_into_events<'a,R:BinReaderExt,T>(data:&mut R,events:&mut Vec,num_events:usize)->binrw::BinResult<()> +where + T:binrw::BinRead, + T::Args<'a>:binrw::__private::Required, +{ + // there is only supposed to be at most one of each type of event chunk per block, so no need to amortize. + events.reserve_exact(num_events); + for _ in 0..num_events{ + events.push(data.read_le()?); + } + Ok(()) +} + +impl Block{ + fn read(mut data:R)->binrw::BinResult{ + let mut block=Block::default(); + // well... this looks error prone + while let Ok(event_chunk_header)=data.read_le::(){ + match event_chunk_header.event_type{ + EventType::Input=>read_data_into_events(&mut data,&mut block.input_events,event_chunk_header.num_events as usize)?, + EventType::Output=>read_data_into_events(&mut data,&mut block.output_events,event_chunk_header.num_events as usize)?, + EventType::Sound=>read_data_into_events(&mut data,&mut block.sound_events,event_chunk_header.num_events as usize)?, + EventType::World=>read_data_into_events(&mut data,&mut block.world_events,event_chunk_header.num_events as usize)?, + EventType::Gravity=>read_data_into_events(&mut data,&mut block.gravity_events,event_chunk_header.num_events as usize)?, + EventType::Run=>read_data_into_events(&mut data,&mut block.run_events,event_chunk_header.num_events as usize)?, + EventType::Camera=>read_data_into_events(&mut data,&mut block.camera_events,event_chunk_header.num_events as usize)?, + EventType::Setting=>read_data_into_events(&mut data,&mut block.setting_events,event_chunk_header.num_events as usize)?, + } + } + Ok(block) + } +} + +#[derive(Debug)] +pub enum Error{ + InvalidBlockId(BlockId), + Seek(std::io::Error), + InvalidData(binrw::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{} + +#[binrw] +#[brw(little)] +#[derive(Debug)] +pub struct BlockId(u32); + +#[binrw] +#[brw(little)] +pub struct TimedBlockId{ + pub time:f64, + pub block_id:BlockId, +} + +#[binrw] +#[brw(little)] +pub struct FileHeader{ + #[brw(magic=b"qbot")] + pub file_version:u32, + pub num_offline_events:u32, + pub num_realtime_events:u32, + #[br(count=num_offline_events+num_realtime_events+1)] + pub block_positions:Vec, + #[br(count=num_offline_events)] + pub offline_blocks_timeline:Vec, + #[br(count=num_realtime_events)] + pub realtime_blocks_timeline:Vec, +} + +pub struct File{ + header:FileHeader, + //reference to the data + data:R, +} + +impl File{ + pub fn new(mut input:R)->Result,binrw::Error>{ + Ok(File{ + header:input.read_le()?, + data:input, + }) + } + fn data_mut(&mut self)->&mut R{ + &mut self.data + } + fn block_reader(&mut self,block_id:BlockId)->Result,Error>{ + if self.header.block_positions.len() as u32<=block_id.0{ + return Err(Error::InvalidBlockId(block_id)) + } + let block_start=self.header.block_positions[block_id.0 as usize] as u64; + let block_end=self.header.block_positions[block_id.0 as usize+1] as u64; + self.data.seek(std::io::SeekFrom::Start(block_start)).map_err(Error::Seek)?; + Ok(self.data_mut().take_seek(block_end-block_start)) + } + pub fn read_block(&mut self,block_id:BlockId)->Result{ + let data=self.block_reader(block_id)?; + let block=Block::read(data).map_err(Error::InvalidData)?; + Ok(block) + } +}