8 Commits

Author SHA1 Message Date
6e59efdef5 wip: dynamic event size 2026-02-11 10:01:23 -08:00
8328b510c6 16x bigger blocks 2026-02-11 09:58:54 -08:00
e92d718c81 zero-indexed Ids 2026-02-11 09:58:46 -08:00
c1979c23e5 fixed block header and event order 2026-02-11 09:55:28 -08:00
17df73b640 style setting 2026-02-11 09:41:40 -08:00
a0e2aa0c55 u32 Time 2026-02-11 09:34:29 -08:00
c38f326483 remove trey float 2026-02-11 09:34:29 -08:00
34b9eb7f33 copy v0 2026-02-11 09:32:47 -08:00
6 changed files with 269 additions and 520 deletions

114
Cargo.lock generated
View File

@@ -44,48 +44,12 @@ version = "1.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
[[package]]
name = "cc"
version = "1.2.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]]
name = "itertools"
version = "0.14.0"
@@ -95,34 +59,12 @@ dependencies = [
"either",
]
[[package]]
name = "jobserver"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom",
"libc",
]
[[package]]
name = "libc"
version = "0.2.182"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
[[package]]
name = "owo-colors"
version = "4.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "proc-macro2"
version = "1.0.103"
@@ -141,18 +83,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "strafesnet_roblox_bot_file"
version = "0.8.1"
@@ -160,7 +90,6 @@ dependencies = [
"binrw",
"bitflags",
"itertools",
"zstd",
]
[[package]]
@@ -179,46 +108,3 @@ name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "wasip2"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
name = "zstd"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.16+zstd.1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
dependencies = [
"cc",
"pkg-config",
]

View File

@@ -7,7 +7,6 @@ edition = "2024"
binrw = "0.15.0"
bitflags = "2.6.0"
itertools = { version = "0.14.0", optional = true }
zstd = "0.13.3"
[features]
default = ["itertools"]

View File

