use crate::codec::Decoder;
use crate::error::RoxResult;
use crate::model::{Metadata, Note, RoxChart, TimingPoint};
use super::types::{AlternationState, ColumnLayout};
use crate::codec::formats::taiko::parser;
pub struct TaikoDecoder;
impl TaikoDecoder {
pub fn decode_with_layout(data: &[u8], layout: ColumnLayout) -> RoxResult<RoxChart> {
let mut state = AlternationState::new(layout);
Self::decode_with_state(data, &mut state)
}
pub fn decode_with_state(data: &[u8], state: &mut AlternationState) -> RoxResult<RoxChart> {
let beatmap = parser::parse(data)?;
let mut chart = RoxChart::new(4);
chart.metadata = Metadata {
#[allow(clippy::cast_sign_loss)]
chart_id: beatmap.metadata.beatmap_id.map(|id| id as u64),
#[allow(clippy::cast_sign_loss)]
chartset_id: beatmap.metadata.beatmap_set_id.map(|id| id as u64),
key_count: 4,
title: beatmap
.metadata
.title_unicode
.clone()
.unwrap_or_else(|| beatmap.metadata.title.clone())
.into(),
artist: beatmap
.metadata
.artist_unicode
.clone()
.unwrap_or_else(|| beatmap.metadata.artist.clone())
.into(),
creator: beatmap.metadata.creator.clone().into(),
difficulty_name: beatmap.metadata.version.clone().into(),
difficulty_value: Some(beatmap.difficulty.overall_difficulty),
audio_file: beatmap.general.audio_filename.clone().into(),
background_file: beatmap.background.clone().map(|s| s.into()),
audio_offset_us: i64::from(beatmap.general.audio_lead_in) * 1000,
preview_time_us: if beatmap.general.preview_time > 0 {
i64::from(beatmap.general.preview_time) * 1000
} else {
0
},
source: beatmap.metadata.source.clone().map(|s| s.into()),
tags: beatmap
.metadata
.tags
.iter()
.map(|s| s.clone().into())
.collect(),
..Default::default()
};
for tp in &beatmap.timing_points {
#[allow(clippy::cast_possible_truncation)]
let time_us = (tp.time * 1000.0) as i64;
if tp.uninherited {
if let Some(bpm) = tp.bpm() {
let mut timing = TimingPoint::bpm(time_us, bpm);
timing.signature = tp.meter;
chart.timing_points.push(timing);
}
} else {
}
}
if chart.timing_points.is_empty() {
chart.timing_points.push(TimingPoint::bpm(0, 120.0));
}
for ho in &beatmap.hit_objects {
if ho.is_spinner() {
continue;
}
#[allow(clippy::cast_possible_truncation)]
let time_us = (ho.time_ms * 1000.0) as i64;
let is_big = ho.hitsound.is_big();
let columns = if ho.hitsound.is_kat() {
state.next_kat_columns(is_big)
} else {
state.next_don_columns(is_big)
};
for col in columns {
chart.notes.push(Note::tap(time_us, col));
}
}
chart.notes.sort_by_key(|n| n.time_us);
Ok(chart)
}
}
impl Decoder for TaikoDecoder {
fn decode(data: &[u8]) -> RoxResult<RoxChart> {
let mut state = AlternationState::default();
Self::decode_with_state(data, &mut state)
}
}