use crate::codec::Encoder;
use crate::error::RoxResult;
use crate::model::RoxChart;
use super::types::{FnfChart, FnfNote, FnfSection, FnfSong};
pub struct FnfEncoder;
impl Encoder for FnfEncoder {
fn encode(chart: &RoxChart) -> RoxResult<Vec<u8>> {
let base_bpm = chart
.timing_points
.iter()
.find(|tp| !tp.is_inherited)
.map_or(120.0, |tp| tp.bpm);
let is_8k = chart.key_count >= 8;
let mut section_notes: Vec<FnfNote> = Vec::new();
for note in &chart.notes {
#[allow(clippy::cast_precision_loss)]
let time_ms = note.time_us as f64 / 1000.0;
let lane = if is_8k {
note.column
} else {
note.column
};
let fnf_note = match ¬e.note_type {
crate::model::NoteType::Hold { duration_us } => {
#[allow(clippy::cast_precision_loss)]
let duration_ms = *duration_us as f64 / 1000.0;
FnfNote::hold(time_ms, lane, duration_ms)
}
_ => FnfNote::tap(time_ms, lane),
};
section_notes.push(fnf_note);
}
let section = FnfSection {
section_notes,
length_in_steps: 160_000, must_hit_section: !is_8k, change_bpm: true,
bpm: base_bpm,
type_of_section: 0,
};
let fnf = FnfChart {
song: FnfSong {
song: chart.metadata.title.clone(),
bpm: base_bpm,
speed: 1.5,
player1: "bf".to_string(),
player2: chart.metadata.creator.clone(),
needs_voices: false,
valid_score: true,
notes: vec![section],
sections: 1,
section_lengths: Vec::new(),
},
};
let json = serde_json::to_string_pretty(&fnf)
.map_err(|e| crate::error::RoxError::InvalidFormat(format!("JSON error: {e}")))?;
Ok(json.into_bytes())
}
}