@@ -1,159 +0,0 @@
use crate::v1 as latest;
// investigate reuse using #[br(map=trey_vec3)]
// Vector2
// Vector3
//
// Settings are looking rather destructive, pls ensure all files round-trip
pub fn from_v0(block_v0:&crate::v0::Block)->latest::Block{
let input_events=block_v0.input_events.iter().map(|event|{
latest::Timed{
time:latest::Time::new(event.time).unwrap(),
event:latest::InputEvent{
game_controls:event.event.game_controls,
mouse_pos:latest::Vector2{x:event.event.mouse_pos.x,y:event.event.mouse_pos.y},
},
}
}).collect();
let output_events=block_v0.output_events.iter().map(|event|{
latest::Timed{
time:latest::Time::new(event.time).unwrap(),
event:latest::OutputEvent{
tick_info:event.event.tick_info,
angles:latest::Vector3{x:event.event.angles.x,y:event.event.angles.y,z:event.event.angles.z},
position:latest::Vector3{x:event.event.position.x,y:event.event.position.y,z:event.event.position.z},
velocity:latest::Vector3{x:event.event.velocity.x,y:event.event.velocity.y,z:event.event.velocity.z},
acceleration:latest::Vector3{x:event.event.acceleration.x,y:event.event.acceleration.y,z:event.event.acceleration.z},
},
}
}).collect();
let sound_events=block_v0.sound_events.iter().map(|event|{
latest::Timed{
time:latest::Time::new(event.time).unwrap(),
event:event.event.clone(),
}
}).collect();
let world_events=block_v0.world_events.iter().map(|event|{
latest::Timed{
time:latest::Time::new(event.time).unwrap(),
event:match &event.event{
crate::v0::WorldEvent::Reset(world_event_reset)=>latest::WorldEvent::Reset(latest::WorldEventReset{
position:latest::Vector3{x:world_event_reset.position.x,y:world_event_reset.position.y,z:world_event_reset.position.z},
}),
crate::v0::WorldEvent::Button(world_event_button)=>latest::WorldEvent::Button(latest::WorldEventButton{
button_id:world_event_button.button_id,
}),
crate::v0::WorldEvent::SetTime(world_event_set_time)=>latest::WorldEvent::SetTime(latest::WorldEventSetTime{
time:latest::Time::new(world_event_set_time.time).unwrap(),
}),
crate::v0::WorldEvent::SetPaused(world_event_set_paused)=>latest::WorldEvent::SetPaused(latest::WorldEventSetPaused{
paused:world_event_set_paused.paused,
}),
},
}
}).collect();
let gravity_events=block_v0.gravity_events.iter().map(|event|{
latest::Timed{
time:latest::Time::new(event.time).unwrap(),
event:latest::GravityEvent{
gravity:latest::Vector3{x:event.event.gravity.x,y:event.event.gravity.y,z:event.event.gravity.z},
},
}
}).collect();
// need to generate a setting event for the style in RunPrepare
let run_events=block_v0.run_events.iter().map(|event|{
latest::Timed{
time:latest::Time::new(event.time).unwrap(),
event:match &event.event{
crate::v0::RunEvent::Prepare(run_event_prepare)=>latest::RunEvent::Prepare(latest::RunEventPrepare{
mode:run_event_prepare.mode,
}),
crate::v0::RunEvent::Start(run_event_zone)=>latest::RunEvent::Start(latest::RunEventZone{
mode:run_event_zone.mode,
}),
crate::v0::RunEvent::Finish(run_event_zone)=>latest::RunEvent::Finish(latest::RunEventZone{
mode:run_event_zone.mode,
}),
crate::v0::RunEvent::Clear(run_event_clear)=>latest::RunEvent::Clear(latest::RunEventClear{
mode:run_event_clear.mode,
}),
crate::v0::RunEvent::Flag(run_event_flag)=>latest::RunEvent::Flag(latest::RunEventFlag{
mode:run_event_flag.mode,
flag_reason:run_event_flag.flag_reason,
}),
crate::v0::RunEvent::LoadState(run_event_practice)=>latest::RunEvent::LoadState(latest::RunEventPractice{
mode:run_event_practice.mode,
state_id:run_event_practice.state_id,
}),
crate::v0::RunEvent::SaveState(run_event_practice)=>latest::RunEvent::SaveState(latest::RunEventPractice{
mode:run_event_practice.mode,
state_id:run_event_practice.state_id,
}),
},
}
}).collect();
let camera_events=block_v0.camera_events.iter().map(|event|{
latest::Timed{
time:latest::Time::new(event.time).unwrap(),
event:match &event.event{
crate::v0::CameraEvent::CameraPunch(camera_event_camera_punch)=>latest::CameraEvent::CameraPunch(latest::CameraEventCameraPunch{
rot_velocity:latest::Vector3{x:camera_event_camera_punch.rot_velocity.x,y:camera_event_camera_punch.rot_velocity.y,z:camera_event_camera_punch.rot_velocity.z},
}),
crate::v0::CameraEvent::Transform(camera_event_transform)=>latest::CameraEvent::Transform(latest::CameraEventTransform{
axis_angle:latest::Vector3{x:camera_event_transform.axis_angle.x,y:camera_event_transform.axis_angle.y,z:camera_event_transform.axis_angle.z},
}),
},
}
}).collect();
let setting_events=block_v0.setting_events.iter().map(|event|{
latest::Timed{
time:latest::Time::new(event.time).unwrap(),
event:match &event.event{
crate::v0::SettingEvent::FieldOfView(setting_event_field_of_view)=>{
// generate three events
// - FOV
// - SensX
// - SensY
todo!();
},
crate::v0::SettingEvent::Sensitivity(setting_event_sensitivity)=>{
// calculate absolute sensitivity from current fov
// generate two events
// - SensX
// - SensY
todo!();
},
crate::v0::SettingEvent::VerticalSensitivityMultiplier(setting_event_vertical_sensitivity_multiplier)=>{
// calculate absolute horizontal sensitivity
// generate one event
// - SensY
let sensitivity=todo!();
latest::SettingEvent::SensitivityY(latest::SettingEventSensitivity{
sensitivity,
})
},
crate::v0::SettingEvent::AbsoluteSensitivity(setting_event_absolute_sensitivity)=>{
// calculate SensY from current VerticalSensitivityMultiplier
// generate two events
// - SensX
// - SensY
todo!();
},
crate::v0::SettingEvent::TurnSpeed(setting_event_turn_speed)=>latest::SettingEvent::TurnSpeed(latest::SettingEventTurnSpeed{
turn_speed:setting_event_turn_speed.turn_speed,
}),
},
}
}).collect();
latest::Block{
input_events,
output_events,
sound_events,
world_events,
gravity_events,
run_events,
camera_events,
setting_events,
}
}

