| Crates.io | rosu-replay |
| lib.rs | rosu-replay |
| version | 0.2.1 |
| created_at | 2025-08-31 17:07:53.279918+00 |
| updated_at | 2025-09-14 21:43:47.921396+00 |
| description | A Rust library for parsing and writing osu! replay files (.osr format), ported from the Python osrparse library |
| homepage | |
| repository | https://github.com/Glubus/rosu-replay |
| max_upload_size | |
| id | 1818703 |
| size | 130,967 |
A high-performance Rust library for parsing and writing osu! replay files (.osr format), with WebAssembly support.
This library is a faithful port of the Python osrparse library, providing the same functionality for parsing and manipulating osu! replay files in Rust with improved performance, memory safety, and additional features.
liblzmaAdd this to your Cargo.toml:
[dependencies]
rosu-replay = "0.1"
For WASM usage, enable the wasm feature:
[dependencies]
rosu-replay = { version = "0.1", features = ["wasm"] }
Then compile with:
wasm-pack build --features wasm --target web
use rosu_replay::Replay;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Parse a replay file
let replay = Replay::from_path("path/to/replay.osr")?;
// Access basic information
println!("Player: {}", replay.username);
println!("Score: {}", replay.score);
println!("Max Combo: {}", replay.max_combo);
println!("Game Mode: {:?}", replay.mode);
println!("Mods: {:?}", replay.mods);
// Access hit statistics
println!("300s: {}, 100s: {}, 50s: {}, Misses: {}",
replay.count_300, replay.count_100, replay.count_50, replay.count_miss);
// Check if it's a perfect play
println!("Perfect: {}", replay.count_miss == 0);
Ok(())
}
use rosu_replay::{Replay, ReplayEvent};
let replay = Replay::from_path("replay.osr")?;
// Iterate through replay events
for (i, event) in replay.replay_data.iter().enumerate().take(10) {
match event {
ReplayEvent::Osu(osu_event) => {
println!("Frame {}: Cursor at ({:.1}, {:.1}) at +{}ms, keys: {}",
i, osu_event.x, osu_event.y, osu_event.time_delta, osu_event.keys.value());
}
ReplayEvent::Taiko(taiko_event) => {
println!("Frame {}: Taiko input at +{}ms, keys: {}",
i, taiko_event.time_delta, taiko_event.keys.value());
}
ReplayEvent::Catch(catch_event) => {
println!("Frame {}: Catch at x={:.1}, +{}ms, dashing: {}",
i, catch_event.x, catch_event.time_delta, catch_event.dashing);
}
ReplayEvent::Mania(mania_event) => {
println!("Frame {}: Mania keys {} at +{}ms",
i, mania_event.keys.value(), mania_event.time_delta);
}
}
}
use rosu_replay::{Replay, Packer};
let mut replay = Replay::from_path("input.osr")?;
// Modify replay data
replay.username = "Modified Player".to_string();
replay.score = 1000000;
// Write compressed (default)
replay.write_path("modified_replay.osr")?;
// Write uncompressed for faster loading
replay.write_path_uncompressed("uncompressed_replay.osr")?;
// Custom compression settings
let custom_packer = Packer::new().with_preset(9); // Maximum compression
let bytes = replay.pack_with(&custom_packer)?;
std::fs::write("custom_compressed.osr", bytes)?;
use rosu_replay::{parse_replay_data, GameMode};
// Parse replay data from osu! API v1
let api_data = b"base64_encoded_replay_data_from_api";
let events = parse_replay_data(api_data, false, false, GameMode::Std)?;
for event in events.iter().take(5) {
if let rosu_replay::ReplayEvent::Osu(osu_event) = event {
println!("API Event: ({:.1}, {:.1}) +{}ms",
osu_event.x, osu_event.y, osu_event.time_delta);
}
}
import init, { WasmReplay, WasmGameMode, parse_replay_data_wasm } from './pkg/rosu_replay.js';
async function parseReplay() {
await init();
// Load replay file (from file input, fetch, etc.)
const replayBytes = new Uint8Array(await file.arrayBuffer());
// Parse replay
const replay = new WasmReplay(replayBytes);
console.log(`Player: ${replay.username}`);
console.log(`Score: ${replay.score}`);
console.log(`Mode: ${replay.mode}`);
console.log(`Events: ${replay.event_count}`);
// Pack back to bytes
const packedBytes = replay.pack();
}
const { WasmReplay, parse_replay_data_wasm, version } = require('./pkg/rosu_replay.js');
const fs = require('fs');
// Read replay file
const replayData = fs.readFileSync('replay.osr');
const replay = new WasmReplay(replayData);
console.log(`Parsed with rosu-replay v${version()}`);
console.log(`Player: ${replay.username}, Score: ${replay.score}`);
This library supports all osu! game modes with mode-specific event data:
| Mode | Enum | Event Type | Data Fields |
|---|---|---|---|
| osu!standard | GameMode::Std |
ReplayEventOsu |
x, y coordinates + key states |
| osu!taiko | GameMode::Taiko |
ReplayEventTaiko |
drum position + hit types |
| osu!catch | GameMode::Catch |
ReplayEventCatch |
horizontal position + dash state |
| osu!mania | GameMode::Mania |
ReplayEventMania |
multi-lane key states |
The .osr format is a binary format used by osu! to store replay data. This library handles:
liblzma for optimal performance)use rosu_replay::{Replay, Packer};
let replay = Replay::from_path("input.osr")?;
// Fastest compression (level 1)
let fast_packer = Packer::new().with_preset(1);
let fast_bytes = replay.pack_with(&fast_packer)?;
// Maximum compression (level 9)
let max_packer = Packer::new().with_preset(9);
let small_bytes = replay.pack_with(&max_packer)?;
// Default compression (level 6) - good balance
let default_bytes = replay.pack()?;
use rosu_replay::{Replay, ReplayError};
match Replay::from_path("maybe_invalid.osr") {
Ok(replay) => println!("Loaded replay for {}", replay.username),
Err(ReplayError::Io(e)) => println!("File error: {}", e),
Err(ReplayError::Parse(e)) => println!("Parse error: {}", e),
Err(ReplayError::Lzma(e)) => println!("Compression error: {}", e),
Err(ReplayError::Utf8(e)) => println!("Text encoding error: {}", e),
}
// For batch processing, reuse the same Packer
let packer = Packer::new().with_preset(6);
for replay_path in replay_files {
let replay = Replay::from_path(replay_path)?;
let bytes = replay.pack_with(&packer)?; // Faster than creating new Packer each time
// Process bytes...
}
// Use uncompressed format for faster repeated access
let replay = Replay::from_path("replay.osr")?;
let uncompressed_bytes = replay.pack_uncompressed()?; // Faster to parse later
rosu-replay is designed for high performance:
liblzma (faster than previous lzma-rs)Benchmarks on a typical replay file:
Check out the examples/ directory for more comprehensive usage:
# Run the basic example
cargo run --example example_1
# Generate documentation with examples
cargo doc --open
# Run tests including WASM features
cargo test --features wasm
If you're upgrading from an earlier version:
liblzma instead of lzma-rswasm featureContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
# Run all tests
cargo test
# Run with WASM features
cargo test --features wasm
# Build documentation
cargo doc --open
# Format code
cargo fmt
# Run linter
cargo clippy
This project is licensed under the MIT License - see the LICENSE file for details.
osrparse Python libraryliblzma, wasm-bindgen, and chronoMade with โค๏ธ for the osu! community