This repository has been archived on 2024-01-28. You can view files and clone it, but cannot push or open issues or pull requests.
brd/src/main.rs

175 lines
4.9 KiB
Rust

use std::fs;
use std::path::PathBuf;
use anyhow::{anyhow, Result};
use clap::Clap;
use log::{debug, error, info, warn};
use brd::converter;
use brd::ddr::ssq::SSQ;
use brd::osu;
use brd::xact3::xwb::{Sound as XWBSound, WaveBank};
#[derive(Clap)]
#[clap()]
struct Opts {
#[clap(subcommand)]
subcmd: SubCommand,
}
#[derive(Clap)]
enum SubCommand {
#[clap(
name = "unxwb",
about = "Extracts sounds from XWB wave banks",
display_order = 1
)]
UnXWB(UnXWB),
#[clap(
about = "Converts DDR step charts to osu!mania beatmaps",
display_order = 1
)]
DDR2osu(DDR2osu),
}
#[derive(Clap)]
struct UnXWB {
#[clap(short, long, about = "List available sounds and exit")]
list_entries: bool,
#[clap(short = "e", long, about = "Only extract this entry")]
single_entry: Option<String>,
#[clap(name = "file")]
file: PathBuf,
}
#[derive(Clap)]
struct DDR2osu {
#[clap(
short = "s",
long = "ssq",
name = "file.ssq",
about = "DDR step chart file",
display_order = 1
)]
ssq_file: PathBuf,
#[clap(
short = "x",
long = "xwb",
name = "file.xwb",
about = "XAC3 wave bank file",
display_order = 2
)]
xwb_file: PathBuf,
#[clap(
short = "o",
long = "out",
name = "file.osz",
about = "osu! beatmap archive",
display_order = 3
)]
out_file: PathBuf,
#[clap(
short = "n",
name = "sound name",
about = "Sound in wave bank, otherwise inferred from SSQ filename",
display_order = 4
)]
sound_name: Option<String>,
#[clap(flatten)]
convert: converter::ddr2osu::Config,
}
fn error(message: String) -> Result<()> {
error!("{}", message);
Err(anyhow!(message))
}
fn get_basename(path: &PathBuf) -> Option<&str> {
match path.file_stem() {
Some(stem) => stem.to_str(),
None => None,
}
}
fn main() -> Result<()> {
pretty_env_logger::init();
let opts: Opts = Opts::parse();
match opts.subcmd {
SubCommand::UnXWB(opts) => {
let xwb_data = fs::read(&opts.file)?;
let wave_bank = WaveBank::parse(&xwb_data)?;
info!("Opened wave bank “{}” from {:?}", wave_bank.name, opts.file);
let entries = match opts.single_entry {
Some(name) => match wave_bank.sounds.get(&name) {
Some(_) => vec![name],
None => return error(format!("Entry {} not found in wave bank", name)),
},
None => wave_bank.sounds.keys().cloned().collect(),
};
for (name, sound) in wave_bank.sounds {
if entries.contains(&name) {
if opts.list_entries {
println!("{}", name);
continue;
}
info!("Extracting {}", name);
fs::write(format!("{}.wav", name), &sound.to_wav()?)?;
}
}
}
SubCommand::DDR2osu(opts) => {
let sound_name = &opts
.sound_name
.unwrap_or(match get_basename(&opts.ssq_file) {
Some(basename) => basename.to_string(),
None => return error(
"Could not extract chart id from file name. Please specify it manually."
.to_string(),
),
});
debug!(
"Converting {:?} and sound {} from {:?} to {:?}",
opts.ssq_file, sound_name, opts.xwb_file, opts.out_file
);
let ssq_data = fs::read(&opts.ssq_file)?;
let ssq = SSQ::parse(&ssq_data)?;
let convert_config = opts.convert;
let beatmaps = ssq.to_beatmaps(&convert_config)?;
let xwb_data = fs::read(&opts.xwb_file)?;
let wave_bank = WaveBank::parse(&xwb_data)?;
let audio_data = if wave_bank.sounds.contains_key(sound_name) {
wave_bank.sounds.get(sound_name).unwrap().to_wav()?
} else if wave_bank.sounds.len() == 2 {
warn!(
"Sound {} not found in wave bank, but it has two entries; assuming these are preview and full song",
sound_name
);
let mut sounds = wave_bank.sounds.values().collect::<Vec<&XWBSound>>();
sounds.sort_unstable_by(|a, b| b.size.cmp(&a.size));
sounds[0].to_wav()?
} else {
return error(format!(
"Could not find matching sound in wave bank (searched for {})",
sound_name,
));
};
let osz = osu::osz::Archive {
beatmaps,
assets: vec![("audio.wav", &audio_data)],
};
osz.write(&opts.out_file)?;
}
}
Ok(())
}