View File

@@ -2,7 +2,6 @@ pub use binrw::Error as BinrwError;
pub mod v0;
pub mod v1;
pub mod convert;
#[cfg(test)]
mod tests;

111
src/v0.rs
View File

@@ -5,26 +5,26 @@ use binrw::BinReaderExt;
use crate::BinrwError;
// the bit chunks are deposited in reverse
const fn read_trey_float(bits:u32)->f32{
fn read_trey_float(bits:u32)->f32{
let s=bits&1;
let e=(bits>>1)&((1<<8)-1);
let m=(bits>>(1+8))&((1<<23)-1);
f32::from_bits(m|(e<<23)|(s<<31))
}
const fn write_trey_float(value:&f32)->u32{
fn write_trey_float(value:&f32)->u32{
let bits=value.to_bits();
let s=(bits>>31)&1;
let e=(bits>>23)&((1<<8)-1);
let m=bits&((1<<23)-1);
m<<(1+8)|(e<<1)|s
}
const fn read_trey_double(bits:u64)->f64{
fn read_trey_double(bits:u64)->f64{
let s=bits&1;
let e=(bits>>1)&((1<<11)-1);
let m=(bits>>(1+11))&((1<<52)-1);
f64::from_bits(m|(e<<52)|(s<<63))
}
const fn write_trey_double(value:&f64)->u64{
fn write_trey_double(value:&f64)->u64{
let bits=value.to_bits();
let s=(bits>>63)&1;
let e=(bits>>52)&((1<<11)-1);
@@ -89,7 +89,7 @@ impl std::fmt::Display for GameControlsError{
}
impl std::error::Error for GameControlsError{}
impl GameControls{
pub fn try_from_bits(bits:u32)->Result<Self,GameControlsError>{
fn try_from_bits(bits:u32)->Result<Self,GameControlsError>{
Self::from_bits(bits).ok_or(GameControlsError)
}
}
@@ -162,7 +162,7 @@ impl std::fmt::Display for TickInfoError{
}
impl std::error::Error for TickInfoError{}
impl TickInfo{
pub fn try_from_bits(bits:u32)->Result<Self,TickInfoError>{
fn try_from_bits(bits:u32)->Result<Self,TickInfoError>{
Self::from_bits(bits).ok_or(TickInfoError)
}
}
@@ -350,7 +350,6 @@ pub enum FlagReason{
#[brw(magic=9u32)]
Practice,
}
/// Creates a new run when the player enters a start zone.
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
@@ -413,91 +412,47 @@ pub enum RunEvent{
}
// camera
/// Punches the camera when the player has an intense collision.
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct CameraEventCameraPunch{
pub rot_velocity:Vector3,
}
/// Rotates the camera when the player goes through a wormhole.
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct CameraEventTransform{
pub axis_angle:Vector3,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub enum CameraEvent{
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub enum CameraEventType{
#[brw(magic=0u32)]
CameraPunch(CameraEventCameraPunch),
CameraPunch,
#[brw(magic=1u32)]
Transform(CameraEventTransform),
Transform,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct CameraEvent{
pub camera_event_type:CameraEventType,
pub value:Vector3,
}
// setting
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct SettingEventFieldOfView{
#[br(map=read_trey_double)]
#[bw(map=write_trey_double)]
pub fov:f64,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct SettingEventSensitivity{
#[br(map=read_trey_double)]
#[bw(map=write_trey_double)]
pub sensitivity:f64,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct SettingEventVerticalSensitivityMultiplier{
#[br(map=read_trey_double)]
#[bw(map=write_trey_double)]
pub multiplier:f64,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct SettingEventAbsoluteSensitivity{
#[br(map=|v:u64|read_trey_double(v)==1.0)]
#[bw(map=|&enabled:&bool|
if enabled{
write_trey_double(&1.0)
}else{
write_trey_double(&0.0)
}
)]
pub enabled:bool,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct SettingEventTurnSpeed{
#[br(map=read_trey_double)]
#[bw(map=write_trey_double)]
pub turn_speed:f64,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub enum SettingEvent{
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub enum SettingType{
#[brw(magic=0u32)]
FieldOfView(SettingEventFieldOfView),
FieldOfView,
#[brw(magic=1u32)]
Sensitivity(SettingEventSensitivity),
Sensitivity,
#[brw(magic=2u32)]
VerticalSensitivityMultiplier(SettingEventVerticalSensitivityMultiplier),
VerticalSensitivityMultiplier,
#[brw(magic=3u32)]
AbsoluteSensitivity(SettingEventAbsoluteSensitivity),
AbsoluteSensitivity,
#[brw(magic=4u32)]
TurnSpeed(SettingEventTurnSpeed),
TurnSpeed,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct SettingEvent{
pub setting_type:SettingType,
#[br(map=read_trey_double)]
#[bw(map=write_trey_double)]
pub value:f64,
}
/// A segment of event timelines.

403
src/v1.rs
View File

@@ -4,52 +4,18 @@ use binrw::io::{TakeSeek,TakeSeekExt};
use binrw::BinReaderExt;
use crate::BinrwError;
pub use crate::v0::{FlagReason,GameControls,GameControlsError,ModeID,ModeSpec,SoundType,SoundEvent,Style,TickInfo,TickInfoError};
const EVENT_SIZE:[usize;8]=[
4+4+2*4, // Input
4+4+4*3*4, // Output
4+4+4, // Sound
4+4+12, // World
4+3*4, // Gravity
4+4+4+4, // Run
4+4+3*4, // Camera
4+4+8, // Setting
];
#[derive(Debug)]
pub enum TimeFromFloatError{
Nan,
Overflow,
Underflow,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq,Ord,PartialOrd)]
#[derive(Debug,Clone,Copy)]
pub struct Time(u32);
impl Time{
const TIME_UNITS_PER_SECOND:f64=65536.0;
const TIME_SECONDS_PER_UNIT:f64=1.0/Self::TIME_UNITS_PER_SECOND;
pub const fn new(value:f64)->Result<Self,TimeFromFloatError>{
use core::num::FpCategory::*;
match value.classify(){
Nan=>Err(TimeFromFloatError::Nan),
Zero=>Ok(Time(0)),
Subnormal
|Normal
|Infinite=>{
if value<u32::MIN as f64*Self::TIME_SECONDS_PER_UNIT{
return Err(TimeFromFloatError::Underflow);
}
if u32::MAX as f64*Self::TIME_SECONDS_PER_UNIT<value{
return Err(TimeFromFloatError::Overflow);
}
Ok(Time((value*Self::TIME_UNITS_PER_SECOND) as u32))
},
}
impl PartialEq for Time{
fn eq(&self,other:&Self)->bool{
self.0.eq(&other.0)
}
pub const fn get(self)->f64{
self.0 as f64*Self::TIME_SECONDS_PER_UNIT
}
impl PartialOrd for Time{
fn partial_cmp(&self,other:&Self)->Option<core::cmp::Ordering>{
self.0.partial_cmp(&other.0)
}
}
@@ -69,6 +35,42 @@ pub struct Vector3{
pub z:f32,
}
bitflags::bitflags!{
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub struct GameControls:u32{
const MoveForward=1<<0;
const MoveLeft=1<<1;
const MoveBack=1<<2;
const MoveRight=1<<3;
const MoveUp=1<<4;
const MoveDown=1<<5;
const LookUp=1<<6;
const LookLeft=1<<7;
const LookDown=1<<8;
const LookRight=1<<9;
const Jump=1<<10;
const Crouch=1<<11;
const Sprint=1<<12;
const Zoom=1<<13;
const Use=1<<14;
const Action1=1<<15;
const Action2=1<<16;
}
}
#[derive(Debug)]
pub struct GameControlsError;
impl std::fmt::Display for GameControlsError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for GameControlsError{}
impl GameControls{
fn try_from_bits(bits:u32)->Result<Self,GameControlsError>{
Self::from_bits(bits).ok_or(GameControlsError)
}
}
// generic timed event
#[binrw]
#[brw(little)]
@@ -117,6 +119,28 @@ pub struct InputEvent{
}
// output
bitflags::bitflags!{
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub struct TickInfo:u32{
const TickEnd=1<<0;
const Jump=1<<1;
const Strafe=1<<2;
const Touching=1<<3;
}
}
#[derive(Debug)]
pub struct TickInfoError;
impl std::fmt::Display for TickInfoError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for TickInfoError{}
impl TickInfo{
fn try_from_bits(bits:u32)->Result<Self,TickInfoError>{
Self::from_bits(bits).ok_or(TickInfoError)
}
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
@@ -130,6 +154,37 @@ pub struct OutputEvent{
pub acceleration:Vector3,
}
// sound
#[binrw]
#[brw(little)]
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub enum SoundType{
#[brw(magic=101u32)]
TrackGround,
#[brw(magic=102u32)]
TrackLadder,
#[brw(magic=103u32)]
TrackWater,
#[brw(magic=104u32)]
TrackAir,
#[brw(magic=201u32)]
JumpGround,
#[brw(magic=202u32)]
JumpLadder,
#[brw(magic=301u32)]
SmashGround,
#[brw(magic=302u32)]
SmashWall,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct SoundEvent{
pub sound_type:SoundType,
/// Roblox enum
pub material:u32,
}
// world
#[binrw]
#[brw(little)]
@@ -142,22 +197,12 @@ pub struct WorldEventReset{
#[derive(Debug,Clone)]
pub struct WorldEventButton{
pub button_id:u32,
// This field does not exist in the final struct and
// exists purely to de/serialize the magic number.
#[br(temp)]
#[bw(ignore)]
#[brw(magic=b"quatdata")]
_magic:(),
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct WorldEventSetTime{
pub time:Time,
#[br(temp)]
#[bw(ignore)]
#[brw(magic=b"quatdata")]
_magic:(),
}
#[binrw]
#[brw(little)]
@@ -166,10 +211,6 @@ pub struct WorldEventSetPaused{
#[br(map=|paused:u32|paused!=0)]
#[bw(map=|&paused:&bool|paused as u32)]
pub paused:bool,
#[br(temp)]
#[bw(ignore)]
#[brw(magic=b"quatdata")]
_magic:(),
}
#[binrw]
#[brw(little)]
@@ -194,7 +235,80 @@ pub struct GravityEvent{
}
// run
/// Creates a new run when the player enters a start zone.
#[binrw]
#[brw(little)]
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub struct ModeID(pub u32);
#[binrw]
#[brw(little)]
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub enum ModeSpec{
Exactly(ModeID),
#[brw(magic=-1i32)]
All,
#[brw(magic=-2i32)]
Invalid,
#[brw(magic=-3i32)]
InProgress,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub enum Style{
#[brw(magic=1u32)]
Autohop,
#[brw(magic=2u32)]
Scroll,
#[brw(magic=3u32)]
Sideways,
#[brw(magic=4u32)]
HalfSideways,
#[brw(magic=5u32)]
WOnly,
#[brw(magic=6u32)]
AOnly,
#[brw(magic=7u32)]
Backwards,
#[brw(magic=8u32)]
Faste,
#[brw(magic=14u32)]
LowGravity,
#[brw(magic=501u32)]
Fly,
#[brw(magic=502u32)]
FlySustain,
#[brw(magic=503u32)]
Rocket,
#[brw(magic=504u32)]
Style3DStrafe,
#[brw(magic=505u32)]
RocketStrafe,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub enum FlagReason{
#[brw(magic=0u32)]
Anticheat,
#[brw(magic=1u32)]
StyleChange,
#[brw(magic=2u32)]
Clock,
#[brw(magic=3u32)]
Pause,
#[brw(magic=4u32)]
Flying,
#[brw(magic=5u32)]
Gravity,
#[brw(magic=6u32)]
Timescale,
#[brw(magic=7u32)]
Timetravel,
#[brw(magic=8u32)]
Teleport,
#[brw(magic=9u32)]
Practice,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
@@ -206,20 +320,12 @@ pub struct RunEventPrepare{
#[derive(Debug,Clone)]
pub struct RunEventZone{
pub mode:ModeID,
#[br(temp)]
#[bw(ignore)]
#[brw(magic=b"data")]
_magic:(),
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct RunEventClear{
pub mode:ModeSpec,
#[br(temp)]
#[bw(ignore)]
#[brw(magic=b"data")]
_magic:(),
}
#[binrw]
#[brw(little)]
@@ -256,28 +362,21 @@ pub enum RunEvent{
}
// camera
/// Punches the camera when the player has an intense collision.
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct CameraEventCameraPunch{
pub rot_velocity:Vector3,
}
/// Rotates the camera when the player goes through a wormhole.
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct CameraEventTransform{
pub axis_angle:Vector3,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub enum CameraEvent{
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub enum CameraEventType{
#[brw(magic=0u32)]
CameraPunch(CameraEventCameraPunch),
CameraPunch,
#[brw(magic=1u32)]
Transform(CameraEventTransform),
Transform,
}
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
pub struct CameraEvent{
pub camera_event_type:CameraEventType,
pub value:Vector3,
}
// setting
@@ -291,7 +390,8 @@ pub struct SettingEventFieldOfView{
#[brw(little)]
#[derive(Debug,Clone)]
pub struct SettingEventSensitivity{
pub sensitivity:f64,
pub sensitivity_x:f64,
pub sensitivity_y:f64,
}
#[binrw]
#[brw(little)]
@@ -304,10 +404,6 @@ pub struct SettingEventTurnSpeed{
#[derive(Debug,Clone)]
pub struct SettingEventStyle{
pub style:Style,
#[br(temp)]
#[bw(ignore)]
#[brw(magic=b"data")]
_magic:(),
}
#[binrw]
#[brw(little)]
@@ -316,12 +412,10 @@ pub enum SettingEvent{
#[brw(magic=0u32)]
FieldOfView(SettingEventFieldOfView),
#[brw(magic=1u32)]
SensitivityX(SettingEventSensitivity),
Sensitivity(SettingEventSensitivity),
#[brw(magic=2u32)]
SensitivityY(SettingEventSensitivity),
#[brw(magic=3u32)]
TurnSpeed(SettingEventTurnSpeed),
#[brw(magic=4u32)]
#[brw(magic=3u32)]
Style(SettingEventStyle),
}
@@ -372,18 +466,6 @@ struct BlockHeader{
num_camera_events:u16,
num_setting_events:u16,
}
impl BlockHeader{
fn payload_size(&self)->usize{
self.num_input_events as usize*EVENT_SIZE[EventType::Input as usize]
+self.num_output_events as usize*EVENT_SIZE[EventType::Output as usize]
+self.num_sound_events as usize*EVENT_SIZE[EventType::Sound as usize]
+self.num_world_events as usize*EVENT_SIZE[EventType::World as usize]
+self.num_gravity_events as usize*EVENT_SIZE[EventType::Gravity as usize]
+self.num_run_events as usize*EVENT_SIZE[EventType::Run as usize]
+self.num_camera_events as usize*EVENT_SIZE[EventType::Camera as usize]
+self.num_setting_events as usize*EVENT_SIZE[EventType::Setting as usize]
}
}
// binread args tech has been further refined
fn read_data_into_events<R,T,F>(
@@ -416,34 +498,28 @@ impl Block{
/// Reserves exactly enough information for the new data.
pub fn extend_from_reader_exact<R:BinReaderExt>(&mut self,mut data:R)->Result<(),BinrwError>{
let block_header:BlockHeader=data.read_le()?;
let mut buffer=Vec::with_capacity(block_header.payload_size());
zstd::stream::copy_decode(data,&mut buffer)?;
let mut reader=std::io::Cursor::new(buffer);
read_data_into_events(&mut reader,&mut self.input_events,block_header.num_input_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut reader,&mut self.output_events,block_header.num_output_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut reader,&mut self.sound_events,block_header.num_sound_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut reader,&mut self.world_events,block_header.num_world_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut reader,&mut self.gravity_events,block_header.num_gravity_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut reader,&mut self.run_events,block_header.num_run_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut reader,&mut self.camera_events,block_header.num_camera_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut reader,&mut self.setting_events,block_header.num_setting_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut data,&mut self.input_events,block_header.num_input_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut data,&mut self.output_events,block_header.num_output_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut data,&mut self.sound_events,block_header.num_sound_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut data,&mut self.world_events,block_header.num_world_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut data,&mut self.gravity_events,block_header.num_gravity_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut data,&mut self.run_events,block_header.num_run_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut data,&mut self.camera_events,block_header.num_camera_events as usize,Vec::reserve_exact)?;
read_data_into_events(&mut data,&mut self.setting_events,block_header.num_setting_events as usize,Vec::reserve_exact)?;
Ok(())
}
/// Read a complete data block and append the elements to the timelines in this block.
pub fn extend_from_reader<R:BinReaderExt>(&mut self,mut data:R)->Result<(),BinrwError>{
// sad code duplication
let block_header:BlockHeader=data.read_le()?;
let mut buffer=Vec::with_capacity(block_header.payload_size());
zstd::stream::copy_decode(data,&mut buffer)?;
let mut reader=std::io::Cursor::new(buffer);
read_data_into_events(&mut reader,&mut self.input_events,block_header.num_input_events as usize,Vec::reserve)?;
read_data_into_events(&mut reader,&mut self.output_events,block_header.num_output_events as usize,Vec::reserve)?;
read_data_into_events(&mut reader,&mut self.sound_events,block_header.num_sound_events as usize,Vec::reserve)?;
read_data_into_events(&mut reader,&mut self.world_events,block_header.num_world_events as usize,Vec::reserve)?;
read_data_into_events(&mut reader,&mut self.gravity_events,block_header.num_gravity_events as usize,Vec::reserve)?;
read_data_into_events(&mut reader,&mut self.run_events,block_header.num_run_events as usize,Vec::reserve)?;
read_data_into_events(&mut reader,&mut self.camera_events,block_header.num_camera_events as usize,Vec::reserve)?;
read_data_into_events(&mut reader,&mut self.setting_events,block_header.num_setting_events as usize,Vec::reserve)?;
read_data_into_events(&mut data,&mut self.input_events,block_header.num_input_events as usize,Vec::reserve)?;
read_data_into_events(&mut data,&mut self.output_events,block_header.num_output_events as usize,Vec::reserve)?;
read_data_into_events(&mut data,&mut self.sound_events,block_header.num_sound_events as usize,Vec::reserve)?;
read_data_into_events(&mut data,&mut self.world_events,block_header.num_world_events as usize,Vec::reserve)?;
read_data_into_events(&mut data,&mut self.gravity_events,block_header.num_gravity_events as usize,Vec::reserve)?;
read_data_into_events(&mut data,&mut self.run_events,block_header.num_run_events as usize,Vec::reserve)?;
read_data_into_events(&mut data,&mut self.camera_events,block_header.num_camera_events as usize,Vec::reserve)?;
read_data_into_events(&mut data,&mut self.setting_events,block_header.num_setting_events as usize,Vec::reserve)?;
Ok(())
}
fn extend_from_block_id_iter<'a,R:BinReaderExt>(&mut self,mut data:R,block_timelines:&BlockTimelines,blocks:impl IntoIterator<Item=&'a Timed<BlockId>>)->Result<(),Error>{
@@ -613,8 +689,7 @@ pub fn read_all_to_block<R:BinReaderExt>(mut data:R)->Result<Block,Error>{
pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),BinrwError>{
use std::ops::Range;
const MAX_BLOCK_SIZE:usize=1<<18;
const FILE_VERSION:u32=1;
const COMPRESSION_LEVEL:i32=19;
const FILE_VERSION:u32=0;
const EVENT_TYPES:[EventType;8]=[
EventType::Input,
EventType::Output,
@@ -625,6 +700,16 @@ pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),B
EventType::Camera,
EventType::Setting,
];
const EVENT_SIZE:[usize;8]=[
8+4+2*4, // Input
8+4+4*3*4, // Output
8+4+4, // Sound
8+4+12, // World
8+3*4, // Gravity
8+4+4+4, // Run
8+4+3*4, // Camera
8+4+8, // Setting
];
#[derive(Clone,Default)]
struct Plan<T>([T;8]);
// A plan of how many events of each type to include in a data block.
@@ -755,40 +840,7 @@ pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),B
num_realtime_blocks:plan_realtime.len() as u32,
};
fn create_block(block:&Block,plan:Plan<Range<usize>>)->Result<Vec<u8>,BinrwError>{
let allocation_size=plan.size();
let mut buffer=Vec::with_capacity(allocation_size);
let mut cursor=std::io::Cursor::new(&mut buffer);
let block_header=plan.header();
for (range,event_type) in plan.0.into_iter().zip(EVENT_TYPES){
let num_events=range.len();
if num_events==0{
continue;
}
match event_type{
EventType::Input=>block.input_events[range].write_le(&mut cursor)?,
EventType::Output=>block.output_events[range].write_le(&mut cursor)?,
EventType::Sound=>block.sound_events[range].write_le(&mut cursor)?,
EventType::World=>block.world_events[range].write_le(&mut cursor)?,
EventType::Gravity=>block.gravity_events[range].write_le(&mut cursor)?,
EventType::Run=>block.run_events[range].write_le(&mut cursor)?,
EventType::Camera=>block.camera_events[range].write_le(&mut cursor)?,
EventType::Setting=>block.setting_events[range].write_le(&mut cursor)?,
}
}
// Allocate way too much space, whatever.
let mut output=Vec::with_capacity(allocation_size);
// Block includes header uncompressed, since the header unambiguously
// determines the size of the output data and that may be useful.
block_header.write_le(&mut std::io::Cursor::new(&mut output))?;
zstd::stream::copy_encode(buffer.as_slice(),&mut output,COMPRESSION_LEVEL)?;
Ok(output)
}
let mut blocks=Vec::with_capacity(plan_offline.len()+plan_realtime.len());
let mut plan_order=Vec::with_capacity(plan_offline.len()+plan_realtime.len());
let mut block_positions=Vec::with_capacity(file_header.block_position_count() as usize);
// Fill the timelines with dummy values, we don't know the block ids yet.
// This can be done with Vec::spare_capacity_mut and unsafe, but whatever.
@@ -802,9 +854,7 @@ pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),B
let mut block_id=0;
let mut push_block=|timeline:&mut Vec<Timed<BlockId>>,planned:PlannedBlock|{
block_positions.push(BlockPosition(position));
let block=create_block(block,planned.plan).unwrap();
position+=block.len() as u32;
blocks.push(block);
position+=planned.plan.size() as u32;
// write the block id to the correct index
timeline[planned.index]=Timed{
@@ -812,6 +862,8 @@ pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),B
event:BlockId(block_id),
};
block_id+=1;
plan_order.push(planned.plan);
};
// the first block in the file is an offline block to
// initialize the state of things like the current style
@@ -852,8 +904,25 @@ pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),B
use binrw::BinWrite;
file_header.write_le(writer)?;
block_timelines.write_le(writer)?;
for block in blocks{
writer.write_all(&block)?;
for plan in plan_order{
let block_header=plan.header();
block_header.write_le(writer)?;
for (range,event_type) in plan.0.into_iter().zip(EVENT_TYPES){
let num_events=range.len();
if num_events==0{
continue;
}
match event_type{
EventType::Input=>block.input_events[range].write_le(writer)?,
EventType::Output=>block.output_events[range].write_le(writer)?,
EventType::Sound=>block.sound_events[range].write_le(writer)?,
EventType::World=>block.world_events[range].write_le(writer)?,
EventType::Gravity=>block.gravity_events[range].write_le(writer)?,
EventType::Run=>block.run_events[range].write_le(writer)?,
EventType::Camera=>block.camera_events[range].write_le(writer)?,
EventType::Setting=>block.setting_events[range].write_le(writer)?,
}
}
}
Ok(())