use crate::structures::common::{self, StructureError};
const LZO_CHECKSUM_SIZE: usize = 4;
#[derive(Debug, Default, Clone)]
pub struct LZOPFileHeader {
pub header_size: usize,
pub block_checksum_present: bool,
}
pub fn parse_lzop_file_header(lzop_data: &[u8]) -> Result<LZOPFileHeader, StructureError> {
const LZO_MAX_VERSION: usize = 0x1040;
const LZO_HEADER_SIZE_P1: usize = 21;
const LZO_HEADER_SIZE_P2: usize = 13;
const FILTER_SIZE: usize = 4;
const FLAG_FILTER: usize = 0x000_00800;
const FLAG_CRC32_C: usize = 0x0000_0200;
const FLAG_ADLER32_C: usize = 0x0000_0002;
let lzo_structure_p1 = vec![
("magic_p1", "u8"),
("magic_p2", "u64"),
("version", "u16"),
("lib_version", "u16"),
("version_needed", "u16"),
("method", "u8"),
("level", "u8"),
("flags", "u32"),
];
let lzo_structure_p2 = vec![
("mode", "u32"),
("mtime", "u32"),
("gmt_diff", "u32"),
("file_name_length", "u8"),
];
let allowed_methods: Vec<usize> = vec![1, 2, 3];
let mut lzop_info = LZOPFileHeader {
..Default::default()
};
if let Ok(lzo_header_p1) = common::parse(lzop_data, &lzo_structure_p1, "big") {
if allowed_methods.contains(&lzo_header_p1["method"]) {
if lzo_header_p1["version"] <= LZO_MAX_VERSION
&& lzo_header_p1["version"] >= lzo_header_p1["version_needed"]
{
let mut header_p2_start: usize = LZO_HEADER_SIZE_P1;
if (lzo_header_p1["flags"] & FLAG_FILTER) != 0 {
header_p2_start += FILTER_SIZE;
}
let header_p2_end: usize = header_p2_start + LZO_HEADER_SIZE_P2;
if let Some(header_p2_data) = lzop_data.get(header_p2_start..header_p2_end) {
if let Ok(lzo_header_p2) =
common::parse(header_p2_data, &lzo_structure_p2, "big")
{
lzop_info.header_size =
header_p2_end + lzo_header_p2["file_name_length"] + LZO_CHECKSUM_SIZE;
lzop_info.block_checksum_present =
(lzo_header_p1["flags"] & FLAG_ADLER32_C & FLAG_CRC32_C) != 0;
if lzop_info.header_size <= lzop_data.len() {
return Ok(lzop_info);
}
}
}
}
}
}
Err(StructureError)
}
#[derive(Debug, Default, Clone)]
pub struct LZOPBlockHeader {
pub header_size: usize,
pub compressed_size: usize,
pub uncompressed_size: usize,
pub checksum_size: usize,
}
pub fn parse_lzop_block_header(
lzo_data: &[u8],
compressed_checksum_present: bool,
) -> Result<LZOPBlockHeader, StructureError> {
const BLOCK_HEADER_SIZE: usize = 12;
const MAX_UNCOMPRESSED_BLOCK_SIZE: usize = 64 * 1024 * 1024;
let block_structure = vec![
("uncompressed_size", "u32"),
("compressed_size", "u32"),
("uncompressed_checksum", "u32"),
];
if let Ok(block_header) = common::parse(lzo_data, &block_structure, "big") {
if block_header["compressed_size"] != 0
&& block_header["uncompressed_size"] != 0
&& block_header["uncompressed_checksum"] != 0
&& block_header["uncompressed_size"] <= MAX_UNCOMPRESSED_BLOCK_SIZE
{
let mut block_hdr_info = LZOPBlockHeader {
..Default::default()
};
block_hdr_info.header_size = BLOCK_HEADER_SIZE;
block_hdr_info.compressed_size = block_header["compressed_size"];
block_hdr_info.uncompressed_size = block_header["uncompressed_size"];
if compressed_checksum_present {
block_hdr_info.checksum_size = LZO_CHECKSUM_SIZE;
}
return Ok(block_hdr_info);
}
}
Err(StructureError)
}
pub fn parse_lzop_eof_marker(eof_data: &[u8]) -> Result<usize, StructureError> {
const EOF_MARKER: usize = 0;
const EOF_MARKER_SIZE: usize = 4;
let eof_structure = vec![("marker", "u32")];
if let Ok(eof_marker) = common::parse(eof_data, &eof_structure, "big") {
if eof_marker["marker"] == EOF_MARKER {
return Ok(EOF_MARKER_SIZE);
}
}
Err(StructureError)
}