From b49425751e51b458fdecd381bc9b8a06832da33d Mon Sep 17 00:00:00 2001 From: Simon Bruder Date: Tue, 30 Jun 2020 09:37:27 +0200 Subject: [PATCH] Add musicdb support to ddr2osu --- README.md | 6 +++++- src/converter/ddr2osu.rs | 44 ++++++++++++++++++++++++++++------------ src/ddr/musicdb.rs | 10 +++++++++ src/ddr/ssq.rs | 20 ++++++++++++++++++ src/main.rs | 39 ++++++++++++++++++++++++++++++----- 5 files changed, 100 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 681ffe7..1be6b38 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,11 @@ This converts DDR step charts (.ssq files) and the corresponding audio (from Basic usage: - brd ddr2osu -s file.ssq -x file.xwb -o file.osz --title "Song Title" --artist "Song Artist" +```shell +brd ddr2osu -s file.ssq -x file.xwb -o file.osz --title "Song Title" --artist "Song Artist" +# use musicdb from DDR A +brd ddr2osu -s file.ssq -x file.xwb -o file.osz -m startup.arc +``` To learn more about supported options run `brd ddr2osu --help` diff --git a/src/converter/ddr2osu.rs b/src/converter/ddr2osu.rs index 46f02f3..88d07a7 100644 --- a/src/converter/ddr2osu.rs +++ b/src/converter/ddr2osu.rs @@ -8,7 +8,7 @@ use log::{debug, info, trace, warn}; use crate::ddr::ssq; use crate::osu::beatmap; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct ConfigRange(f32, f32); impl ConfigRange { @@ -35,7 +35,7 @@ impl FromStr for ConfigRange { } } -#[derive(Debug, Clap)] +#[derive(Debug, Clap, Clone)] pub struct Config { #[clap(skip = "audio.wav")] pub audio_filename: String, @@ -43,7 +43,7 @@ pub struct Config { long = "no-stops", about = "Disable stops", parse(from_flag = std::ops::Not::not), - display_order = 5 + display_order = 3 )] pub stops: bool, #[clap( @@ -51,7 +51,7 @@ pub struct Config { long, default_value = "step", about = "What to do with shocks", - display_order = 5 + display_order = 3 )] pub shock_action: ShockAction, #[clap( @@ -70,19 +70,21 @@ pub struct Config { pub metadata: ConfigMetadata, } -#[derive(Clap, Debug)] +#[derive(Clap, Debug, Clone)] pub struct ConfigMetadata { - #[clap(long, about = "Song title to use in beatmap", display_order = 6)] - pub title: String, - #[clap(long, about = "Artist name to use in beatmap", display_order = 6)] - pub artist: String, + #[clap(long, about = "Song title to use in beatmap", display_order = 4)] + pub title: Option, + #[clap(long, about = "Artist name to use in beatmap", display_order = 4)] + pub artist: Option, #[clap( long, default_value = "Dance Dance Revolution", about = "Source to use in beatmap", - display_order = 6 + display_order = 4 )] pub source: String, + #[clap(skip)] + pub levels: Option>, } impl fmt::Display for Config { @@ -336,10 +338,26 @@ impl ConvertedChart { }, editor: beatmap::Editor {}, metadata: beatmap::Metadata { - title: config.metadata.title.clone(), - artist: config.metadata.artist.clone(), + title: config + .metadata + .title + .as_ref() + .unwrap_or(&"unknown title".to_string()) + .clone(), + artist: config + .metadata + .artist + .as_ref() + .unwrap_or(&"unknown artist".to_string()) + .clone(), creator: format!("{}", config), - version: format!("{}", self.difficulty), + version: match &config.metadata.levels { + Some(levels) => { + let level = self.difficulty.to_level(levels); + format!("{} (Lv. {})", self.difficulty, level) + } + None => format!("{}", self.difficulty), + }, source: config.metadata.source.clone(), tags: vec![], }, diff --git a/src/ddr/musicdb.rs b/src/ddr/musicdb.rs index 4253d7c..1cc408f 100644 --- a/src/ddr/musicdb.rs +++ b/src/ddr/musicdb.rs @@ -92,4 +92,14 @@ impl MusicDB { Self::parse(&String::from_utf8(musicdb_data.to_vec())?).map_err(|err| err.into()) } + + pub fn get_entry_from_basename(&self, basename: &str) -> Option<&Entry> { + for entry in &self.music { + if entry.basename == basename { + return Some(entry); + } + } + + None + } } diff --git a/src/ddr/ssq.rs b/src/ddr/ssq.rs index fc0ba59..a035e95 100644 --- a/src/ddr/ssq.rs +++ b/src/ddr/ssq.rs @@ -351,6 +351,26 @@ impl Into for Difficulty { } } +/// Gets level for difficulty from [`ddr::musicdb::Entry.diff_lv`]. +/// +/// [`ddr::musicdb::Entry.diff_lv`]: ../musicdb/struct.Entry.html#structfield.diff_lv +impl Difficulty { + pub fn to_level(&self, levels: &[u8]) -> u8 { + let base = match self.difficulty { + 1 => 1, + 2 => 2, + 3 => 4, + 4 => 0, + 6 => 3, + _ => 4, + }; + + let index: usize = (base + (self.players - 1) * 5).into(); + + levels[index] + } +} + impl fmt::Display for Difficulty { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let players = match self.players { diff --git a/src/main.rs b/src/main.rs index 9dff78a..d42520a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,7 +45,7 @@ enum SubCommand { about = "Converts DDR step charts to osu!mania beatmaps", display_order = 1 )] - DDR2osu(DDR2osu), + DDR2osu(Box), } #[derive(Clap)] @@ -89,7 +89,7 @@ struct DDR2osu { long = "xwb", name = "file.xwb", about = "XAC3 wave bank file", - display_order = 2 + display_order = 1 )] xwb_file: PathBuf, #[clap( @@ -97,14 +97,22 @@ struct DDR2osu { long = "out", name = "file.osz", about = "osu! beatmap archive", - display_order = 3 + display_order = 1 )] out_file: PathBuf, + #[clap( + short = "m", + long = "musicdb", + name = "musicdb.xml|startup.arc", + about = "musicdb.xml or startup.arc for metadata", + display_order = 1 + )] + musicdb_file: Option, #[clap( short = "n", name = "sound name", about = "Sound in wave bank, otherwise inferred from SSQ filename", - display_order = 4 + display_order = 2 )] sound_name: Option, #[clap(flatten)] @@ -271,8 +279,29 @@ fn main() -> Result<()> { .with_context(|| format!("failed to read SSQ file {}", &opts.ssq_file.display()))?; let ssq = SSQ::parse(&ssq_data).context("failed to parse SSQ file")?; + let mut convert_options = opts.convert.clone(); + + if let Some(musicdb_file) = &opts.musicdb_file { + debug!("Reading metadata from {}", musicdb_file.display()); + let musicdb = read_musicdb(&musicdb_file)?; + let musicdb_entry = musicdb + .get_entry_from_basename(sound_name) + .ok_or_else(|| anyhow!("Entry not found in musicdb"))?; + if convert_options.metadata.title.is_none() { + info!("Using title from musicdb: “{}”", musicdb_entry.title); + convert_options.metadata.title = Some(musicdb_entry.title.clone()); + } + if convert_options.metadata.artist.is_none() { + info!("Using artist from musicdb: “{}”", musicdb_entry.artist); + convert_options.metadata.artist = Some(musicdb_entry.artist.clone()); + } + convert_options.metadata.levels = Some(musicdb_entry.diff_lv.clone()); + } else if convert_options.metadata.title.is_none() { + convert_options.metadata.title = Some(sound_name.to_string()); + } + let beatmaps = ssq - .to_beatmaps(&opts.convert) + .to_beatmaps(&convert_options) .context("failed to convert DDR step chart to osu!mania beatmap")?; let xwb_data = fs::read(&opts.xwb_file).with_context(|| {