param: good enough parsing of bitfields
This commit is contained in:
parent
da80ff1d9a
commit
15dd0b2cb8
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -114,6 +114,14 @@ dependencies = [
|
|||
"syn 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.10"
|
||||
|
@ -395,6 +403,7 @@ dependencies = [
|
|||
"nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strum_macros 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -463,6 +472,17 @@ name = "strsim"
|
|||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.20"
|
||||
|
@ -481,6 +501,11 @@ dependencies = [
|
|||
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.7"
|
||||
|
@ -540,6 +565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28"
|
||||
"checksum flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42"
|
||||
"checksum ghost 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a36606a68532b5640dc86bb1f33c64b45c4682aad4c50f3937b317ea387f3d6"
|
||||
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||
"checksum hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e"
|
||||
"checksum indoc 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "79255cf29f5711995ddf9ec261b4057b1deb34e66c90656c201e41376872c544"
|
||||
"checksum indoc-impl 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "54554010aa3d17754e484005ea0022f1c93839aabc627c2c55f3d7b47206134c"
|
||||
|
@ -580,8 +606,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
|
||||
"checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
||||
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
"checksum strum_macros 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c"
|
||||
"checksum syn 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1b5e337360b1fae433c59fcafa0c6b77c605e92540afa5221a7b81a9eca91d"
|
||||
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
|
||||
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum unindent 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "63f18aa3b0e35fed5a0048f029558b1518095ffe2a0a31fb87c93dece93a4993"
|
||||
|
|
|
@ -18,6 +18,7 @@ flate2 = "1.0"
|
|||
nom = "5"
|
||||
num-bigint = "0.2"
|
||||
num-traits = "0.2"
|
||||
strum_macros = "0.18"
|
||||
|
||||
[workspace]
|
||||
members = ["bindings/python"]
|
||||
|
|
|
@ -222,7 +222,7 @@ fn cmd_param(args: &ArgMatches) -> i32 {
|
|||
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(¶m); 0 }
|
||||
Ok(param) => { unpackers::param::print_param_with_def(¶m, ¶mdef); 0 }
|
||||
Err(e) => { eprintln!("Failed to load PARAM: {:?}", e); 1 }
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ fn cmd_param(args: &ArgMatches) -> i32 {
|
|||
}
|
||||
} else {
|
||||
match unpackers::param::load_param_file(file_path, None) {
|
||||
Ok(param) => { unpackers::param::print_param_no_data(¶m); 0 }
|
||||
Ok(param) => { unpackers::param::print_param(¶m); 0 }
|
||||
Err(e) => { eprintln!("Failed to load PARAM: {:?}", e); 1 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::fmt::{self, Debug};
|
||||
|
||||
use nom::IResult;
|
||||
use nom::bytes::complete::take;
|
||||
use nom::multi::count;
|
||||
|
@ -6,7 +8,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;
|
||||
use crate::utils::bin::{has_flag, mask};
|
||||
|
||||
const FLAGS2D_UNK1: u8 = 0b00000001;
|
||||
const FLAGS2D_32B_OFS_DATA: u8 = 0b00000010;
|
||||
|
@ -104,7 +106,35 @@ pub struct ParamRow {
|
|||
pub ofs_data: VarSizeInt,
|
||||
pub ofs_name: VarSizeInt,
|
||||
pub name: Option<String>,
|
||||
pub data: Vec<u8>,
|
||||
pub data: Vec<ParamRowValue>,
|
||||
}
|
||||
|
||||
#[derive(strum_macros::IntoStaticStr)]
|
||||
pub enum ParamRowValue {
|
||||
S8(i8), U8(u8), S16(i16), U16(u16), S32(i32), U32(u32), F32(f32), UNK(Vec<u8>)
|
||||
}
|
||||
|
||||
impl fmt::Debug for ParamRowValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let name: &str = self.into();
|
||||
write!(f, "{}: {}", name, self)
|
||||
}
|
||||
}
|
||||
|
||||
// Could be probably be done better with a macro...
|
||||
impl fmt::Display for ParamRowValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ParamRowValue::S8(i) => fmt::Display::fmt(&i, f),
|
||||
ParamRowValue::U8(i) => fmt::Display::fmt(&i, f),
|
||||
ParamRowValue::S16(i) => fmt::Display::fmt(&i, f),
|
||||
ParamRowValue::U16(i) => fmt::Display::fmt(&i, f),
|
||||
ParamRowValue::S32(i) => fmt::Display::fmt(&i, f),
|
||||
ParamRowValue::U32(i) => fmt::Display::fmt(&i, f),
|
||||
ParamRowValue::F32(i) => fmt::Display::fmt(&i, f),
|
||||
ParamRowValue::UNK(i) => fmt::Debug::fmt(&i, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_row<'a>(i: &'a[u8], header: &ParamHeader) -> IResult<&'a[u8], ParamRow> {
|
||||
|
@ -122,6 +152,83 @@ 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!() }))
|
||||
}
|
||||
|
||||
fn parse_row_data<'a>(
|
||||
i: &'a[u8],
|
||||
header: &ParamHeader,
|
||||
paramdef: ¶mdef::Paramdef
|
||||
) -> IResult<&'a[u8], Vec<ParamRowValue>> {
|
||||
let use_be = header.use_be();
|
||||
let mut data = vec!();
|
||||
let mut bitfield = 0u16; // Current bitfield being parsed. u16 is largest handled type.
|
||||
let mut remaining_bits = 0; // Remaining bits in bitfield.
|
||||
let mut data_slice = i;
|
||||
for field in ¶mdef.fields {
|
||||
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)?;
|
||||
data_slice = rest;
|
||||
remaining_bits = 0;
|
||||
value
|
||||
} else {
|
||||
// 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),
|
||||
};
|
||||
data_slice = rest;
|
||||
}
|
||||
// Parse masked bits.
|
||||
let value = bitfield & mask(bit_size as usize) as u16;
|
||||
// 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),
|
||||
}
|
||||
};
|
||||
data.push(value);
|
||||
}
|
||||
Ok((i, data))
|
||||
}
|
||||
|
||||
fn parse_row_value<'a>(
|
||||
i: &'a[u8],
|
||||
type_str: &str,
|
||||
num_bytes: usize,
|
||||
use_be: bool
|
||||
) -> IResult<&'a[u8], ParamRowValue> {
|
||||
Ok(match type_str {
|
||||
"s8" => le_i8(i)
|
||||
.map(|(i, v)| (i, ParamRowValue::S8(v)))?,
|
||||
"u8" => le_u8(i)
|
||||
.map(|(i, v)| (i, ParamRowValue::U8(v)))?,
|
||||
"s16" => (if use_be { be_i16 } else { le_i16 }) (i)
|
||||
.map(|(i, v)| (i, ParamRowValue::S16(v)))?,
|
||||
"u16" => (if use_be { be_u16 } else { le_u16 }) (i)
|
||||
.map(|(i, v)| (i, ParamRowValue::U16(v)))?,
|
||||
"s32" => (if use_be { be_i32 } else { le_i32 }) (i)
|
||||
.map(|(i, v)| (i, ParamRowValue::S32(v)))?,
|
||||
"u32" => (if use_be { be_u32 } else { le_u32 }) (i)
|
||||
.map(|(i, v)| (i, ParamRowValue::U32(v)))?,
|
||||
"f32" => (if use_be { be_f32 } else { le_f32 }) (i)
|
||||
.map(|(i, v)| (i, ParamRowValue::F32(v)))?,
|
||||
_ => take(num_bytes)(i)
|
||||
.map(|(i, v)| (i, ParamRowValue::UNK(v.to_vec())))?,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Param {
|
||||
pub header: ParamHeader,
|
||||
|
@ -151,14 +258,16 @@ pub fn parse<'a>(i: &'a[u8], paramdef: Option<¶mdef::Paramdef>) -> IResult<&
|
|||
}
|
||||
|
||||
if paramdef.is_some() {
|
||||
let row_size = paramdef.unwrap().row_size();
|
||||
let def = paramdef.unwrap();
|
||||
let row_size = def.row_size();
|
||||
for row in &mut rows {
|
||||
let ofs_data = row.ofs_data.u64_if(header.has_u64_ofs_data()) as usize;
|
||||
if ofs_data == 0 {
|
||||
continue
|
||||
}
|
||||
let ofs_data_end = ofs_data + row_size;
|
||||
row.data = full_file[ofs_data..ofs_data_end].to_vec();
|
||||
let (_, data) = parse_row_data(&full_file[ofs_data..ofs_data_end], &header, &def)?;
|
||||
row.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ pub fn load_param(
|
|||
}
|
||||
}
|
||||
|
||||
/// Print a PARAM's name and number of rows.
|
||||
fn print_param_intro(param: ¶m::Param) {
|
||||
println!(
|
||||
"{} -- {}",
|
||||
|
@ -43,16 +44,23 @@ fn print_param_intro(param: ¶m::Param) {
|
|||
);
|
||||
}
|
||||
|
||||
pub fn print_param_no_data(param: ¶m::Param) {
|
||||
print_param_intro(param);
|
||||
for row in ¶m.rows {
|
||||
println!(" - [{}] {}", row.id, row.name.as_ref().unwrap_or(&String::from("<noname>")));
|
||||
}
|
||||
}
|
||||
|
||||
/// Print simple information about a PARAM.
|
||||
pub fn print_param(param: ¶m::Param) {
|
||||
print_param_intro(param);
|
||||
for row in ¶m.rows {
|
||||
println!("{:?}", row);
|
||||
println!(" - [{}] {}", row.id, row.name.as_ref().unwrap_or(&String::from("<noname>")));
|
||||
if row.data.len() > 0 {
|
||||
println!(" {:?}", row.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Print a PARAM's data using PARAMDEF fields.
|
||||
pub fn print_param_with_def(param: ¶m::Param, paramdef: ¶mdef::Paramdef) {
|
||||
print_param_intro(param);
|
||||
for row in ¶m.rows {
|
||||
|
||||
println!("{:?}", row);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,11 @@ pub fn has_flag(i: u8, flag: u8) -> bool {
|
|||
i & flag == flag
|
||||
}
|
||||
|
||||
/// Return a mask for this number of bits.
|
||||
pub fn mask(bit_size: usize) -> usize {
|
||||
(1 << bit_size) - 1
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
@ -16,4 +21,13 @@ mod test {
|
|||
assert!(has_flag(0x80, 0x80));
|
||||
assert!(!has_flag(0x80, 0x40));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mask() {
|
||||
assert_eq!(mask(1), 0b00000001);
|
||||
assert_eq!(mask(2), 0b00000011);
|
||||
assert_eq!(mask(4), 0b00001111);
|
||||
assert_eq!(mask(8), 0b11111111);
|
||||
assert_eq!(mask(15), 0b01111111_11111111);
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue