63 Commits
stream ... peq

Author SHA1 Message Date
f22b0cdb27 expect fail 2025-12-14 19:20:08 -08:00
6b395b970c peq test 2025-12-14 18:57:48 -08:00
0ea00baeb5 v0.7.1-pre1 2025-12-14 18:56:06 -08:00
6b4e56ca82 PartialEq test 2025-12-14 18:55:16 -08:00
d153e8284b clippy fixes 2025-12-14 13:03:01 -08:00
f54af6600b move import to relevant section 2025-12-14 13:01:26 -08:00
fb4fb5b5b3 unplumb block and use closure capture 2025-12-14 12:59:07 -08:00
5dafa3971c generic timeline fn 2025-12-14 12:56:03 -08:00
a1cd627836 use mask 2025-12-14 12:45:42 -08:00
39049fa12e fix block size prediction 2025-12-14 12:34:01 -08:00
236e297cc1 use zip for funsies 2025-12-14 12:19:12 -08:00
838aecbda5 no test exact data 2025-12-14 12:13:27 -08:00
a5d5b30e0c fix chunk header size prediction 2025-12-14 12:06:56 -08:00
99fb761a1d fix lints without itertools 2025-12-13 13:16:59 -08:00
59fcc7c0fb fix tests without itertools 2025-12-13 13:13:55 -08:00
775c510ee6 tab group 2025-12-13 10:58:56 -08:00
22c01e5910 move and tweak comment 2025-12-13 10:58:56 -08:00
8c3e3c9463 skip empty event chunks 2025-12-13 10:58:56 -08:00
f315069f96 comments 2025-12-13 10:58:56 -08:00
881a3bad11 remove macro 2025-12-13 10:58:56 -08:00
bd8207ac2d simplify timeline construction 2025-12-13 10:58:56 -08:00
60ed1e2661 scary shadowing 2025-12-13 10:58:56 -08:00
680d49f6db fix timelines 2025-12-13 10:58:56 -08:00
504c3ff354 fix BlockId & BlockPosition write 2025-12-13 10:58:56 -08:00
89c5f94a28 change variable name 2025-12-13 10:58:56 -08:00
673255383d fix float serialization 2025-12-13 10:58:56 -08:00
677cb86987 fix VecDeque mistake 2025-12-13 10:58:56 -08:00
59b36c8821 fix event range serialization 2025-12-13 10:58:56 -08:00
0dc231d972 fix macro 2025-12-13 10:58:56 -08:00
4de222b1b6 test serializer 2025-12-13 10:58:55 -08:00
40e8f7c595 rename variable 2025-12-13 10:58:55 -08:00
6038241d7a v0 serializer 2025-12-13 10:58:55 -08:00
4ef063a474 merge impl blocks 2025-12-13 10:58:53 -08:00
03b5b8c239 v0.7.0 fix RunClear 2025-12-13 08:39:43 -08:00
488ce55990 fix RunClear 2025-12-13 08:37:56 -08:00
fe98210d10 v0.6.0 fix RunEvent 2025-12-13 08:05:25 -08:00
2eb282dfe2 update deps 2025-12-13 08:05:02 -08:00
c8ec986a2b Fix RunEvent (#2)
It was really wrong!

Reviewed-on: #2
Co-authored-by: Rhys Lloyd <krakow20@gmail.com>
Co-committed-by: Rhys Lloyd <krakow20@gmail.com>
2025-12-13 15:57:26 +00:00
a6ce331682 un-nest example code 2025-12-09 16:05:09 -08:00
6a6849ec79 update readme 2025-11-06 13:09:14 -08:00
8b52f0765f tweak test to remove cfg macro 2025-11-06 10:39:06 -08:00
8030308310 update readme 2025-11-06 10:20:35 -08:00
67bb41fa5e FileHeader::block_timelines_info 2025-11-06 10:20:35 -08:00
7659e941b9 read_offline_to_block + read_realtime_to_block 2025-11-06 10:08:27 -08:00
fd9ddd9ca8 v0.5.4 asymmetric Timed Ord 2025-11-06 16:37:17 +01:00
7f86d0edaf allow Ord comparison between different inner Timed types 2025-11-06 16:36:25 +01:00
6021177364 reduce verbosity in readme (feature is enabled by default) 2025-11-05 16:10:14 -08:00
eb26ba0593 move FileHeader magic 2025-11-05 15:01:48 -08:00
d4e94ab0da binrw tech 2025-11-05 10:56:41 -08:00
cdbb5e6b01 v0.5.3 tweak inner types + add more documentation 2025-11-05 10:56:41 -08:00
240a8798cd document more 2025-11-05 10:56:40 -08:00
7e0cb86a32 BlockInfo extends Range 2025-11-05 10:36:54 -08:00
9fdaf34df3 BlockPosition struct never used in a public interface 2025-11-05 10:24:31 -08:00
cc67966743 unused derives 2025-11-05 10:24:31 -08:00
9af3f4e6cf v0.5.2 derive more traits + relax Timed Ord 2025-11-05 09:56:24 -08:00
95b8fb2d95 add #[derive(Debug,Clone)] to everything 2025-11-05 09:54:08 -08:00
503710aa95 impl PartialEq & PartialOrd for all Timed events 2025-11-05 09:49:46 -08:00
fd59928644 v0.5.1 update deps 2025-11-05 09:29:54 -08:00
873ea5e9da update deps 2025-11-05 09:29:19 -08:00
f3768af543 v0.5.0 refactor 2025-11-05 09:26:43 -08:00
91d1ba68d4 refactor 2025-11-05 09:04:10 -08:00
3a13ae1642 v0.4.0 support multiple bonuses 2025-11-03 18:59:42 +01:00
f21b5f8c40 support multiple bonuses 2025-11-03 18:57:14 +01:00
5 changed files with 682 additions and 298 deletions

50
Cargo.lock generated
View File

@@ -10,9 +10,9 @@ checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc"
[[package]]
name = "binrw"
version = "0.14.1"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d4bca59c20d6f40c2cc0802afbe1e788b89096f61bdf7aeea6bf00f10c2909b"
checksum = "81419ff39e6ed10a92a7f125290859776ced35d9a08a665ae40b23e7ca702f30"
dependencies = [
"array-init",
"binrw_derive",
@@ -21,9 +21,9 @@ dependencies = [
[[package]]
name = "binrw_derive"
version = "0.14.1"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8ba42866ce5bced2645bfa15e97eef2c62d2bdb530510538de8dd3d04efff3c"
checksum = "376404e55ec40d0d6f8b4b7df3f87b87954bd987f0cf9a7207ea3b6ea5c9add4"
dependencies = [
"either",
"owo-colors",
@@ -34,15 +34,15 @@ dependencies = [
[[package]]
name = "bitflags"
version = "2.9.0"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "bytemuck"
version = "1.22.0"
version = "1.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
[[package]]
name = "either"
@@ -51,42 +51,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "owo-colors"
version = "3.5.0"
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "owo-colors"
version = "4.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52"
[[package]]
name = "proc-macro2"
version = "1.0.94"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "strafesnet_roblox_bot_file"
version = "0.3.1"
version = "0.7.1-pre1"
dependencies = [
"binrw",
"bitflags",
"itertools",
]
[[package]]
name = "syn"
version = "1.0.109"
version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
dependencies = [
"proc-macro2",
"quote",
@@ -95,6 +105,6 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.18"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"

View File

@@ -1,8 +1,13 @@
[package]
name = "strafesnet_roblox_bot_file"
version = "0.3.1"
edition = "2021"
version = "0.7.1-pre1"
edition = "2024"
[dependencies]
binrw = "0.14.1"
binrw = "0.15.0"
bitflags = "2.6.0"
itertools = { version = "0.14.0", optional = true }
[features]
default = ["itertools"]
itertools = ["dep:itertools"]

View File

@@ -3,30 +3,45 @@ Roblox Bhop/Surf Bot File Format
## Example
Read the whole file and print each position:
```rust
use strafesnet_roblox_bot_file::{File,TimedBlockId};
use strafesnet_roblox_bot_file::v0::read_all_to_block;
let file=std::fs::File::open("bot_file")?;
let input=std::io::BufReader::new(file);
let mut bot_file=File::new(input)?;
let file=std::fs::read("bot_file")?;
let mut input=std::io::Cursor::new(file);
// read the whole file
let block=bot_file.read_all()?;
let block=read_all_to_block(&mut input)?;
// or do data streaming block by block
for &TimedBlockId{time,block_id} in &bot_file.header.offline_blocks_timeline{
// header is immutably borrowed
// while data is mutably borrowed
let block_info=bot_file.header.block_info(block_id)?;
let block=bot_file.data.read_block_info(block_info)?;
// offline blocks include the following event types:
// World, Gravity, Run, Camera, Setting
for output_event in &block.output_events{
println!("{:?}",output_event.event.position);
}
for &TimedBlockId{time,block_id} in &bot_file.header.realtime_blocks_timeline{
let block_info=bot_file.header.block_info(block_id)?;
let block=bot_file.data.read_block_info(block_info)?;
// realtime blocks include the following event types:
// Input, Output, Sound
```
Or decode individual blocks using block location info:
```rust
use strafesnet_roblox_bot_file::v0::{Block,BlockTimelines,FileHeader};
let file=std::fs::read("bot_file")?;
let mut input=std::io::Cursor::new(file);
// FileHeader is the first 16 bytes of the file.
let header=FileHeader::from_reader(&mut input)?;
// BlockTimelines is an index of the blocks within the file.
let timelines=BlockTimelines::from_reader(&header,&mut input)?;
// offline blocks include the following event types:
// World, Gravity, Run, Camera, Setting
for timed in timelines.offline_blocks(){
let block_info=timelines.block_info(timed.event)?;
let block_reader=block_info.take_seek(&mut input)?;
let block=Block::from_reader(block_reader)?;
}
// realtime blocks include the following event types:
// Input, Output, Sound
for timed in timelines.realtime_blocks(){
let block_info=timelines.block_info(timed.event)?;
let block_reader=block_info.take_seek(&mut input)?;
let block=Block::from_reader(block_reader)?;
}
```

View File

@@ -1,43 +1,60 @@
use crate::v0::{Error,File,TimedBlockId};
use crate::v0::{Block,BlockTimelines,FileHeader,Timed};
#[test]
fn _1()->Result<(),Error>{
let file=std::fs::File::open("files/bhop_marble_7cf33a64-7120-4514-b9fa-4fe29d9523d").unwrap();
let input=std::io::BufReader::new(file);
let mut bot_file=File::new(input).unwrap();
println!("header={:?}",bot_file.header);
for &TimedBlockId{time,block_id} in &bot_file.header.offline_blocks_timeline{
fn deserialize_manual(){
let file=std::fs::read("files/bhop_marble_7cf33a64-7120-4514-b9fa-4fe29d9523d").unwrap();
let mut input=std::io::Cursor::new(file);
let header=FileHeader::from_reader(&mut input).unwrap();
let timelines=BlockTimelines::from_reader(&header,&mut input).unwrap();
println!("header={:?}",header);
for &Timed{time,event:block_id} in timelines.offline_blocks(){
println!("offline time={} block_id={:?}",time,block_id);
let block_info=bot_file.header.block_info(block_id)?;
let _block=bot_file.data.read_block_info(block_info)?;
let take_seek=timelines.block_info(block_id).unwrap().take_seek(&mut input).unwrap();
let _block=Block::from_reader(take_seek).unwrap();
// offline blocks include the following event types:
// World, Gravity, Run, Camera, Setting
}
for &TimedBlockId{time,block_id} in &bot_file.header.realtime_blocks_timeline{
for &Timed{time,event:block_id} in timelines.realtime_blocks(){
println!("realtime time={} block_id={:?}",time,block_id);
let block_info=bot_file.header.block_info(block_id)?;
let _block=bot_file.data.read_block_info(block_info)?;
let take_seek=timelines.block_info(block_id).unwrap().take_seek(&mut input).unwrap();
let _block=Block::from_reader(take_seek).unwrap();
// realtime blocks include the following event types:
// Input, Output, Sound
}
Ok(())
}
#[test]
fn _2()->Result<(),Error>{
let file=std::fs::File::open("files/bhop_marble_7cf33a64-7120-4514-b9fa-4fe29d9523d").unwrap();
let input=std::io::BufReader::new(file);
#[cfg(feature="itertools")]
fn deserialize_all()->Result<(),crate::v0::Error>{
let file=std::fs::read("files/bhop_marble_7cf33a64-7120-4514-b9fa-4fe29d9523d").unwrap();
let t0=std::time::Instant::now();
let mut bot_file=File::new(input).unwrap();
let _block=bot_file.read_all()?;
let _block=crate::v0::read_all_to_block(std::io::Cursor::new(file))?;
println!("{:?}",t0.elapsed());
Ok(())
}
// TODO: file serialization test
#[test]
#[cfg(feature="itertools")]
fn serialize_round_trip()->Result<(),binrw::Error>{
use crate::v0::serialize;
let file=std::fs::read("files/bhop_marble_7cf33a64-7120-4514-b9fa-4fe29d9523d").unwrap();
let mut block=crate::v0::read_all_to_block(std::io::Cursor::new(file.as_slice())).unwrap();
let mut data=Vec::with_capacity(file.len());
serialize(&block,&mut std::io::Cursor::new(&mut data))?;
let block_rt=crate::v0::read_all_to_block(std::io::Cursor::new(data.as_slice())).unwrap();
assert_eq!(block_rt,block);
block.output_events.pop();
assert_eq!(block_rt,block);
Ok(())
}

807
src/v0.rs

File diff suppressed because it is too large Load Diff