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<TimedInputEvent>,
+	pub output_events:Vec<TimedOutputEvent>,
+	pub sound_events:Vec<TimedSoundEvent>,
+	pub world_events:Vec<TimedWorldEvent>,
+	pub gravity_events:Vec<TimedGravityEvent>,
+	pub run_events:Vec<TimedRunEvent>,
+	pub camera_events:Vec<TimedCameraEvent>,
+	pub setting_events:Vec<TimedSettingEvent>,
+}
+
+#[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<T>,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<R:BinReaderExt>(mut data:R)->binrw::BinResult<Block>{
+		let mut block=Block::default();
+		// well... this looks error prone
+		while let Ok(event_chunk_header)=data.read_le::<EventChunkHeader>(){
+			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<u32>,
+	#[br(count=num_offline_events)]
+	pub offline_blocks_timeline:Vec<TimedBlockId>,
+	#[br(count=num_realtime_events)]
+	pub realtime_blocks_timeline:Vec<TimedBlockId>,
+}
+
+pub struct File<R:BinReaderExt>{
+	header:FileHeader,
+	//reference to the data
+	data:R,
+}
+
+impl<R:BinReaderExt> File<R>{
+	pub fn new(mut input:R)->Result<File<R>,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<binrw::io::TakeSeek<&mut R>,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<Block,Error>{
+		let data=self.block_reader(block_id)?;
+		let block=Block::read(data).map_err(Error::InvalidData)?;
+		Ok(block)
+	}
+}