#[cfg(feature = "replay-data")]
#[cfg_attr(docsrs, doc(cfg(feature = "replay-data")))]
use std::io::BufRead;
use crate::timing::Millis;
#[cfg(feature = "replay-data")]
#[cfg_attr(docsrs, doc(cfg(feature = "replay-data")))]
use super::ReplayResult;
#[derive(Debug, Clone)]
pub struct ReplayAction {
pub time: Millis,
pub x: f32,
pub y: f32,
pub buttons: Buttons,
}
bitflags! {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Buttons: u32 {
const M1 = 1;
const M2 = 2;
const K1 = 4;
const K2 = 8;
const SMOKE = 16;
}
}
#[derive(Debug, Clone)]
pub struct ReplayActionData {
pub frames: Vec<ReplayAction>,
pub rng_seed: Option<u32>,
}
impl ReplayActionData {
#[cfg(feature = "replay-data")]
#[cfg_attr(docsrs, doc(cfg(feature = "replay-data")))]
pub fn parse(data: impl BufRead) -> ReplayResult<Self> {
use super::ReplayError;
let data = super::lzma::decode(data)?;
let string = String::from_utf8(data)?;
let mut frames = string
.split(',')
.filter(|action_str| !action_str.trim().is_empty())
.map(|action_str| {
let mut parts = action_str.split('|');
let time = Millis(parts.next().unwrap().parse::<i32>()?);
let x = parts.next().unwrap().parse::<f32>()?;
let y = parts.next().unwrap().parse::<f32>()?;
let bits = parts.next().unwrap().parse::<u32>()?;
let buttons = if time.0 == -12345 {
Buttons::from_bits_retain(bits)
} else {
Buttons::from_bits(bits).ok_or(ReplayError::InvalidButtons(bits))?
};
Ok(ReplayAction {
time,
x,
y,
buttons,
})
})
.collect::<ReplayResult<Vec<_>>>()?;
let has_seed = matches!(
frames.last(),
Some(ReplayAction {
time: Millis(-12345),
..
})
);
let rng_seed = if has_seed {
let last_element = frames.pop().expect("has_seed checked");
Some(last_element.buttons.bits())
} else {
None
};
Ok(ReplayActionData { frames, rng_seed })
}
}