use super::{utils::string_null_pad, ElmaError, Position};
use crate::utils::{boolean, null_padded_string};
use byteorder::{WriteBytesExt, LE};
use itertools::izip;
use nom::{
bytes::complete::take,
combinator::{map, map_opt, verify},
multi::{count, many_m_n},
number::complete::{le_f32, le_f64, le_i16, le_i32, le_u32, le_u8},
IResult,
};
use std::fs;
use std::path::PathBuf;
const END_OF_PLAYER: i32 = 0x00_49_2F_75;
const REPLAY_VERSION: u32 = 0x83;
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum Direction {
Right,
Left,
}
impl Default for Direction {
fn default() -> Self {
Direction::Left
}
}
#[derive(Debug, Default, PartialEq, Clone)]
pub struct Frame {
pub bike: Position<f32>,
pub left_wheel: Position<i16>,
pub right_wheel: Position<i16>,
pub head: Position<i16>,
pub rotation: i16,
pub left_wheel_rotation: u8,
pub right_wheel_rotation: u8,
pub throttle_and_dir: u8,
pub back_wheel_rot_speed: u8,
pub collision_strength: u8,
}
impl Frame {
pub fn new() -> Self {
Frame::default()
}
pub fn throttle(&self) -> bool {
self.throttle_and_dir & 1 != 0
}
pub fn direction(&self) -> Direction {
if self.throttle_and_dir & (1 << 1) != 0 {
Direction::Right
} else {
Direction::Left
}
}
}
#[derive(Debug, Default, PartialEq, Clone)]
pub struct Event {
pub time: f64,
pub event_type: EventType,
}
#[derive(Debug, PartialEq, Clone)]
pub enum EventType {
ObjectTouch(i16),
Apple,
Turn,
VoltRight,
VoltLeft,
Ground(f32),
}
impl<'a> From<&'a EventType> for u8 {
fn from(event_type: &'a EventType) -> Self {
match *event_type {
EventType::Apple => 4,
EventType::Ground(_) => 1,
EventType::ObjectTouch(_) => 0,
EventType::Turn => 5,
EventType::VoltLeft => 7,
EventType::VoltRight => 6,
}
}
}
impl Default for EventType {
fn default() -> EventType {
EventType::ObjectTouch(0)
}
}
impl Event {
pub fn new() -> Self {
Event::default()
}
}
#[derive(Debug, PartialEq)]
pub(crate) struct ReplayHeader {
pub multi: bool,
pub flag_tag: bool,
pub link: u32,
pub level: String,
}
#[derive(Debug, PartialEq, Default, Clone)]
pub struct Ride {
pub frames: Vec<Frame>,
pub events: Vec<Event>,
}
impl Ride {
pub fn new() -> Self {
Ride {
frames: vec![],
events: vec![],
}
}
pub fn get_frame_time(&self) -> f64 {
self.frames.len() as f64 * 33.333
}
pub fn get_time(&self) -> f64 {
let last_event = self.events.last();
let time = match last_event {
Some(e) => match e.event_type {
EventType::ObjectTouch { .. } => e.time,
_ => 0_f64,
},
None => 0_f64,
};
time * 2_289.377_289_38
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Replay {
pub flag_tag: bool,
pub link: u32,
pub level: String,
pub rides: Vec<Ride>,
}
impl Replay {
pub fn is_multi(&self) -> bool {
self.rides.len() > 1
}
}
impl Default for Replay {
fn default() -> Replay {
Replay::new()
}
}
fn parse_header_and_ride(input: &[u8]) -> IResult<&[u8], (ReplayHeader, Ride)> {
let (input, frame_count) = map(le_i32, |x| x as usize)(input)?;
let (input, _) = verify(le_u32, |x| *x == REPLAY_VERSION)(input)?;
let (input, multi) = boolean(input)?;
let (input, flag_tag) = boolean(input)?;
let (input, link) = le_u32(input)?;
let (input, level) = null_padded_string(input, 16)?;
let (input, bodyx) = count(le_f32, frame_count)(input)?;
let (input, bodyy) = count(le_f32, frame_count)(input)?;
let (input, leftwheelx) = count(le_i16, frame_count)(input)?;
let (input, leftwheely) = count(le_i16, frame_count)(input)?;
let (input, rightwheelx) = count(le_i16, frame_count)(input)?;
let (input, rightwheely) = count(le_i16, frame_count)(input)?;
let (input, headx) = count(le_i16, frame_count)(input)?;
let (input, heady) = count(le_i16, frame_count)(input)?;
let (input, rotation) = count(le_i16, frame_count)(input)?;
let (input, leftwheelrotation) = count(le_u8, frame_count)(input)?;
let (input, rightwheelrotation) = count(le_u8, frame_count)(input)?;
let (input, dir_and_throttle) = count(le_u8, frame_count)(input)?;
let (input, back_wheel) = count(le_u8, frame_count)(input)?;
let (input, collision_strength) = count(le_u8, frame_count)(input)?;
let (input, num_events) = map(le_i32, |x| x as usize)(input)?;
let (input, events) = count(parse_event, num_events)(input)?;
let (input, _) = verify(le_i32, |x| *x == END_OF_PLAYER)(input)?;
let header = ReplayHeader {
multi,
flag_tag,
link,
level: level.to_string(),
};
let frames = izip!(
bodyx,
bodyy,
leftwheelx,
leftwheely,
rightwheelx,
rightwheely,
headx,
heady,
rotation,
leftwheelrotation,
rightwheelrotation,
dir_and_throttle,
back_wheel,
collision_strength
)
.map(
|(bx, by, lx, ly, rx, ry, hx, hy, r, lr, rr, dt, bw, cs)| Frame {
bike: Position::new(bx, by),
left_wheel: Position::new(lx, ly),
right_wheel: Position::new(rx, ry),
head: Position::new(hx, hy),
rotation: r,
left_wheel_rotation: lr,
right_wheel_rotation: rr,
throttle_and_dir: dt,
back_wheel_rot_speed: bw,
collision_strength: cs,
},
)
.collect();
let ride = Ride { frames, events };
Ok((input, (header, ride)))
}
fn parse_event(input: &[u8]) -> IResult<&[u8], Event> {
let (input, time) = le_f64(input)?;
let (input, info) = le_i16(input)?;
let (input, mut event_type) = map_opt(le_u8, |event_type| match event_type {
0 => Some(EventType::ObjectTouch(info)),
1 => Some(EventType::Ground(0.0)), 4 => Some(EventType::Apple),
5 => Some(EventType::Turn),
6 => Some(EventType::VoltRight),
7 => Some(EventType::VoltLeft),
_ => None,
})(input)?;
let (input, _) = take(1u8)(input)?;
let (input, info2) = le_f32(input)?;
if let EventType::Ground(ref mut val) = event_type { *val = info2 }
Ok((input, Event { time, event_type }))
}
fn parse_replay(input: &[u8]) -> IResult<&[u8], Replay> {
let (input, players) = many_m_n(1, 2, parse_header_and_ride)(input)?;
let replay = Replay {
flag_tag: players[0].0.flag_tag,
link: players[0].0.link,
level: players[0].0.level.clone(),
rides: players.into_iter().map(|(_, ride)| ride).collect(),
};
Ok((input, replay))
}
impl Replay {
pub fn new() -> Self {
Replay {
flag_tag: false,
link: 0,
level: String::new(),
rides: vec![],
}
}
pub fn load<P: Into<PathBuf>>(path: P) -> Result<Self, ElmaError> {
let path = path.into();
let buffer = fs::read(path.as_path())?;
let rec = Replay::parse_replay(&buffer)?;
Ok(rec)
}
pub fn from_bytes<B: AsRef<[u8]>>(buffer: B) -> Result<Self, ElmaError> {
Replay::parse_replay(buffer.as_ref())
}
fn parse_replay(buffer: &[u8]) -> Result<Self, ElmaError> {
match parse_replay(buffer) {
Ok((_, replay)) => Ok(replay),
Err(_) => Err(ElmaError::InvalidReplayFile),
}
}
pub fn to_bytes(&self) -> Result<Vec<u8>, ElmaError> {
let mut bytes: Vec<u8> = vec![];
for r in &self.rides {
bytes.write_i32::<LE>(r.frames.len() as i32)?;
bytes.write_u32::<LE>(REPLAY_VERSION)?;
bytes.write_i32::<LE>(if self.is_multi() { 1_i32 } else { 0_i32 })?;
bytes.write_i32::<LE>(if self.flag_tag { 1_i32 } else { 0_i32 })?;
bytes.write_u32::<LE>(self.link)?;
bytes.extend_from_slice(&string_null_pad(&self.level, 12)?);
bytes.write_i32::<LE>(0x00_i32)?;
bytes.extend_from_slice(&write_frames(&r.frames)?);
bytes.extend_from_slice(&write_events(&r.events)?);
bytes.write_i32::<LE>(END_OF_PLAYER)?;
}
Ok(bytes)
}
pub fn save<P: Into<PathBuf>>(&mut self, path: P) -> Result<(), ElmaError> {
let path = path.into();
fs::write(path.as_path(), &self.to_bytes()?)?;
Ok(())
}
pub fn get_time_ms(&self) -> (usize, bool) {
let times = self
.rides
.iter()
.map(|r| (r.get_time(), r.get_frame_time()))
.collect::<Vec<_>>();
let (event_time_max, frame_time_max) =
times.iter().fold((0_f64, 0_f64), |(acc_a, acc_b), (a, b)| {
(a.max(acc_a), b.max(acc_b))
});
if event_time_max == 0. {
return (frame_time_max.round() as usize, false);
}
if frame_time_max > (event_time_max + 33.333) {
return (frame_time_max.round() as usize, false);
}
(event_time_max.round() as usize, true)
}
pub fn get_time_hs(&self) -> (usize, bool) {
let (time, finished) = self.get_time_ms();
(time / 10, finished)
}
}
fn write_frames(frame_data: &[Frame]) -> Result<Vec<u8>, ElmaError> {
let mut bytes = vec![];
let mut bike_x = vec![];
let mut bike_y = vec![];
let mut left_x = vec![];
let mut left_y = vec![];
let mut right_x = vec![];
let mut right_y = vec![];
let mut head_x = vec![];
let mut head_y = vec![];
let mut rotation = vec![];
let mut left_rotation = vec![];
let mut right_rotation = vec![];
let mut data = vec![];
let mut back_wheel = vec![];
let mut collision = vec![];
for frame in frame_data {
bike_x.write_f32::<LE>(frame.bike.x)?;
bike_y.write_f32::<LE>(frame.bike.y)?;
left_x.write_i16::<LE>(frame.left_wheel.x)?;
left_y.write_i16::<LE>(frame.left_wheel.y)?;
right_x.write_i16::<LE>(frame.right_wheel.x)?;
right_y.write_i16::<LE>(frame.right_wheel.y)?;
head_x.write_i16::<LE>(frame.head.x)?;
head_y.write_i16::<LE>(frame.head.y)?;
rotation.write_i16::<LE>(frame.rotation)?;
left_rotation.write_u8(frame.left_wheel_rotation)?;
right_rotation.write_u8(frame.right_wheel_rotation)?;
data.write_u8(frame.throttle_and_dir)?;
back_wheel.write_u8(frame.back_wheel_rot_speed)?;
collision.write_u8(frame.collision_strength)?;
}
bytes.extend_from_slice(&bike_x);
bytes.extend_from_slice(&bike_y);
bytes.extend_from_slice(&left_x);
bytes.extend_from_slice(&left_y);
bytes.extend_from_slice(&right_x);
bytes.extend_from_slice(&right_y);
bytes.extend_from_slice(&head_x);
bytes.extend_from_slice(&head_y);
bytes.extend_from_slice(&rotation);
bytes.extend_from_slice(&left_rotation);
bytes.extend_from_slice(&right_rotation);
bytes.extend_from_slice(&data);
bytes.extend_from_slice(&back_wheel);
bytes.extend_from_slice(&collision);
Ok(bytes)
}
fn write_events(event_data: &[Event]) -> Result<Vec<u8>, ElmaError> {
let mut bytes = vec![];
bytes.write_i32::<LE>(event_data.len() as i32)?;
for event in event_data {
bytes.write_f64::<LE>(event.time)?;
let default_info = -1;
let default_info2 = 0.99;
let event_type = (&event.event_type).into();
match event.event_type {
EventType::ObjectTouch(info) => {
bytes.write_i16::<LE>(info)?;
bytes.write_u8(event_type)?;
bytes.write_u8(0)?;
bytes.write_f32::<LE>(0.0)?; }
EventType::Ground(info2) => {
bytes.write_i16::<LE>(default_info)?;
bytes.write_u8(event_type)?;
bytes.write_u8(0)?;
bytes.write_f32::<LE>(info2)?;
}
_ => {
bytes.write_i16::<LE>(default_info)?;
bytes.write_u8(event_type)?;
bytes.write_u8(0)?;
bytes.write_f32::<LE>(default_info2)?;
}
}
}
Ok(bytes)
}