use std::convert::TryInto;
use crate::{
common::{parser::parse_crc_node, FileMeta, FileMetaPair},
md5::MD5Sum,
};
use super::file::*;
use nom::{
bytes::complete::{tag, take},
combinator::{map, map_res},
multi::length_count,
number::complete::le_u32,
sequence::tuple,
IResult,
};
pub fn parse_pk_magic(input: &[u8]) -> IResult<&[u8], ()> {
let (rest, _) = tag("ndpk")(input)?;
Ok((rest, ()))
}
pub fn parse_pk_trailer(input: &[u8]) -> IResult<&[u8], PKTrailer> {
map(tuple((le_u32, le_u32)), |(a, b)| PKTrailer {
file_list_base_addr: a,
num_compressed: b,
})(input)
}
fn parse_hash(i: &[u8]) -> IResult<&[u8], MD5Sum> {
map_res(take(32usize), MD5Sum::from_hex_bytes)(i)
}
fn parse_compressed(i: &[u8]) -> IResult<&[u8], u32> {
let (i, byte_slice) = take(4usize)(i)?;
let bytes: [u8; 4] = byte_slice.try_into().unwrap();
Ok((i, u32::from_le_bytes(bytes)))
}
fn parse_file_meta(input: &[u8]) -> IResult<&[u8], FileMeta> {
let (input, size) = le_u32(input)?;
let (input, hash) = parse_hash(input)?;
let (input, _padding) = take(4usize)(input)?;
Ok((input, FileMeta { size, hash }))
}
fn parse_file_meta_pair(input: &[u8]) -> IResult<&[u8], FileMetaPair> {
let (input, raw) = parse_file_meta(input)?;
let (input, compressed) = parse_file_meta(input)?;
Ok((input, FileMetaPair { raw, compressed }))
}
pub fn parse_pk_entry_data(input: &[u8]) -> IResult<&[u8], PKEntryData> {
let (input, meta) = parse_file_meta_pair(input)?;
let (input, file_data_addr) = le_u32(input)?;
let (input, is_compressed) = parse_compressed(input)?;
Ok((
input,
PKEntryData {
meta,
file_data_addr,
is_compressed,
},
))
}
pub fn parse_pk_entry(input: &[u8]) -> IResult<&[u8], PKEntry> {
parse_crc_node(parse_pk_entry_data)(input)
}
pub fn parse_pk_entry_list(input: &[u8]) -> IResult<&[u8], Vec<PKEntry>> {
length_count(le_u32, parse_pk_entry)(input)
}
#[cfg(test)]
mod tests {
use crate::crc::CRC;
use super::*;
const EMPTY: &[u8] = &[];
#[test]
fn test_magic() {
assert_eq!(parse_pk_magic(b"ndpk"), Ok((EMPTY, ())));
}
#[test]
fn test_trailer() {
assert_eq!(
parse_pk_trailer(&[0x20, 0x10, 0, 0, 0, 0, 0, 0]),
Ok((
EMPTY,
PKTrailer {
file_list_base_addr: 0x1020,
num_compressed: 0,
}
))
)
}
const ARR: [u8; 16] = [
0x33, 0x7e, 0x24, 0xd7, 0x26, 0xfd, 0x72, 0x8f, 0x92, 0x95, 0x7a, 0x2c, 0x90, 0x8d, 0xde, 0xe6,
];
const ARR2: [u8; 16] = [
0x44, 0x7e, 0x24, 0xd7, 0x26, 0xfd, 0x72, 0x8f, 0x92, 0x95, 0x7a, 0x2c, 0x90, 0x8d, 0xde, 0xe6,
];
#[test]
fn test_parse_hash() {
assert_eq!(
parse_hash(b"337e24d726fd728f92957a2c908ddee6"),
Ok((EMPTY, MD5Sum(ARR)))
);
}
#[test]
fn test_entry() {
let mut data = Vec::new();
data.extend_from_slice(&u32::to_le_bytes(1234)); data.extend_from_slice(&i32::to_le_bytes(100)); data.extend_from_slice(&i32::to_le_bytes(200)); data.extend_from_slice(&u32::to_le_bytes(4444)); data.extend_from_slice(b"337e24d726fd728f92957a2c908ddee6");
data.extend_from_slice(&[0, 0, 0, 0]);
data.extend_from_slice(&u32::to_le_bytes(8888)); data.extend_from_slice(b"447e24d726fd728f92957a2c908ddee6");
data.extend_from_slice(&[0, 0, 0, 0]);
data.extend_from_slice(&u32::to_le_bytes(5678)); data.extend_from_slice(&[1, 0, 0, 0]); assert_eq!(
parse_pk_entry(&data),
Ok((
EMPTY,
PKEntry {
crc: CRC::from_raw(1234),
left: 100,
right: 200,
data: PKEntryData {
meta: FileMetaPair {
raw: FileMeta {
size: 4444,
hash: MD5Sum(ARR)
},
compressed: FileMeta {
size: 8888,
hash: MD5Sum(ARR2)
}
},
file_data_addr: 5678,
is_compressed: 1,
}
}
))
);
}
}