param: complete printing

This commit is contained in:
dece 2020-05-23 00:16:10 +02:00
parent 15dd0b2cb8
commit be1d40ac7b
5 changed files with 131 additions and 77 deletions

View file

@ -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(&paramdef)) {
Ok(param) => { unpackers::param::print_param_with_def(&param, &paramdef); 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(&param); 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(&param, &paramdef),
None => unpackers::param::print_param(&param),
};
0
}

View file

@ -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<ParamRowValue>,
}
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("<noname>")))
}
}
#[derive(strum_macros::IntoStaticStr)]
pub enum ParamRowValue {
S8(i8), U8(u8), S16(i16), U16(u16), S32(i32), U32(u32), F32(f32), UNK(Vec<u8>)
@ -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 &paramdef.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<ParamRow>,
}
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<&paramdef::Paramdef>) -> IResult<&'a[u8], Param> {
let full_file = i;
let (i, mut header) = parse_header(i)?;

View file

@ -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("<noname>")),
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)?;

View file

@ -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: &param::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: &param::Param) {
print_param_intro(param);
println!("{}", param);
for row in &param.rows {
println!(" - [{}] {}", row.id, row.name.as_ref().unwrap_or(&String::from("<noname>")));
println!(" - {}", row);
if row.data.len() > 0 {
println!(" {:?}", row.data);
}
@ -57,10 +47,13 @@ pub fn print_param(param: &param::Param) {
/// Print a PARAM's data using PARAMDEF fields.
pub fn print_param_with_def(param: &param::Param, paramdef: &paramdef::Paramdef) {
print_param_intro(param);
println!("{}", param);
for row in &param.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());
}
}

View file

@ -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<paramdef::Paramdef, UnpackE
}
}
/// Print brief data about a PARAMDEF.
pub fn print_paramdef_intro(paramdef: &paramdef::Paramdef) {
println!(
"{} -- ver. {} -- format ver. {} -- {} fields -- {} per row",
paramdef.header.param_name,
paramdef.header.data_version, paramdef.header.format_version,
paramdef.header.num_fields,
utils_str::n_bytes_pluralise(paramdef.row_size() as i32)
);
}
/// Print verbose data about a PARAMDEF.
pub fn print_paramdef(paramdef: &paramdef::Paramdef) {
print_paramdef_intro(paramdef);
println!("{}", paramdef);
for field in &paramdef.fields {
let size_str = match field.bit_size() {
0 => 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("<noname>")),
field.display_type,
field.internal_type,
size_str
);
println!(" - {}", field);
println!(
" Values: default {}, range [{}, {}], inc {}",
field.default_value,