diff --git a/README.md b/README.md index 375df97..5e0bede 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ Usage The project contains 2 artefacts: -- `librir`, a library containing all the parsing/unpacking features implemented. -- `ironring`, an executable to use main lib features from the CLI. +- `ironring`, a library with all the parsing/unpacking features implemented. +- `rir`, an executable to use main lib features from the CLI. The goal is to make the lib compatible with FFI tools such as Python's ctypes, to ship a dynamic lib accessible for any language to easily script tasks and @@ -26,10 +26,10 @@ ideas, but we're not there yet. Ironring usage: ``` -Iron Ring +Rusted Iron Ring USAGE: - ironring [SUBCOMMAND] + rir [SUBCOMMAND] FLAGS: -h, --help Prints help information @@ -64,6 +64,12 @@ Repacking is not supported, maybe one day. It is not that useful when using [yabber]: https://github.com/JKAnderson/Yabber [sieglib]: https://github.com/Dece/DarkSoulsDev/tree/master/Programs/SiegLib +There is a demo Python binding for some `name_hashes` features in the +`bindings/python` dir, that uses [PyO3][pyo3] and thus requires nightly rustc to +build. + +[pyo3]: https://pyo3.rs/ + Credits diff --git a/src/bin/rir.rs b/src/bin/rir.rs index 2ea30af..1f2e01b 100644 --- a/src/bin/rir.rs +++ b/src/bin/rir.rs @@ -73,6 +73,11 @@ fn main() { .arg(Arg::with_name("overwrite") .help("Overwrite existing files") .short("f").long("force").takes_value(false).required(false))) + .subcommand(SubCommand::with_name("paramdef") + .about("TODO") + .arg(Arg::with_name("file") + .help("PARAMDEF file path") + .takes_value(true).required(true))) .get_matches(); process::exit(match matches.subcommand() { @@ -82,6 +87,7 @@ fn main() { ("dcx", Some(s)) => { cmd_dcx(s) } ("bnd", Some(s)) => { cmd_bnd(s) } ("bhf", Some(s)) => { cmd_bhf(s) } + ("paramdef", Some(s)) => { cmd_paramdef(s) } _ => { 0 } }) } @@ -191,3 +197,11 @@ fn cmd_bhf(args: &ArgMatches) -> i32 { _ => 0 } } + +fn cmd_paramdef(args: &ArgMatches) -> i32 { + let file_path: &str = args.value_of("file").unwrap(); + match unpackers::paramdef::load_paramdef_file(file_path) { + Ok(paramdef) => { unpackers::paramdef::print_paramdef(¶mdef); 0 } + Err(e) => { eprintln!("Failed to load PARAMDEF: {:?}", e); 1 } + } +} diff --git a/src/lib.rs b/src/lib.rs index 2480aca..6b633b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub mod parsers { pub mod bnd; pub mod common; pub mod dcx; + pub mod paramdef; } pub mod unpackers { pub mod bhd; @@ -14,6 +15,7 @@ pub mod unpackers { pub mod bnd; pub mod dcx; pub mod errors; + pub mod paramdef; } pub mod utils { pub mod bin; diff --git a/src/parsers/paramdef.rs b/src/parsers/paramdef.rs new file mode 100644 index 0000000..404576f --- /dev/null +++ b/src/parsers/paramdef.rs @@ -0,0 +1,56 @@ +use std::str; + +use nom::IResult; +use nom::bytes::complete::{tag, take}; +use nom::multi::count; +use nom::number::complete::*; +use nom::sequence::tuple; + +use crate::parsers::common::{sjis_to_string, take_cstring}; + +#[derive(Debug)] +pub struct ParamdefHeader { + pub file_size: u32, + pub header_size: u16, + pub data_version: u16, + pub num_entries: u16, + pub entry_size: u16, + pub param_name: Vec, + pub endianness: u8, + pub unicode: u8, + pub format_version: u16, +} + +fn parse_header(i: &[u8]) -> IResult<&[u8], ParamdefHeader> { + let p_u32 = if i[0x2C] == 0xFF { be_u32 } else { le_u32 }; + let p_u16 = if i[0x2C] == 0xFF { be_u16 } else { le_u16 }; + let (i, (file_size, header_size, data_version, num_entries, entry_size)) = + tuple((p_u32, p_u16, p_u16, p_u16, p_u16))(i)?; + let (_, param_name) = take_cstring(&i[..0x20])?; + let (i, (endianness, unicode, format_version)) = + tuple((le_u8, le_u8, p_u16))(&i[0x20..])?; + Ok(( + i, + ParamdefHeader { + file_size, + header_size, + data_version, + num_entries, + entry_size, + param_name: param_name.to_vec(), + endianness, + unicode, + format_version, + } + )) +} + +#[derive(Debug)] +pub struct Paramdef { + pub header: ParamdefHeader, +} + +pub fn parse(i: &[u8]) -> IResult<&[u8], Paramdef> { + let (i, header) = parse_header(i)?; + Ok((i, Paramdef { header })) +} diff --git a/src/unpackers/dcx.rs b/src/unpackers/dcx.rs index f4bea38..5fa313d 100644 --- a/src/unpackers/dcx.rs +++ b/src/unpackers/dcx.rs @@ -81,15 +81,13 @@ pub fn get_decompressed_path(dcx_path: &str, output_path: Option<&str>) -> Optio if path::Path::new(&output_path).is_dir() { output_path_valid = false; if let Some(file_pb) = utils_fs::strip_extension(&path::PathBuf::from(&dcx_path)) { - if let Some(file_name) = file_pb.file_name() { - if let Some(file_name_str) = file_name.to_str() { - let mut out_pb = path::PathBuf::from(&output_path); - out_pb.push(file_name_str); - if let Some(s) = out_pb.as_path().to_str() { - output_path.clear(); - output_path.push_str(s); - output_path_valid = true; - } + if let Some(file_name) = file_pb.file_name().and_then(|s| s.to_str()) { + let mut out_pb = path::PathBuf::from(&output_path); + out_pb.push(file_name); + if let Some(s) = out_pb.as_path().to_str() { + output_path.clear(); + output_path.push_str(s); + output_path_valid = true; } } } diff --git a/src/unpackers/paramdef.rs b/src/unpackers/paramdef.rs new file mode 100644 index 0000000..33bf184 --- /dev/null +++ b/src/unpackers/paramdef.rs @@ -0,0 +1,24 @@ +use std::path; + +use nom::Err::{Error as NomError, Failure as NomFailure}; + +use crate::parsers::paramdef; +use crate::unpackers::errors::UnpackError; +use crate::utils::fs as utils_fs; + +pub fn load_paramdef_file(paramdef_path: &str) -> Result { + let paramdef_data = utils_fs::open_file_to_vec(path::Path::new(paramdef_path))?; + Ok(load_paramdef(¶mdef_data)?) +} + +pub fn load_paramdef(paramdef_data: &[u8]) -> Result { + match paramdef::parse(paramdef_data) { + Ok((_, result)) => Ok(result), + Err(NomError(e)) | Err(NomFailure(e)) => Err(UnpackError::parsing_err("PARAMDEF", e.1)), + e => Err(UnpackError::Unknown(format!("Unknown error: {:?}", e))), + } +} + +pub fn print_paramdef(paramdef: ¶mdef::Paramdef) { + println!("{:?}", paramdef); +}