use std::fs::File;
use std::io::{self, Cursor, Read, Write};
use std::path::Path;
use anyhow::Result;
use libosu::{
data::{Mode, Mods},
replay::{Buttons, Replay, ReplayActionData},
timing::Millis,
};
#[cfg(feature = "replay-data")]
fn compare_action_data(replay: &Replay, replay2: &Replay) -> Result<()> {
let action_data = replay.parse_action_data()?;
let action_data2 = replay2.parse_action_data()?;
assert_eq!(action_data.frames.len(), action_data2.frames.len());
assert_eq!(action_data.rng_seed, action_data2.rng_seed);
for (a, b) in action_data.frames.iter().zip(action_data2.frames.iter()) {
assert_eq!(a.time, b.time);
assert!((a.x - b.x).abs() < 0.001);
assert!((a.y - b.y).abs() < 0.001);
assert_eq!(a.buttons, b.buttons);
}
Ok(())
}
#[test]
fn test_replay_writer() -> Result<()> {
test_replay_writer_with("tests/files/replay-osu_2058788_3017707256.osr")?;
test_replay_writer_with("tests/files/replay_with_life.osr")?;
Ok(())
}
fn test_replay_writer_with(path: impl AsRef<Path>) -> Result<()> {
let mut osr = File::open(path.as_ref())?;
let mut contents = Vec::new();
osr.read_to_end(&mut contents)?;
let mut curs = Cursor::new(&contents);
let replay = Replay::parse(&mut curs)?;
let mut contents2 = Vec::new();
replay.write(&mut contents2)?;
let mut curs2 = Cursor::new(&contents2);
let replay2 = Replay::parse(&mut curs2)?;
assert_eq!(replay.count_300, replay2.count_300);
assert_eq!(replay.count_100, replay2.count_100);
assert_eq!(replay.count_50, replay2.count_50);
assert_eq!(replay.count_geki, replay2.count_geki);
assert_eq!(replay.life_graph, replay2.life_graph);
#[cfg(feature = "replay-data")]
compare_action_data(&replay, &replay2)?;
Ok(())
}
#[cfg(feature = "replay-data")]
#[test]
fn test_replay_action_update() -> Result<()> {
let mut osr = File::open("tests/files/replay-osu_2058788_3017707256.osr")?;
let replay = Replay::parse(&mut osr)?;
let actions = replay.parse_action_data()?;
let mut replay2 = replay.clone();
replay2.update_action_data(&actions)?;
compare_action_data(&replay, &replay2)?;
Ok(())
}
#[test]
fn test_replay_parse_header() -> Result<()> {
let mut osr = File::open("tests/files/replay-osu_2058788_3017707256.osr")?;
let header = Replay::parse(&mut osr)?;
assert_eq!(header.mode, Mode::Osu);
assert_eq!(header.version, 20200304);
assert_eq!(
header.beatmap_hash,
"4190b795c2847f9eae06a0651493d6e2".to_string()
);
assert_eq!(header.player_username, "FGSky".to_string());
assert_eq!(
header.replay_hash,
"e8983dbdb53360e5d19cbe5de5de49a7".to_string()
);
assert_eq!(header.count_300, 330);
assert_eq!(header.count_100, 24);
assert_eq!(header.count_50, 0);
assert_eq!(header.count_geki, 87);
assert_eq!(header.count_katu, 21);
assert_eq!(header.count_miss, 2);
assert_eq!(header.score, 7756117);
assert_eq!(header.max_combo, 527);
assert_eq!(header.perfect, false);
assert_eq!(
header.mods,
Mods::Flashlight | Mods::Hidden | Mods::DoubleTime | Mods::HardRock
);
Ok(())
}
#[cfg(feature = "replay-data")]
#[test]
fn test_seed() -> Result<()> {
let mut osr = io::BufReader::new(File::open(
"tests/files/replay-osu_2058788_3017707256.osr",
)?);
let replay = Replay::parse(&mut osr)?;
let actions = replay.parse_action_data()?;
assert_eq!(actions.rng_seed, Some(16516643));
Ok(())
}
#[test]
fn test_parse_after_actions() -> Result<()> {
{
let mut osr = File::open(
"tests/files/ - nekodex - new beginnings [tutorial] (2020-12-16) Osu.osr",
)?;
let replay = Replay::parse(&mut osr)?;
assert_eq!(replay.score_id, None);
assert_eq!(replay.target_practice_total_accuracy, None);
}
{
let mut osr = File::open("tests/files/replay-osu_2058788_3017707256.osr")?;
let replay = Replay::parse(&mut osr)?;
assert_eq!(replay.score_id, Some(3017707256));
assert_eq!(replay.target_practice_total_accuracy, None);
}
Ok(())
}
#[cfg(feature = "replay-data-xz2")]
fn lzma_encode(data: &[u8]) -> Result<Vec<u8>> {
use xz2::{
stream::{LzmaOptions, Stream},
write::XzEncoder,
};
let mut buf = Vec::new();
let opts = LzmaOptions::new_preset(0)?;
let stream = Stream::new_lzma_encoder(&opts)?;
{
let mut xz = XzEncoder::new_stream(&mut buf, stream);
xz.write_all(data)?;
}
Ok(buf)
}
#[cfg(feature = "replay-data")]
fn lzma_encode(mut data: &[u8]) -> Result<Vec<u8>> {
let mut data_out = Vec::new();
lzma_rs::lzma_compress(&mut data, &mut data_out)?;
Ok(data_out)
}
#[cfg(feature = "replay-data")]
#[test]
fn test_replay_action_parser() -> Result<()> {
let actions_text = "1|32.1|300.734|0,32|500.5123|0|10,-12345|0|0|734243";
let data = lzma_encode(actions_text.as_bytes())?;
let actions_reader = Cursor::new(data);
let action_data = ReplayActionData::parse(actions_reader)?;
let actions = &action_data.frames;
assert_eq!(actions.len(), 2);
assert_eq!(actions[0].time, Millis(1));
assert_eq!(actions[0].x, 32.1);
assert_eq!(actions[0].y, 300.734);
assert_eq!(actions[0].buttons, Buttons::empty());
assert_eq!(actions[1].time, Millis(32));
assert_eq!(actions[1].x, 500.5123);
assert_eq!(actions[1].y, 0.0);
assert_eq!(actions[1].buttons, Buttons::K2 | Buttons::M2);
assert_eq!(action_data.rng_seed, Some(734243));
Ok(())
}
#[test]
fn test_replay_parse() -> Result<()> {
let mut osr = File::open("tests/files/replay-osu_1816113_2892542031.osr")?;
let replay = Replay::parse(&mut osr)?;
assert_eq!(replay.mode, Mode::Osu);
assert_eq!(replay.version, 20190906);
assert_eq!(
replay.beatmap_hash,
"edd35ab673c5f73029cc8eda6faefe00".to_string()
);
assert_eq!(replay.player_username, "Vaxei".to_string());
assert_eq!(
replay.replay_hash,
"139c99f18fc78555cd8f30a963aadf0a".to_string()
);
assert_eq!(replay.count_300, 2977);
assert_eq!(replay.count_100, 38);
assert_eq!(replay.count_50, 0);
assert_eq!(replay.count_geki, 605);
assert_eq!(replay.count_katu, 30);
assert_eq!(replay.count_miss, 0);
assert_eq!(replay.score, 364_865_850);
assert_eq!(replay.max_combo, 4078);
assert_eq!(replay.perfect, false);
assert_eq!(replay.mods, Mods::None);
#[cfg(feature = "replay-data")]
{
let action_data = replay.parse_action_data()?;
assert_eq!(action_data.rng_seed, Some(7364804));
}
assert_eq!(replay.score_id, Some(2892542031));
assert_eq!(replay.target_practice_total_accuracy, None);
Ok(())
}