use std::path::Path;
use crate::error::{RoxError, RoxResult};
use crate::model::RoxChart;
use super::super::Decoder;
#[cfg(feature = "compression")]
use super::super::formats::RoxCodec;
use super::super::formats::{
FnfDecoder, JroxDecoder, OsuDecoder, QuaDecoder, SmDecoder, TaikoDecoder, YroxDecoder,
};
use super::types::InputFormat;
pub fn auto_decode(path: impl AsRef<Path>) -> RoxResult<RoxChart> {
let path = path.as_ref();
let format = InputFormat::from_path(path)?;
let file = std::fs::File::open(path)?;
let mmap = unsafe { memmap2::Mmap::map(&file)? };
let data = &*mmap;
match format {
#[cfg(feature = "compression")]
InputFormat::Rox => RoxCodec::decode(data),
InputFormat::Jrox => JroxDecoder::decode(data),
InputFormat::Yrox => YroxDecoder::decode(data),
InputFormat::Osu | InputFormat::Taiko => decode_osu_by_mode(data),
InputFormat::Sm => SmDecoder::decode(data),
InputFormat::Qua => QuaDecoder::decode(data),
InputFormat::Fnf => FnfDecoder::decode(data),
}
}
fn decode_osu_by_mode(data: &[u8]) -> RoxResult<RoxChart> {
match detect_osu_mode(data) {
1 => TaikoDecoder::decode(data),
3 => OsuDecoder::decode(data),
mode => Err(RoxError::UnsupportedFormat(format!(
"osu! mode {mode} is not supported (only taiko=1 and mania=3)"
))),
}
}
pub(crate) fn detect_osu_mode(data: &[u8]) -> u8 {
let Ok(content) = std::str::from_utf8(data) else {
return 3; };
for line in content.lines() {
let line = line.trim();
if let Some(value) = line.strip_prefix("Mode:")
&& let Ok(mode) = value.trim().parse::<u8>()
{
return mode;
}
if line == "[Metadata] section" {
break;
}
}
3 }
pub fn decode_with_format(data: &[u8], format: InputFormat) -> RoxResult<RoxChart> {
match format {
#[cfg(feature = "compression")]
InputFormat::Rox => <RoxCodec as Decoder>::decode(data),
InputFormat::Jrox => <JroxDecoder as Decoder>::decode(data),
InputFormat::Yrox => <YroxDecoder as Decoder>::decode(data),
InputFormat::Osu => <OsuDecoder as Decoder>::decode(data),
InputFormat::Taiko => <TaikoDecoder as Decoder>::decode(data),
InputFormat::Sm => <SmDecoder as Decoder>::decode(data),
InputFormat::Qua => <QuaDecoder as Decoder>::decode(data),
InputFormat::Fnf => <FnfDecoder as Decoder>::decode(data),
}
}
pub fn from_string(data: &str) -> RoxResult<RoxChart> {
let bytes = data.as_bytes();
match decode_osu_by_mode(bytes) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as osu: {}", e),
}
match SmDecoder::decode(bytes) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as StepMania: {}", e),
}
match QuaDecoder::decode(bytes) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as Quaver: {}", e),
}
match FnfDecoder::decode(bytes) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as FNF: {}", e),
}
match JroxDecoder::decode(bytes) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as JROX: {}", e),
}
match YroxDecoder::decode(bytes) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as YROX: {}", e),
}
Err(RoxError::InvalidFormat(
"Failed to decode chart: no format decoder succeeded".into(),
))
}
pub fn from_bytes(data: &[u8]) -> RoxResult<RoxChart> {
#[cfg(feature = "compression")]
match RoxCodec::decode(data) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as ROX: {}", e),
}
match decode_osu_by_mode(data) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as osu: {}", e),
}
match SmDecoder::decode(data) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as StepMania: {}", e),
}
match QuaDecoder::decode(data) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as Quaver: {}", e),
}
match FnfDecoder::decode(data) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as FNF: {}", e),
}
match JroxDecoder::decode(data) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as JROX: {}", e),
}
match YroxDecoder::decode(data) {
Ok(chart) => return Ok(chart),
Err(e) => tracing::debug!("Failed to auto-decode as YROX: {}", e),
}
Err(RoxError::InvalidFormat(
"Failed to decode chart: no format decoder succeeded".into(),
))
}