From 93fec8608536638f66d4e36e413ac991a6cd59c4 Mon Sep 17 00:00:00 2001 From: dece Date: Sun, 17 May 2020 18:48:45 +0200 Subject: [PATCH] param: complete parsing without data --- src/bin/rir.rs | 2 +- src/parsers/common.rs | 15 +++++++ src/parsers/param.rs | 92 ++++++++++++++++++++++++++++++++++------- src/parsers/paramdef.rs | 19 ++++----- src/unpackers/param.rs | 10 ++++- 5 files changed, 108 insertions(+), 30 deletions(-) diff --git a/src/bin/rir.rs b/src/bin/rir.rs index 797cd64..7bbd469 100644 --- a/src/bin/rir.rs +++ b/src/bin/rir.rs @@ -218,7 +218,7 @@ fn cmd_paramdef(args: &ArgMatches) -> i32 { 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 } + Ok(param) => { unpackers::param::print_param_no_data(¶m); 0 } Err(e) => { eprintln!("Failed to load PARAM: {:?}", e); 1 } } } diff --git a/src/parsers/common.rs b/src/parsers/common.rs index 4734b9f..baa69cf 100644 --- a/src/parsers/common.rs +++ b/src/parsers/common.rs @@ -1,3 +1,5 @@ +use std::fmt; + use encoding_rs::SHIFT_JIS; use nom::IResult; use nom::bytes::complete::take_while; @@ -30,6 +32,19 @@ pub fn sjis_to_string_lossy(i: &[u8]) -> String { sjis_to_string(i).unwrap_or(format!("{:x?}", i)) } +/// Represent an integer that can be 32 or 64 bits, +/// depending on the platform and flags used. +pub union VarSizeInt { + pub vu32: u32, + pub vu64: u64, +} + +impl fmt::Debug for VarSizeInt { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VarSizeInt: {{ {}: u32, {}: u64 }}", unsafe { self.vu32 }, unsafe { self.vu64 }) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/parsers/param.rs b/src/parsers/param.rs index 8bafa35..d3290e8 100644 --- a/src/parsers/param.rs +++ b/src/parsers/param.rs @@ -1,8 +1,10 @@ use nom::IResult; +use nom::bytes::complete::take; +use nom::multi::count; use nom::number::complete::*; use nom::sequence::tuple; -use crate::parsers::common::{sjis_to_string, take_cstring_from}; +use crate::parsers::common::{sjis_to_string, take_cstring, take_cstring_from, VarSizeInt}; use crate::utils::bin::has_flag; const FLAGS2D_UNK1: u8 = 0b00000001; @@ -11,34 +13,34 @@ const FLAGS2D_64B_OFS_DATA: u8 = 0b00000100; const FLAGS2D_OFS_STRING: u8 = 0b10000000; const FLAGS2E_UNICODE_NAMES: u8 = 0b00000001; +#[derive(Debug)] pub struct ParamHeader { - pub ofs_strings: u32, + pub ofs_strings: u32, // Unreliable. pub ofs_data: u16, pub unk06: u16, pub paramdef_data_version: u16, pub num_rows: u16, pub param_type: String, + pub ofs_name: Option, 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, + pub ofs_data_long: Option, } impl ParamHeader { pub fn use_be(&self) -> bool { use_be(self.endianness) } + pub fn has_ofs_string_name(&self) -> bool { has_ofs_string_name(self.flags2D) } + pub fn has_u32_ofs_data(&self) -> bool { has_u32_ofs_data(self.flags2D) } + pub fn has_u64_ofs_data(&self) -> bool { has_u64_ofs_data(self.flags2D) } } 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_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) } @@ -54,11 +56,13 @@ fn parse_header(i: &[u8]) -> IResult<&[u8], ParamHeader> { 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) { + let (i, param_type, ofs_name) = if has_ofs_string_name(flags2D) { // Name is in the strings block, parse it later. - (&i[0x20..], String::new()) + let (i, (_, ofs_name, _)) = tuple((take(0x4usize), p_u64, take(0x14usize)))(i)?; + (i, String::new(), Some(ofs_name)) } else { - take_cstring_from(i, 0x20).map(|(i, s)| (i, String::from_utf8_lossy(s).to_string()))? + let (i, name) = take_cstring_from(i, 0x20)?; + (i, String::from_utf8_lossy(name).to_string(), None) }; let i = &i[0x2..]; // Skip endianness and flags2D. @@ -66,10 +70,10 @@ fn parse_header(i: &[u8]) -> IResult<&[u8], ParamHeader> { let (i, ofs_data_long) = if use_u32_ofs_data { let (_, o) = p_u32(i)?; - (&i[0x20..], Some(ParamHeaderLongOfsData { ofs32: o })) + (&i[0x20..], Some(VarSizeInt { vu32: o })) } else if use_u64_ofs_data { let (_, o) = p_u64(i)?; - (&i[0x20..], Some(ParamHeaderLongOfsData { ofs64: o })) + (&i[0x20..], Some(VarSizeInt { vu64: o })) } else { (i, None) }; @@ -83,6 +87,7 @@ fn parse_header(i: &[u8]) -> IResult<&[u8], ParamHeader> { paramdef_data_version, num_rows, param_type, + ofs_name, endianness, flags2D, flags2E, @@ -92,12 +97,67 @@ fn parse_header(i: &[u8]) -> IResult<&[u8], ParamHeader> { )) } +#[derive(Debug)] +pub struct ParamRow { + pub id: u32, + pub ofs_data: VarSizeInt, + pub ofs_name: VarSizeInt, + pub name: Option, +} + +impl ParamRow { + /// Get a u64 for the name offset, regardless of the format. + pub fn get_ofs_name(&self, header: &ParamHeader) -> u64 { + if header.has_u64_ofs_data() { + unsafe { self.ofs_name.vu64 } + } else { + unsafe { self.ofs_name.vu32 as u64 } + } + } +} + +fn parse_row<'a>(i: &'a[u8], header: &ParamHeader) -> IResult<&'a[u8], ParamRow> { + let p_u32 = if header.use_be() { be_u32 } else { le_u32 }; + let p_u64 = if header.use_be() { be_u64 } else { le_u64 }; + + let (i, (id, ofs_data, ofs_name)) = if header.has_u64_ofs_data() { + let (i, (id, _, ofs_data, ofs_name)) = tuple((p_u32, take(4usize), p_u64, p_u64))(i)?; + (i, (id, VarSizeInt { vu64: ofs_data }, VarSizeInt { vu64: ofs_name })) + } else { + let (i, (id, ofs_data, ofs_name)) = tuple((p_u32, p_u32, p_u32))(i)?; + (i, (id, VarSizeInt { vu32: ofs_data }, VarSizeInt { vu32: ofs_name })) + }; + + Ok((i, ParamRow { id, ofs_data, ofs_name, name: None })) +} + +#[derive(Debug)] pub struct Param { pub header: ParamHeader, + pub rows: Vec, } pub fn parse(i: &[u8]) -> IResult<&[u8], Param> { let full_file = i; - let (i, header) = parse_header(i)?; - Ok((i, Param { header })) + let (i, mut header) = parse_header(i)?; + if header.has_ofs_string_name() && header.ofs_name.is_some() { + let ofs_name = header.ofs_name.unwrap() as usize; + let (_, name) = take_cstring(&full_file[ofs_name..])?; + header.param_type.push_str(&String::from_utf8_lossy(name).to_string()); + } + + let (i, mut rows) = count(|i| parse_row(i, &header), header.num_rows as usize)(i)?; + + for row in &mut rows { + let ofs_name = row.get_ofs_name(&header) as usize; + if ofs_name != 0 { + let (_, name) = take_cstring(&full_file[ofs_name..])?; + row.name = sjis_to_string(name).or_else(|| { + eprintln!("Can't parse row name: {:?}", name); + None + }); + } + } + + Ok((i, Param { header, rows })) } diff --git a/src/parsers/paramdef.rs b/src/parsers/paramdef.rs index a3e3e70..a0c9b2a 100644 --- a/src/parsers/paramdef.rs +++ b/src/parsers/paramdef.rs @@ -3,7 +3,7 @@ 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}; +use crate::parsers::common::{sjis_to_string_lossy, take_cstring, take_cstring_from, VarSizeInt}; #[derive(Debug)] pub struct ParamdefHeader { @@ -63,6 +63,7 @@ fn parse_header(i: &[u8]) -> IResult<&[u8], ParamdefHeader> { )) } +#[derive(Debug)] pub struct ParamdefField { pub display_name: String, pub display_type: String, @@ -73,7 +74,7 @@ pub struct ParamdefField { pub increment: f32, pub edit_flags: u32, pub byte_count: u32, - pub ofs_desc: ParamdefFieldDescOffset, + pub ofs_desc: VarSizeInt, pub internal_type: String, pub internal_name: Option, pub sort_id: u32, @@ -99,11 +100,6 @@ impl ParamdefField { } } -pub union ParamdefFieldDescOffset { - ofs32: u32, - ofs64: u64, -} - 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)?; @@ -118,10 +114,10 @@ fn parse_field<'a>(i: &'a[u8], header: &ParamdefHeader) -> IResult<&'a[u8], Para let (i, ofs_desc) = if header.format_version < 201 { let (i, o) = p_u32(i)?; - (i, ParamdefFieldDescOffset { ofs32: o }) + (i, VarSizeInt { vu32: o }) } else { let (i, o) = p_u64(i)?; - (i, ParamdefFieldDescOffset { ofs64: o }) + (i, VarSizeInt { vu64: o }) }; let (i, internal_type) = take_cstring_from(i, 0x20)?; @@ -156,6 +152,7 @@ fn parse_field<'a>(i: &'a[u8], header: &ParamdefHeader) -> IResult<&'a[u8], Para )) } +#[derive(Debug)] pub struct Paramdef { pub header: ParamdefHeader, pub fields: Vec, @@ -174,9 +171,9 @@ pub fn parse(i: &[u8]) -> IResult<&[u8], Paramdef> { for field in &mut fields { let ofs: usize = if header.has_64b_ofs_desc() { - unsafe { field.ofs_desc.ofs64 as usize } + unsafe { field.ofs_desc.vu64 as usize } } else { - unsafe { field.ofs_desc.ofs32 as usize } + unsafe { field.ofs_desc.vu32 as usize } }; if ofs == 0 { continue diff --git a/src/unpackers/param.rs b/src/unpackers/param.rs index 5a96087..5a4c885 100644 --- a/src/unpackers/param.rs +++ b/src/unpackers/param.rs @@ -5,6 +5,7 @@ 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; +use crate::utils::str as utils_str; pub fn load_param_file(param_path: &str) -> Result { let param_data = utils_fs::open_file_to_vec(path::Path::new(param_path))?; @@ -19,6 +20,11 @@ pub fn load_param(param_data: &[u8]) -> Result { } } -pub fn print_param(param: ¶m::Param) { - println!("{} -- {} rows", param.header.param_type, param.header.num_rows); +pub fn print_param_no_data(param: ¶m::Param) { + println!("{} -- {}", param.header.param_type, + utils_str::n_plural(param.header.num_rows as i32, "row", "rows")); + + for row in ¶m.rows { + println!(" - [{}] {}", row.id, row.name.as_ref().unwrap_or(&String::from(""))); + } }