From 1d3dab75238e5f5bb4ccf462a77d2f4713a2a362 Mon Sep 17 00:00:00 2001 From: dece Date: Sun, 17 May 2020 02:46:16 +0200 Subject: [PATCH] param: WIP parsing --- src/bin/rir.rs | 17 +++++++ src/lib.rs | 2 + src/parsers/param.rs | 103 +++++++++++++++++++++++++++++++++++++++++ src/unpackers/param.rs | 24 ++++++++++ 4 files changed, 146 insertions(+) create mode 100644 src/parsers/param.rs create mode 100644 src/unpackers/param.rs diff --git a/src/bin/rir.rs b/src/bin/rir.rs index 0f4eff5..797cd64 100644 --- a/src/bin/rir.rs +++ b/src/bin/rir.rs @@ -78,6 +78,14 @@ fn main() { .arg(Arg::with_name("file") .help("PARAMDEF file path") .takes_value(true).required(true))) + .subcommand(SubCommand::with_name("param") + .about("TODO") + .arg(Arg::with_name("file") + .help("PARAM file path") + .takes_value(true).required(true)) + .arg(Arg::with_name("paramdef") + .help("PARAMDEF file path") + .short("d").long("def").takes_value(true).required(false))) .get_matches(); process::exit(match matches.subcommand() { @@ -88,6 +96,7 @@ fn main() { ("bnd", Some(s)) => { cmd_bnd(s) } ("bhf", Some(s)) => { cmd_bhf(s) } ("paramdef", Some(s)) => { cmd_paramdef(s) } + ("param", Some(s)) => { cmd_param(s) } _ => { 0 } }) } @@ -205,3 +214,11 @@ fn cmd_paramdef(args: &ArgMatches) -> i32 { Err(e) => { eprintln!("Failed to load PARAMDEF: {:?}", e); 1 } } } + +fn cmd_param(args: &ArgMatches) -> i32 { + let file_path: &str = args.value_of("file").unwrap(); + match unpackers::param::load_param_file(file_path) { + Ok(param) => { unpackers::param::print_param(¶m); 0 } + Err(e) => { eprintln!("Failed to load PARAM: {:?}", e); 1 } + } +} diff --git a/src/lib.rs b/src/lib.rs index 2397e09..a05af1e 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 param; pub mod paramdef; } pub mod unpackers { @@ -15,6 +16,7 @@ pub mod unpackers { pub mod bnd; pub mod dcx; pub mod errors; + pub mod param; pub mod paramdef; } pub mod utils { diff --git a/src/parsers/param.rs b/src/parsers/param.rs new file mode 100644 index 0000000..8bafa35 --- /dev/null +++ b/src/parsers/param.rs @@ -0,0 +1,103 @@ +use nom::IResult; +use nom::number::complete::*; +use nom::sequence::tuple; + +use crate::parsers::common::{sjis_to_string, take_cstring_from}; +use crate::utils::bin::has_flag; + +const FLAGS2D_UNK1: u8 = 0b00000001; +const FLAGS2D_32B_OFS_DATA: u8 = 0b00000010; +const FLAGS2D_64B_OFS_DATA: u8 = 0b00000100; +const FLAGS2D_OFS_STRING: u8 = 0b10000000; +const FLAGS2E_UNICODE_NAMES: u8 = 0b00000001; + +pub struct ParamHeader { + pub ofs_strings: u32, + pub ofs_data: u16, + pub unk06: u16, + pub paramdef_data_version: u16, + pub num_rows: u16, + pub param_type: String, + pub endianness: u8, + pub flags2D: u8, + pub flags2E: u8, + pub paramdef_format_version: u8, + pub ofs_data_long: Option, +} + +pub union ParamHeaderLongOfsData { + ofs32: u32, + ofs64: u64, +} + +impl ParamHeader { + pub fn use_be(&self) -> bool { use_be(self.endianness) } +} + +fn use_be(endianness: u8) -> bool { endianness == 0xFF } + +fn has_ofs_string_name(flags: u8) -> bool { has_flag(flags, FLAGS2D_OFS_STRING) } + +fn has_u32_ofs_data(flags: u8) -> bool { has_flag(flags, FLAGS2D_UNK1 & FLAGS2D_32B_OFS_DATA) } + +fn has_u64_ofs_data(flags: u8) -> bool { has_flag(flags, FLAGS2D_64B_OFS_DATA) } + +fn parse_header(i: &[u8]) -> IResult<&[u8], ParamHeader> { + let endianness = i[0x2C]; + let p_u16 = if use_be(endianness) { be_u16 } else { le_u16 }; + let p_u32 = if use_be(endianness) { be_u32 } else { le_u32 }; + let p_u64 = if use_be(endianness) { be_u64 } else { le_u64 }; + let flags2D = i[0x2D]; + let use_u32_ofs_data = has_u32_ofs_data(flags2D); + let use_u64_ofs_data = has_u64_ofs_data(flags2D); + + let (i, (ofs_strings, ofs_data, unk06, paramdef_data_version, num_rows)) = + tuple((p_u32, p_u16, p_u16, p_u16, p_u16))(i)?; + + let (i, param_type) = if has_ofs_string_name(flags2D) { + // Name is in the strings block, parse it later. + (&i[0x20..], String::new()) + } else { + take_cstring_from(i, 0x20).map(|(i, s)| (i, String::from_utf8_lossy(s).to_string()))? + }; + + let i = &i[0x2..]; // Skip endianness and flags2D. + let (i, (flags2E, paramdef_format_version)) = tuple((le_u8, le_u8))(i)?; + + let (i, ofs_data_long) = if use_u32_ofs_data { + let (_, o) = p_u32(i)?; + (&i[0x20..], Some(ParamHeaderLongOfsData { ofs32: o })) + } else if use_u64_ofs_data { + let (_, o) = p_u64(i)?; + (&i[0x20..], Some(ParamHeaderLongOfsData { ofs64: o })) + } else { + (i, None) + }; + + Ok(( + i, + ParamHeader { + ofs_strings, + ofs_data, + unk06, + paramdef_data_version, + num_rows, + param_type, + endianness, + flags2D, + flags2E, + paramdef_format_version, + ofs_data_long, + } + )) +} + +pub struct Param { + pub header: ParamHeader, +} + +pub fn parse(i: &[u8]) -> IResult<&[u8], Param> { + let full_file = i; + let (i, header) = parse_header(i)?; + Ok((i, Param { header })) +} diff --git a/src/unpackers/param.rs b/src/unpackers/param.rs new file mode 100644 index 0000000..5a96087 --- /dev/null +++ b/src/unpackers/param.rs @@ -0,0 +1,24 @@ +use std::path; + +use nom::Err::{Error as NomError, Failure as NomFailure}; + +use crate::parsers::param; +use crate::unpackers::errors::UnpackError; +use crate::utils::fs as utils_fs; + +pub fn load_param_file(param_path: &str) -> Result { + let param_data = utils_fs::open_file_to_vec(path::Path::new(param_path))?; + Ok(load_param(¶m_data)?) +} + +pub fn load_param(param_data: &[u8]) -> Result { + match param::parse(param_data) { + Ok((_, result)) => Ok(result), + Err(NomError(e)) | Err(NomFailure(e)) => Err(UnpackError::parsing_err("PARAM", e.1)), + Err(e) => Err(UnpackError::Unknown(format!("Unknown error: {:?}", e))), + } +} + +pub fn print_param(param: ¶m::Param) { + println!("{} -- {} rows", param.header.param_type, param.header.num_rows); +}