Add musicdb support to ddr2osu
This commit is contained in:
parent
dc02995496
commit
b49425751e
|
@ -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`
|
||||
|
||||
|
|
|
@ -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<String>,
|
||||
#[clap(long, about = "Artist name to use in beatmap", display_order = 4)]
|
||||
pub artist: Option<String>,
|
||||
#[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<Vec<u8>>,
|
||||
}
|
||||
|
||||
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![],
|
||||
},
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -351,6 +351,26 @@ impl Into<f32> 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 {
|
||||
|
|
39
src/main.rs
39
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<DDR2osu>),
|
||||
}
|
||||
|
||||
#[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<PathBuf>,
|
||||
#[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<String>,
|
||||
#[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(|| {
|
||||
|
|
Reference in a new issue