one-rust/one-rust/src/bin/onear.rs

226 lines
6.2 KiB
Rust

#[macro_use]
extern crate argh;
extern crate binread;
extern crate one_rust;
use one_rust::prelude::*;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::str::FromStr;
/// Unpack .one archives from NiGHTS: Journey of Dreams
#[derive(FromArgs)]
struct Args {
#[argh(subcommand)]
action: Action,
}
#[derive(FromArgs)]
#[argh(subcommand)]
enum Action {
DescribeFile(DescribeFile),
ExtractFile(ExtractFile),
CreateFile(CreateFile),
CreateJodFile(CreateJodFile),
CreateSsrFile(CreateSsrFile),
CreateShadFile(CreateShadFile),
CreateHerFile(CreateHerFile),
}
enum GameType {
/// NiGHTS: Journey of Dreams
Dreams,
/// Sonic and the Secret Rings
Rings,
/// Shadow the Hedgehog
Shadow,
/// Sonic Heroes
Heroes,
}
impl FromStr for GameType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.chars().next() {
None => Err("no GameType found".to_string()),
Some('D') | Some('d') => Ok(GameType::Dreams),
Some('R') | Some('r') => Ok(GameType::Rings),
Some('S') | Some('s') => Ok(GameType::Shadow),
Some('H') | Some('h') => Ok(GameType::Heroes),
Some(_) => Err(format!("Unsupported game: {}", s)),
}
}
}
/// Unpack and decompress the files inside a .one archive.
#[derive(FromArgs)]
#[argh(subcommand, name = "xf")]
struct ExtractFile {
/// path to the .one file
#[argh(positional)]
file: PathBuf,
}
/// Describe the metadata of a .one archive.
#[derive(FromArgs)]
#[argh(subcommand, name = "tf")]
struct DescribeFile {
/// path to the .one file
#[argh(positional)]
file: PathBuf,
}
/// Create a .one archive from the given files.
#[derive(FromArgs)]
#[argh(subcommand, name = "c")]
struct CreateFile {
/// (D|R|S|H) game of the .one file to create
#[argh(option, short = 'g')]
game: GameType,
/// path of the .one file to create
#[argh(option, short = 'f')]
file: PathBuf,
/// paths of the files to pack into the archive
#[argh(positional)]
contents: Vec<PathBuf>,
}
// sugar:
/// Create a NiGHTS: Journey of _D_reams .one archive.
#[derive(FromArgs)]
#[argh(subcommand, name = "cDf")]
struct CreateJodFile {
/// path of the .one file to create
#[argh(positional)]
file: PathBuf,
/// paths of the files to pack into the archive
#[argh(positional)]
contents: Vec<PathBuf>,
}
/// Create a Sonic and the Secret _R_ings .one archive.
#[derive(FromArgs)]
#[argh(subcommand, name = "cRf")]
struct CreateSsrFile {
/// path of the .one file to create
#[argh(positional)]
file: PathBuf,
/// paths of the files to pack into the archive
#[argh(positional)]
contents: Vec<PathBuf>,
}
/// Create a _S_hadow the Hedgehog .one archive.
#[derive(FromArgs)]
#[argh(subcommand, name = "cSf")]
struct CreateShadFile {
/// path of the .one file to create
#[argh(positional)]
file: PathBuf,
/// paths of the files to pack into the archive
#[argh(positional)]
contents: Vec<PathBuf>,
}
/// Create a Sonic _H_eroes .one archive.
#[derive(FromArgs)]
#[argh(subcommand, name = "cHf")]
struct CreateHerFile {
/// path of the .one file to create
#[argh(positional)]
file: PathBuf,
/// paths of the files to pack into the archive
#[argh(positional)]
contents: Vec<PathBuf>,
}
fn create_one(subcmd: CreateFile) -> Result<(), Box<dyn Error>> {
if subcmd.file.exists() {
return Err(format!("{:?} already exists, refusing to overwrite", subcmd.file).into());
}
let mut contents = Vec::new();
for path in subcmd.contents {
let mut data = Vec::new();
File::open(&path)?.read_to_end(&mut data)?;
contents.push((path, data));
}
let archive: Box<dyn OneArchive> = match subcmd.game {
GameType::Dreams => Box::new(JodOne::pack(contents)),
GameType::Rings => Box::new(SsrOne::pack(contents)),
GameType::Shadow => Box::new(ShadOne::pack(contents)),
GameType::Heroes => Box::new(HerOne::pack(contents)),
};
let mut outfile = File::create(&subcmd.file)?;
archive.write(&mut outfile)?;
Ok(())
}
fn main() -> Result<(), Box<dyn Error>> {
let args: Args = argh::from_env();
match args.action {
Action::ExtractFile(subcmd) => {
let infile = File::open(&subcmd.file)?;
let archive = read_one_archive(infile)?;
let entries = archive.unpack();
let dir = PathBuf::from(format!("{}.d", subcmd.file.to_string_lossy()));
for (name, data) in entries {
let full_path = dir.join(name);
if let Some(path) = full_path.parent() {
std::fs::create_dir_all(path)?;
}
File::create(full_path)?.write_all(&data)?;
}
Ok(())
}
Action::DescribeFile(subcmd) => {
let infile = File::open(&subcmd.file)?;
let archive = read_one_archive(infile)?;
println!("{:?}", archive);
for entry in archive.entries() {
println!("{:?}", entry);
}
Ok(())
}
Action::CreateFile(subcmd) => create_one(subcmd),
Action::CreateJodFile(subcmd) => {
let subcmd = CreateFile {
game: GameType::Dreams,
file: subcmd.file,
contents: subcmd.contents,
};
create_one(subcmd)
}
Action::CreateSsrFile(subcmd) => {
let subcmd = CreateFile {
game: GameType::Rings,
file: subcmd.file,
contents: subcmd.contents,
};
create_one(subcmd)
}
Action::CreateShadFile(subcmd) => {
let subcmd = CreateFile {
game: GameType::Shadow,
file: subcmd.file,
contents: subcmd.contents,
};
create_one(subcmd)
}
Action::CreateHerFile(subcmd) => {
let subcmd = CreateFile {
game: GameType::Heroes,
file: subcmd.file,
contents: subcmd.contents,
};
create_one(subcmd)
}
}
}