From be1d40ac7b094115f97ac8bc72b3a1c6501e4971 Mon Sep 17 00:00:00 2001 From: dece Date: Sat, 23 May 2020 00:16:10 +0200 Subject: [PATCH] param: complete printing --- src/bin/rir.rs | 34 ++++++++------- src/parsers/param.rs | 87 ++++++++++++++++++++++++++++++--------- src/parsers/paramdef.rs | 34 +++++++++++++++ src/unpackers/param.rs | 25 ++++------- src/unpackers/paramdef.rs | 28 +------------ 5 files changed, 131 insertions(+), 77 deletions(-) diff --git a/src/bin/rir.rs b/src/bin/rir.rs index df9f5dd..0295606 100644 --- a/src/bin/rir.rs +++ b/src/bin/rir.rs @@ -79,7 +79,7 @@ fn main() { .help("PARAMDEF file path") .takes_value(true).required(true))) .subcommand(SubCommand::with_name("param") - .about("TODO") + .about("Parse PARAM contents") .arg(Arg::with_name("file") .help("PARAM file path") .takes_value(true).required(true)) @@ -217,21 +217,25 @@ fn cmd_paramdef(args: &ArgMatches) -> i32 { fn cmd_param(args: &ArgMatches) -> i32 { let file_path: &str = args.value_of("file").unwrap(); + let paramdef_path: Option<&str> = args.value_of("paramdef"); - if let Some(paramdef_path) = args.value_of("paramdef") { - match unpackers::paramdef::load_paramdef_file(paramdef_path) { - Ok(paramdef) => { - match unpackers::param::load_param_file(file_path, Some(¶mdef)) { - Ok(param) => { unpackers::param::print_param_with_def(¶m, ¶mdef); 0 } - Err(e) => { eprintln!("Failed to load PARAM: {:?}", e); 1 } - } - } - Err(e) => { eprintln!("Failed to load PARAMDEF: {:?}", e); 1 } + let paramdef = if paramdef_path.is_some() { + match unpackers::paramdef::load_paramdef_file(paramdef_path.unwrap()) { + Ok(paramdef) => Some(paramdef), + Err(e) => { eprintln!("Failed to load PARAMDEF: {:?}", e); return 1 } } } else { - match unpackers::param::load_param_file(file_path, None) { - Ok(param) => { unpackers::param::print_param(¶m); 0 } - Err(e) => { eprintln!("Failed to load PARAM: {:?}", e); 1 } - } - } + None + }; + + let param = match unpackers::param::load_param_file(file_path, paramdef.as_ref()) { + Ok(param) => param, + Err(e) => { eprintln!("Failed to load PARAM: {:?}", e); return 1 } + }; + + match paramdef { + Some(paramdef) => unpackers::param::print_param_with_def(¶m, ¶mdef), + None => unpackers::param::print_param(¶m), + }; + 0 } diff --git a/src/parsers/param.rs b/src/parsers/param.rs index 492c4e7..9b9de19 100644 --- a/src/parsers/param.rs +++ b/src/parsers/param.rs @@ -9,6 +9,7 @@ use nom::sequence::tuple; use crate::parsers::common::{sjis_to_string, take_cstring, take_cstring_from, VarSizeInt}; use crate::parsers::paramdef; use crate::utils::bin::{has_flag, mask}; +use crate::utils::str as utils_str; const FLAGS2D_UNK1: u8 = 0b00000001; const FLAGS2D_32B_OFS_DATA: u8 = 0b00000010; @@ -39,6 +40,17 @@ impl ParamHeader { pub fn has_u64_ofs_data(&self) -> bool { has_u64_ofs_data(self.flags2D) } } +impl fmt::Display for ParamHeader { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} -- {}", + self.param_type, + utils_str::n_pluralise(self.num_rows as i32, "row", "rows") + ) + } +} + fn use_be(endianness: u8) -> bool { endianness == 0xFF } fn has_ofs_string_name(flags: u8) -> bool { has_flag(flags, FLAGS2D_OFS_STRING) } @@ -109,6 +121,12 @@ pub struct ParamRow { pub data: Vec, } +impl fmt::Display for ParamRow { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "[{}] {}", self.id, self.name.as_ref().unwrap_or(&String::from(""))) + } +} + #[derive(strum_macros::IntoStaticStr)] pub enum ParamRowValue { S8(i8), U8(u8), S16(i16), U16(u16), S32(i32), U32(u32), F32(f32), UNK(Vec) @@ -152,6 +170,7 @@ fn parse_row<'a>(i: &'a[u8], header: &ParamHeader) -> IResult<&'a[u8], ParamRow> Ok((i, ParamRow { id, ofs_data, ofs_name, name: None, data: vec!() })) } +/// Parse row data using field definitions from PARAMDEF. fn parse_row_data<'a>( i: &'a[u8], header: &ParamHeader, @@ -163,10 +182,11 @@ fn parse_row_data<'a>( let mut remaining_bits = 0; // Remaining bits in bitfield. let mut data_slice = i; for field in ¶mdef.fields { + let type_str = &field.display_type; + let num_bytes = field.byte_count as usize; let bit_size = field.bit_size(); let value = if bit_size == 0 { - let (rest, value) = parse_row_value(data_slice, &field.display_type, - field.byte_count as usize, use_be)?; + let (rest, value) = parse_row_value(data_slice, type_str, num_bytes, use_be)?; data_slice = rest; remaining_bits = 0; value @@ -174,35 +194,23 @@ fn parse_row_data<'a>( // Bitfield parsing. If it's the first bitfield in a series, get the containing bytes // in the bitfield var. if remaining_bits == 0 { - let (rest, bf) = take(field.byte_count as usize)(data_slice)?; - bitfield = match field.display_type.as_str() { - "u8" => { remaining_bits = 8; le_u8(bf).map(|(_, v)| v as u16)? } - "dummy8" => { remaining_bits = 8; le_u8(bf).map(|(_, v)| v as u16)? } - "u16" => { - remaining_bits = 16; - (if use_be { be_u16 } else { le_u16 }) (bf) .map(|(_, v)| v)? - } - e => panic!("Unhandled PARAMDEF type {}", e), - }; + let (rest, bf) = parse_row_bitfield(data_slice, type_str, num_bytes, use_be)?; data_slice = rest; + bitfield = bf; + remaining_bits = bit_size * 8; } - // Parse masked bits. - let value = bitfield & mask(bit_size as usize) as u16; + let value = parse_row_bitfield_value(bitfield, type_str, bit_size); // Shift bitfield so next values can be parsed directly with a bitmask. bitfield >>= bit_size; remaining_bits -= bit_size; - match field.display_type.as_str() { - "u8" => ParamRowValue::U8(value as u8), - "dummy8" => ParamRowValue::U8(value as u8), - "u16" => ParamRowValue::U16(value), - e => panic!("Unhandled PARAMDEF type {}", e), - } + value }; data.push(value); } Ok((i, data)) } +/// Parse a single row value, using its type string. fn parse_row_value<'a>( i: &'a[u8], type_str: &str, @@ -229,12 +237,51 @@ fn parse_row_value<'a>( }) } +/// Parse a bitfield unsigned int of the max handled size (u16). +/// +/// Parsing is done using the type string. Whatever the size of the int +/// is, place it in the max handled size to avoid maintaining one +/// bitfield for each type. +fn parse_row_bitfield<'a>( + bf: &'a[u8], + type_str: &str, + num_bytes: usize, + use_be: bool +) -> IResult<&'a[u8], u16> { + let (rest, bf) = take(num_bytes)(bf)?; + let bitfield = match type_str { + "u8" => le_u8(bf).map(|(_, v)| v as u16)?, + "dummy8" => le_u8(bf).map(|(_, v)| v as u16)?, + "u16" => (if use_be { be_u16 } else { le_u16 })(bf).map(|(_, v)| v)?, + e => panic!("Unhandled PARAMDEF type {}", e), + }; + Ok((rest, bitfield)) +} + +/// Parse a single row value (max u16) from a bitfield. +fn parse_row_bitfield_value(bitfield: u16, type_str: &str, bit_size: usize) -> ParamRowValue { + let value = bitfield & mask(bit_size) as u16; + match type_str { + "u8" => ParamRowValue::U8(value as u8), + "dummy8" => ParamRowValue::U8(value as u8), + "u16" => ParamRowValue::U16(value), + e => panic!("Unhandled PARAMDEF type {}", e), + } +} + #[derive(Debug)] pub struct Param { pub header: ParamHeader, pub rows: Vec, } +impl fmt::Display for Param { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.header, f) + } +} + +/// Parse PARAM data, using PARAMDEF info if provided. pub fn parse<'a>(i: &'a[u8], paramdef: Option<¶mdef::Paramdef>) -> IResult<&'a[u8], Param> { let full_file = i; let (i, mut header) = parse_header(i)?; diff --git a/src/parsers/paramdef.rs b/src/parsers/paramdef.rs index e91db7d..6747c3b 100644 --- a/src/parsers/paramdef.rs +++ b/src/parsers/paramdef.rs @@ -1,9 +1,12 @@ +use std::fmt; + use nom::IResult; use nom::multi::count; use nom::number::complete::*; use nom::sequence::tuple; use crate::parsers::common::{sjis_to_string_lossy, take_cstring, take_cstring_from, VarSizeInt}; +use crate::utils::str as utils_str; #[derive(Debug)] pub struct ParamdefHeader { @@ -100,6 +103,23 @@ impl ParamdefField { } } +impl fmt::Display for ParamdefField { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} {} ({}, {}, {})", + self.display_name, + self.internal_name.as_ref().unwrap_or(&String::from("")), + self.display_type, + self.internal_type, + match self.bit_size() { + 0 => utils_str::n_bytes_pluralise(self.byte_count as i32), + x => utils_str::n_pluralise(x as i32, "bit", "bits") + } + ) + } +} + fn parse_field<'a>(i: &'a[u8], header: &ParamdefHeader) -> IResult<&'a[u8], ParamdefField> { let (i, display_name) = take_cstring_from(i, 0x40)?; let (i, display_type) = take_cstring_from(i, 0x8)?; @@ -175,6 +195,20 @@ impl Paramdef { } } +impl fmt::Display for Paramdef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} -- ver. {} -- format ver. {} -- {} fields -- {} per row", + self.header.param_name, + self.header.data_version, + self.header.format_version, + self.header.num_fields, + utils_str::n_bytes_pluralise(self.row_size() as i32) + ) + } +} + pub fn parse(i: &[u8]) -> IResult<&[u8], Paramdef> { let full_file = i; let (i, header) = parse_header(i)?; diff --git a/src/unpackers/param.rs b/src/unpackers/param.rs index 2227e99..12c4c4c 100644 --- a/src/unpackers/param.rs +++ b/src/unpackers/param.rs @@ -6,7 +6,6 @@ use crate::parsers::param; use crate::parsers::paramdef; use crate::unpackers::errors::UnpackError; use crate::utils::fs as utils_fs; -use crate::utils::str as utils_str; /// Load a PARAM file from disk. /// @@ -35,20 +34,11 @@ pub fn load_param( } } -/// Print a PARAM's name and number of rows. -fn print_param_intro(param: ¶m::Param) { - println!( - "{} -- {}", - param.header.param_type, - utils_str::n_pluralise(param.header.num_rows as i32, "row", "rows") - ); -} - /// Print simple information about a PARAM. pub fn print_param(param: ¶m::Param) { - print_param_intro(param); + println!("{}", param); for row in ¶m.rows { - println!(" - [{}] {}", row.id, row.name.as_ref().unwrap_or(&String::from(""))); + println!(" - {}", row); if row.data.len() > 0 { println!(" {:?}", row.data); } @@ -57,10 +47,13 @@ pub fn print_param(param: ¶m::Param) { /// Print a PARAM's data using PARAMDEF fields. pub fn print_param_with_def(param: ¶m::Param, paramdef: ¶mdef::Paramdef) { - print_param_intro(param); + println!("{}", param); for row in ¶m.rows { - - println!("{:?}", row); - + println!(" - {}", row); + let mut desc = String::with_capacity(row.data.len() * 32); // Rough estimate. + for (value, field_def) in row.data.iter().zip(paramdef.fields.iter()) { + desc.push_str(&format!(" - {} = {}\n", field_def, value)); + } + println!("{}", desc.as_str()); } } diff --git a/src/unpackers/paramdef.rs b/src/unpackers/paramdef.rs index 8536326..8ced473 100644 --- a/src/unpackers/paramdef.rs +++ b/src/unpackers/paramdef.rs @@ -5,7 +5,6 @@ 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; -use crate::utils::str as utils_str; /// Load a PARAMDEF file from disk. /// @@ -24,34 +23,11 @@ pub fn load_paramdef(paramdef_data: &[u8]) -> Result utils_str::n_bytes_pluralise(field.byte_count as i32), - x => utils_str::n_pluralise(x as i32, "bit", "bits") - }; - println!( - " - [{}] {} ({}) {} ({}, {})", - field.sort_id, - field.display_name, - field.internal_name.as_ref().unwrap_or(&String::from("")), - field.display_type, - field.internal_type, - size_str - ); + println!(" - {}", field); println!( " Values: default {}, range [{}, {}], inc {}", field.default_value,