v0--v2 replay upgrade, make decoder own file

This commit is contained in:
2025-10-31 13:23:51 -07:00
parent cddbe6bf08
commit 4e77d2c988
6 changed files with 534 additions and 61 deletions
Generated
+358 -3
View File
@@ -8,12 +8,55 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bindgen"
version = "0.63.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885"
dependencies = [
"bitflags 1.3.2",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn 1.0.109",
"which",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "bytemuck"
version = "1.24.0"
@@ -38,12 +81,32 @@ dependencies = [
"shlex",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -53,6 +116,22 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.4"
@@ -82,6 +161,21 @@ dependencies = [
"wasip2",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "home"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "jobserver"
version = "0.1.34"
@@ -92,12 +186,34 @@ dependencies = [
"libc",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "libloading"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
dependencies = [
"cfg-if",
"windows-link",
]
[[package]]
name = "libz-rs-sys"
version = "0.5.2"
@@ -107,6 +223,30 @@ dependencies = [
"zlib-rs",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "log"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
@@ -123,6 +263,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -132,12 +282,24 @@ dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "pkg-config"
version = "0.3.32"
@@ -168,6 +330,46 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "regex"
version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "retro-rs"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "656d99fcb289714d4b7e294187c09fa8d0563414b8b3f1dca2f5d258bd221441"
dependencies = [
"libc",
"libloading",
"rust-libretro-sys",
]
[[package]]
name = "rmp"
version = "0.8.14"
@@ -187,6 +389,7 @@ dependencies = [
"byteorder",
"flate2",
"nohash-hasher",
"retro-rs",
"rmp",
"smallvec",
"thiserror",
@@ -194,6 +397,47 @@ dependencies = [
"zstd",
]
[[package]]
name = "rust-libretro-sys"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cbdb106ca97d195be38097412623e1e89926fa3de9f3af995145f6fa0c958c6"
dependencies = [
"bindgen",
"libc",
"rust-libretro-sys-proc",
]
[[package]]
name = "rust-libretro-sys-proc"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dad70ae46523827ed74c292727aa40021db22397e570a31dd86240c87fc2d929"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags 2.10.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "shlex"
version = "1.3.0"
@@ -212,6 +456,17 @@ version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[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 = "syn"
version = "2.0.108"
@@ -240,14 +495,14 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.108",
]
[[package]]
name = "unicode-ident"
version = "1.0.20"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "wasip2"
@@ -258,6 +513,106 @@ dependencies = [
"wit-bindgen",
]
[[package]]
name = "which"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "wit-bindgen"
version = "0.46.0"
+1
View File
@@ -13,3 +13,4 @@ smallvec = "1.15.1"
thiserror = "2.0.17"
xxhash-rust = { version = "0.8.15", features = ["xxh3"] }
zstd = "0.13.3"
retro-rs = { version = "0.5.2", default-features=false }
+2 -2
View File
@@ -4,8 +4,8 @@ fn main() {
let args: Vec<_> = std::env::args().collect();
let file =
std::fs::File::open(args.get(1).unwrap_or(&"examples/bobl.replay".to_string())).unwrap();
let mut file = std::io::BufReader::new(file);
let mut rply = decode(&mut file).unwrap();
let file = std::io::BufReader::new(file);
let mut rply = decode(file).unwrap();
let header = &rply.header;
println!("{header:?}");
let mut frame = Frame::default();
+7 -3
View File
@@ -9,13 +9,13 @@ fn main() {
.unwrap_or(&"examples/bobl_smallblocks.replay".to_string()),
)
.unwrap();
let mut file = std::io::BufReader::new(file);
let file = std::io::BufReader::new(file);
let mut outfile = std::io::BufWriter::new(outfile);
let mut rply = decode(&mut file).unwrap();
let mut rply = decode(file).unwrap();
let header = &rply.header;
println!("{header:?}");
if header.version() == 0 {
println!("Can't upgrade v0 replays without a core");
println!("Can't upgrade v0 replays with reencode, upgrade to v1 first using upgrade0");
std::process::exit(-1);
}
let mut header_out = header.clone();
@@ -38,6 +38,10 @@ fn main() {
rply.frame_number,
frame.inputs(),
);
// TODO run libretro core here, maybe serialize into frame.checkpoint_bytes
// TODO maybe get screenshot or add to video
//frame.drop_checkpoint();
out.write_frame(&frame).unwrap();
if Some(rply.frame_number) == rply.header.frame_count() {
+73
View File
@@ -0,0 +1,73 @@
use retro_rs::Emulator;
use rply_codec::{Frame, InputData, ReplayError, decode, encode};
use std::cell::RefCell;
use std::path::Path;
use std::rc::Rc;
fn main() {
let args: Vec<_> = std::env::args().collect();
let file =
std::fs::File::open(args.get(1).unwrap_or(&"examples/v0.replay".to_string())).unwrap();
let outfile =
std::fs::File::create(args.get(2).unwrap_or(&"examples/v2.replay".to_string())).unwrap();
let corefile = args
.get(3)
.unwrap_or(&"cores/fceumm_libretro".to_string())
.clone();
let romfile = args.get(4).unwrap_or(&"roms/demo.nes".to_string()).clone();
let mut emu = Emulator::create(Path::new(&corefile), Path::new(&romfile));
let file = std::io::BufReader::new(file);
let mut outfile = std::io::BufWriter::new(outfile);
let mut rply = decode(file).unwrap();
let header = &rply.header;
println!("Header in: {header:?}");
if header.version() != 0 {
println!("Only use this program for v0 replays!");
std::process::exit(-1);
}
assert!(emu.load(&rply.initial_state));
let mut header_out = header.clone();
header_out.upgrade();
let mut encoder = encode(header_out, &rply.initial_state, &mut outfile).unwrap();
let mut frame = Frame::default();
rply.read_key_events(&mut frame).unwrap();
rply.read_end_of_frame(&mut frame).unwrap();
let frame = Rc::new(RefCell::new(frame));
let rply = Rc::new(RefCell::new(rply));
let cb = {
let frame = Rc::clone(&frame);
let rply = Rc::clone(&rply);
Box::new(move |port, device, idx, id| {
let val = rply.borrow_mut().read_v0_button().unwrap();
//println!("{port}-{device}-{idx}-{id}: 0x{val:x}");
frame.borrow_mut().input_events.push(InputData {
port: u8::try_from(port).unwrap(),
device: u8::try_from(device).unwrap(),
idx: u8::try_from(idx).unwrap(),
id: u16::try_from(id).unwrap(),
val,
});
val
})
};
loop {
frame.borrow_mut().clear();
emu.run_with_button_callback(cb.clone());
match rply.borrow_mut().read_key_events(&mut frame.borrow_mut()) {
Ok(()) => {}
Err(ReplayError::IO(e)) => {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
break;
}
panic!("{e}");
}
Err(e) => panic!("{e}"),
}
rply.borrow_mut()
.read_end_of_frame(&mut frame.borrow_mut())
.unwrap();
encoder.write_frame(&frame.borrow()).unwrap();
}
encoder.finish().unwrap();
println!("Header out: {:?}", encoder.header);
}
+92 -52
View File
@@ -176,15 +176,15 @@ pub enum ReplayError {
type Result<T> = std::result::Result<T, ReplayError>;
pub struct ReplayDecoder<'a, R: std::io::BufRead> {
rply: &'a mut R,
pub struct ReplayDecoder<R: std::io::BufRead> {
rply: R,
pub header: Header,
pub initial_state: Vec<u8>,
pub frame_number: u64,
ss_state: statestream::Ctx,
}
impl<R: std::io::BufRead> ReplayDecoder<'_, R> {
impl<R: std::io::BufRead> ReplayDecoder<R> {
/// Creates a [`ReplayDecoder`] for the given buffered readable stream.
///
/// # Errors
@@ -192,7 +192,7 @@ impl<R: std::io::BufRead> ReplayDecoder<'_, R> {
/// [`ReplayError::Magic`]: Invalid magic number at beginning of file
/// [`ReplayError::Version`]: Version identifier not recognized by parser
/// [`ReplayError::Compression`]: Unsupported compression scheme for checkpoints
pub fn new(rply: &mut R) -> Result<ReplayDecoder<'_, R>> {
pub fn new(mut rply: R) -> Result<ReplayDecoder<R>> {
use byteorder::{LittleEndian, ReadBytesExt};
let magic = rply.read_u32::<LittleEndian>()?;
if magic != MAGIC {
@@ -245,35 +245,20 @@ impl<R: std::io::BufRead> ReplayDecoder<'_, R> {
frame_number: 0,
ss_state: statestream::Ctx::new(block_size, superblock_size),
};
if replay.header.version() == 1 {
replay.rply.read_exact(&mut replay.initial_state)?;
} else {
replay.decode_initial_checkpoint()?;
}
Ok(replay)
}
/// Reads a single frame at the current decoder position.
pub fn inner(&mut self) -> &mut R {
&mut self.rply
}
/// Reads keyboard event records at the current input position. Only really appropriate to explicitly call for v0 replays.
/// # Errors
/// [`ReplayError::IO`]: Unexpected end of stream or other I/O error
/// [`ReplayError::Compression`]: Unsupported compression scheme
/// [`ReplayError::Encoding`]: Unsupported encoding scheme
/// [`ReplayError::BadFrameToken`]: Frame token not recognized or misaligned
/// [`ReplayError::NoCoreRead`]: Tried to read a frame on a version 0 replay without a loaded core
/// [`ReplayError::CheckpointTooBig`]: Tried to read a checkpoint bigger than the address space
#[allow(clippy::too_many_lines)]
pub fn read_frame(&mut self, frame: &mut Frame) -> Result<()> {
pub fn read_key_events(&mut self, frame: &mut Frame) -> Result<()> {
use byteorder::{LittleEndian, ReadBytesExt};
let stopwatch = clock::time(Timer::DecodeFrame);
let vsn = self.header.version();
let rply = &mut *self.rply;
if vsn == 0 {
return Err(ReplayError::NoCoreRead());
}
if vsn > 1 {
/* skip over the backref */
let _ = rply.read_u32::<LittleEndian>()?;
}
let rply = &mut self.rply;
let key_count = rply.read_u8()? as usize;
frame.key_events.resize_with(key_count, Default::default);
for ki in 0..key_count {
@@ -294,6 +279,74 @@ impl<R: std::io::BufRead> ReplayDecoder<'_, R> {
};
frame.key_events[ki] = key_data;
}
Ok(())
}
/// Reads an end of frame marker at the current input position. Only really appropriate to explicitly call for v0 replays.
/// # Errors
/// [`ReplayError::IO`]: Unexpected end of stream or other I/O error
/// [`ReplayError::Compression`]: Unsupported compression scheme
/// [`ReplayError::Encoding`]: Unsupported encoding scheme
/// [`ReplayError::BadFrameToken`]: Frame token not recognized or misaligned
/// [`ReplayError::CheckpointTooBig`]: Tried to read a checkpoint bigger than the address space
pub fn read_end_of_frame(&mut self, frame: &mut Frame) -> Result<()> {
use byteorder::{LittleEndian, ReadBytesExt};
let rply = &mut self.rply;
let tok = rply.read_u8()?;
match FrameToken::from(tok) {
FrameToken::Regular => {
frame.checkpoint_compression = Compression::None;
frame.checkpoint_encoding = Encoding::Raw;
frame.checkpoint_bytes.clear();
}
FrameToken::Checkpoint => {
frame.checkpoint_compression = Compression::None;
frame.checkpoint_encoding = Encoding::Raw;
let cp_size = usize::try_from(rply.read_u64::<LittleEndian>()?)
.map_err(ReplayError::CheckpointTooBig)?;
frame.checkpoint_bytes.resize(cp_size, 0);
rply.read_exact(frame.checkpoint_bytes.as_mut_slice())?;
}
FrameToken::Checkpoint2 => {
self.decode_checkpoint(&mut frame.checkpoint_bytes)?;
}
_ => return Err(ReplayError::BadFrameToken(tok)),
}
Ok(())
}
/// Reads a single button value at the current input position. Only appropriate for v0 replays and only if you are implementing an input callback for a core.
/// # Errors
/// [`ReplayError::IO`]: Unexpected end of stream or other I/O error
pub fn read_v0_button(&mut self) -> Result<i16> {
use byteorder::{LittleEndian, ReadBytesExt};
self.rply
.read_i16::<LittleEndian>()
.map_err(ReplayError::IO)
}
/// Reads a single frame at the current decoder position.
/// # Errors
/// [`ReplayError::IO`]: Unexpected end of stream or other I/O error
/// [`ReplayError::Compression`]: Unsupported compression scheme
/// [`ReplayError::Encoding`]: Unsupported encoding scheme
/// [`ReplayError::BadFrameToken`]: Frame token not recognized or misaligned
/// [`ReplayError::NoCoreRead`]: Tried to read a frame on a version 0 replay without a loaded core
/// [`ReplayError::CheckpointTooBig`]: Tried to read a checkpoint bigger than the address space
#[allow(clippy::too_many_lines)]
pub fn read_frame(&mut self, frame: &mut Frame) -> Result<()> {
use byteorder::{LittleEndian, ReadBytesExt};
let stopwatch = clock::time(Timer::DecodeFrame);
let vsn = self.header.version();
if vsn == 0 {
return Err(ReplayError::NoCoreRead());
}
if vsn > 1 {
/* skip over the backref */
let _ = self.rply.read_u32::<LittleEndian>()?;
}
self.read_key_events(frame)?;
let rply = &mut self.rply;
let input_count = rply.read_u16::<LittleEndian>()? as usize;
frame
.input_events
@@ -315,26 +368,7 @@ impl<R: std::io::BufRead> ReplayDecoder<'_, R> {
};
frame.input_events[ii] = inp_data;
}
let tok = rply.read_u8()?;
match FrameToken::from(tok) {
FrameToken::Invalid => return Err(ReplayError::BadFrameToken(tok)),
FrameToken::Regular => {
frame.checkpoint_compression = Compression::None;
frame.checkpoint_encoding = Encoding::Raw;
frame.checkpoint_bytes.clear();
}
FrameToken::Checkpoint => {
frame.checkpoint_compression = Compression::None;
frame.checkpoint_encoding = Encoding::Raw;
let cp_size = usize::try_from(rply.read_u64::<LittleEndian>()?)
.map_err(ReplayError::CheckpointTooBig)?;
frame.checkpoint_bytes.resize(cp_size, 0);
rply.read_exact(frame.checkpoint_bytes.as_mut_slice())?;
}
FrameToken::Checkpoint2 => {
self.decode_checkpoint(&mut frame.checkpoint_bytes)?;
}
}
self.read_end_of_frame(frame)?;
self.frame_number += 1;
drop(stopwatch);
Ok(())
@@ -350,7 +384,7 @@ impl<R: std::io::BufRead> ReplayDecoder<'_, R> {
fn decode_checkpoint(&mut self, checkpoint_bytes: &mut Vec<u8>) -> Result<()> {
use byteorder::{LittleEndian, ReadBytesExt};
let stopwatch = clock::time(Timer::DecodeCheckpoint);
let rply = &mut *self.rply;
let rply = &mut self.rply;
// read a 1 byte compression code
let compression =
Compression::try_from(rply.read_u8()?).map_err(ReplayError::Compression)?;
@@ -424,7 +458,7 @@ impl<R: std::io::BufRead> ReplayDecoder<'_, R> {
///
/// # Errors
/// See [`ReplayDecoder::new`].
pub fn decode<R: std::io::BufRead>(rply: &mut R) -> Result<ReplayDecoder<'_, R>> {
pub fn decode<R: std::io::BufRead>(rply: R) -> Result<ReplayDecoder<R>> {
ReplayDecoder::new(rply)
}
@@ -723,14 +757,15 @@ impl Header {
*self = Header::V2(HeaderV2 {
base: base.clone(),
frame_count: 0,
block_size: 0,
superblock_size: 0,
checkpoint_commit_interval: 0,
checkpoint_commit_threshold: 0,
block_size: 256,
superblock_size: 256,
checkpoint_commit_interval: 8,
checkpoint_commit_threshold: 4,
checkpoint_compression: Compression::None,
});
}
let Header::V2(v2) = self else { unreachable!() };
v2.base.version = 2;
v2
}
#[must_use]
@@ -830,6 +865,11 @@ impl Frame {
self.checkpoint_compression = Compression::None;
self.checkpoint_encoding = Encoding::Raw;
}
pub fn clear(&mut self) {
self.key_events.clear();
self.input_events.clear();
self.drop_checkpoint();
}
}
impl Default for Frame {