param: complete parsing without data
This commit is contained in:
parent
1d3dab7523
commit
93fec86085
|
@ -218,7 +218,7 @@ fn cmd_paramdef(args: &ArgMatches) -> i32 {
|
||||||
fn cmd_param(args: &ArgMatches) -> i32 {
|
fn cmd_param(args: &ArgMatches) -> i32 {
|
||||||
let file_path: &str = args.value_of("file").unwrap();
|
let file_path: &str = args.value_of("file").unwrap();
|
||||||
match unpackers::param::load_param_file(file_path) {
|
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 }
|
Err(e) => { eprintln!("Failed to load PARAM: {:?}", e); 1 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use encoding_rs::SHIFT_JIS;
|
use encoding_rs::SHIFT_JIS;
|
||||||
use nom::IResult;
|
use nom::IResult;
|
||||||
use nom::bytes::complete::take_while;
|
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))
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use nom::IResult;
|
use nom::IResult;
|
||||||
|
use nom::bytes::complete::take;
|
||||||
|
use nom::multi::count;
|
||||||
use nom::number::complete::*;
|
use nom::number::complete::*;
|
||||||
use nom::sequence::tuple;
|
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;
|
use crate::utils::bin::has_flag;
|
||||||
|
|
||||||
const FLAGS2D_UNK1: u8 = 0b00000001;
|
const FLAGS2D_UNK1: u8 = 0b00000001;
|
||||||
|
@ -11,34 +13,34 @@ const FLAGS2D_64B_OFS_DATA: u8 = 0b00000100;
|
||||||
const FLAGS2D_OFS_STRING: u8 = 0b10000000;
|
const FLAGS2D_OFS_STRING: u8 = 0b10000000;
|
||||||
const FLAGS2E_UNICODE_NAMES: u8 = 0b00000001;
|
const FLAGS2E_UNICODE_NAMES: u8 = 0b00000001;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ParamHeader {
|
pub struct ParamHeader {
|
||||||
pub ofs_strings: u32,
|
pub ofs_strings: u32, // Unreliable.
|
||||||
pub ofs_data: u16,
|
pub ofs_data: u16,
|
||||||
pub unk06: u16,
|
pub unk06: u16,
|
||||||
pub paramdef_data_version: u16,
|
pub paramdef_data_version: u16,
|
||||||
pub num_rows: u16,
|
pub num_rows: u16,
|
||||||
pub param_type: String,
|
pub param_type: String,
|
||||||
|
pub ofs_name: Option<u64>,
|
||||||
pub endianness: u8,
|
pub endianness: u8,
|
||||||
pub flags2D: u8,
|
pub flags2D: u8,
|
||||||
pub flags2E: u8,
|
pub flags2E: u8,
|
||||||
pub paramdef_format_version: u8,
|
pub paramdef_format_version: u8,
|
||||||
pub ofs_data_long: Option<ParamHeaderLongOfsData>,
|
pub ofs_data_long: Option<VarSizeInt>,
|
||||||
}
|
|
||||||
|
|
||||||
pub union ParamHeaderLongOfsData {
|
|
||||||
ofs32: u32,
|
|
||||||
ofs64: u64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParamHeader {
|
impl ParamHeader {
|
||||||
pub fn use_be(&self) -> bool { use_be(self.endianness) }
|
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 use_be(endianness: u8) -> bool { endianness == 0xFF }
|
||||||
|
|
||||||
fn has_ofs_string_name(flags: u8) -> bool { has_flag(flags, FLAGS2D_OFS_STRING) }
|
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) }
|
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)) =
|
let (i, (ofs_strings, ofs_data, unk06, paramdef_data_version, num_rows)) =
|
||||||
tuple((p_u32, p_u16, p_u16, p_u16, p_u16))(i)?;
|
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.
|
// 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 {
|
} 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.
|
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 (i, ofs_data_long) = if use_u32_ofs_data {
|
||||||
let (_, o) = p_u32(i)?;
|
let (_, o) = p_u32(i)?;
|
||||||
(&i[0x20..], Some(ParamHeaderLongOfsData { ofs32: o }))
|
(&i[0x20..], Some(VarSizeInt { vu32: o }))
|
||||||
} else if use_u64_ofs_data {
|
} else if use_u64_ofs_data {
|
||||||
let (_, o) = p_u64(i)?;
|
let (_, o) = p_u64(i)?;
|
||||||
(&i[0x20..], Some(ParamHeaderLongOfsData { ofs64: o }))
|
(&i[0x20..], Some(VarSizeInt { vu64: o }))
|
||||||
} else {
|
} else {
|
||||||
(i, None)
|
(i, None)
|
||||||
};
|
};
|
||||||
|
@ -83,6 +87,7 @@ fn parse_header(i: &[u8]) -> IResult<&[u8], ParamHeader> {
|
||||||
paramdef_data_version,
|
paramdef_data_version,
|
||||||
num_rows,
|
num_rows,
|
||||||
param_type,
|
param_type,
|
||||||
|
ofs_name,
|
||||||
endianness,
|
endianness,
|
||||||
flags2D,
|
flags2D,
|
||||||
flags2E,
|
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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 struct Param {
|
||||||
pub header: ParamHeader,
|
pub header: ParamHeader,
|
||||||
|
pub rows: Vec<ParamRow>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(i: &[u8]) -> IResult<&[u8], Param> {
|
pub fn parse(i: &[u8]) -> IResult<&[u8], Param> {
|
||||||
let full_file = i;
|
let full_file = i;
|
||||||
let (i, header) = parse_header(i)?;
|
let (i, mut header) = parse_header(i)?;
|
||||||
Ok((i, Param { header }))
|
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 }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use nom::multi::count;
|
||||||
use nom::number::complete::*;
|
use nom::number::complete::*;
|
||||||
use nom::sequence::tuple;
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct ParamdefHeader {
|
pub struct ParamdefHeader {
|
||||||
|
@ -63,6 +63,7 @@ fn parse_header(i: &[u8]) -> IResult<&[u8], ParamdefHeader> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ParamdefField {
|
pub struct ParamdefField {
|
||||||
pub display_name: String,
|
pub display_name: String,
|
||||||
pub display_type: String,
|
pub display_type: String,
|
||||||
|
@ -73,7 +74,7 @@ pub struct ParamdefField {
|
||||||
pub increment: f32,
|
pub increment: f32,
|
||||||
pub edit_flags: u32,
|
pub edit_flags: u32,
|
||||||
pub byte_count: u32,
|
pub byte_count: u32,
|
||||||
pub ofs_desc: ParamdefFieldDescOffset,
|
pub ofs_desc: VarSizeInt,
|
||||||
pub internal_type: String,
|
pub internal_type: String,
|
||||||
pub internal_name: Option<String>,
|
pub internal_name: Option<String>,
|
||||||
pub sort_id: u32,
|
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> {
|
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_name) = take_cstring_from(i, 0x40)?;
|
||||||
let (i, display_type) = take_cstring_from(i, 0x8)?;
|
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, ofs_desc) = if header.format_version < 201 {
|
||||||
let (i, o) = p_u32(i)?;
|
let (i, o) = p_u32(i)?;
|
||||||
(i, ParamdefFieldDescOffset { ofs32: o })
|
(i, VarSizeInt { vu32: o })
|
||||||
} else {
|
} else {
|
||||||
let (i, o) = p_u64(i)?;
|
let (i, o) = p_u64(i)?;
|
||||||
(i, ParamdefFieldDescOffset { ofs64: o })
|
(i, VarSizeInt { vu64: o })
|
||||||
};
|
};
|
||||||
|
|
||||||
let (i, internal_type) = take_cstring_from(i, 0x20)?;
|
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 struct Paramdef {
|
||||||
pub header: ParamdefHeader,
|
pub header: ParamdefHeader,
|
||||||
pub fields: Vec<ParamdefField>,
|
pub fields: Vec<ParamdefField>,
|
||||||
|
@ -174,9 +171,9 @@ pub fn parse(i: &[u8]) -> IResult<&[u8], Paramdef> {
|
||||||
|
|
||||||
for field in &mut fields {
|
for field in &mut fields {
|
||||||
let ofs: usize = if header.has_64b_ofs_desc() {
|
let ofs: usize = if header.has_64b_ofs_desc() {
|
||||||
unsafe { field.ofs_desc.ofs64 as usize }
|
unsafe { field.ofs_desc.vu64 as usize }
|
||||||
} else {
|
} else {
|
||||||
unsafe { field.ofs_desc.ofs32 as usize }
|
unsafe { field.ofs_desc.vu32 as usize }
|
||||||
};
|
};
|
||||||
if ofs == 0 {
|
if ofs == 0 {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -5,6 +5,7 @@ use nom::Err::{Error as NomError, Failure as NomFailure};
|
||||||
use crate::parsers::param;
|
use crate::parsers::param;
|
||||||
use crate::unpackers::errors::UnpackError;
|
use crate::unpackers::errors::UnpackError;
|
||||||
use crate::utils::fs as utils_fs;
|
use crate::utils::fs as utils_fs;
|
||||||
|
use crate::utils::str as utils_str;
|
||||||
|
|
||||||
pub fn load_param_file(param_path: &str) -> Result<param::Param, UnpackError> {
|
pub fn load_param_file(param_path: &str) -> Result<param::Param, UnpackError> {
|
||||||
let param_data = utils_fs::open_file_to_vec(path::Path::new(param_path))?;
|
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<param::Param, UnpackError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_param(param: ¶m::Param) {
|
pub fn print_param_no_data(param: ¶m::Param) {
|
||||||
println!("{} -- {} rows", param.header.param_type, param.header.num_rows);
|
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("<noname>")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue