use crate::error::{Error, Result};
pub const RT_MAIN_MASTER: u16 = 0x03F8;
pub const RT_PPDRAWING: u16 = 0x040C;
pub const RT_CURRENT_USER_ATOM: u16 = 0x0FF6;
pub const RT_USER_EDIT_ATOM: u16 = 0x0FF5;
pub const RT_PERSIST_DIRECTORY_ATOM: u16 = 0x1772;
pub fn parse_record_header(data: &[u8], offset: usize) -> Result<(u8, u16, u16, u32)> {
if offset + 8 > data.len() {
return Err(Error::ppt97(format!(
"record header parse: offset {} + 8 out of range (data len {})",
offset,
data.len()
)));
}
let ver_inst = u16::from_le_bytes([data[offset], data[offset + 1]]);
let rec_type = u16::from_le_bytes([data[offset + 2], data[offset + 3]]);
let rec_len = u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
let ver = (ver_inst & 0x0F) as u8;
let inst = (ver_inst >> 4) & 0x0FFF;
Ok((ver, inst, rec_type, rec_len))
}
pub fn read_u32_le(data: &[u8], offset: usize) -> Result<u32> {
if offset + 4 > data.len() {
return Err(Error::ppt97(format!(
"read_u32_le: offset {} + 4 out of range (data len {})",
offset,
data.len()
)));
}
Ok(u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]))
}
pub fn write_u32_le(data: &mut [u8], offset: usize, val: u32) -> Result<()> {
if offset + 4 > data.len() {
return Err(Error::ppt97(format!(
"write_u32_le: offset {} + 4 out of range (data len {})",
offset,
data.len()
)));
}
let bytes = val.to_le_bytes();
data[offset..offset + 4].copy_from_slice(&bytes);
Ok(())
}
pub fn parse_persist_directory(data: &[u8], offset: usize) -> Result<Vec<(u32, u32)>> {
let (_, _, rec_type, rec_len) = parse_record_header(data, offset)?;
if rec_type != RT_PERSIST_DIRECTORY_ATOM {
return Err(Error::ppt97(format!(
"persist directory parse: expected type 0x{:04X}, got 0x{:04X}",
RT_PERSIST_DIRECTORY_ATOM, rec_type
)));
}
let pd_start = offset + 8;
let pd_end = pd_start + rec_len as usize;
if pd_end > data.len() {
return Err(Error::ppt97(format!(
"persist directory parse: data out of range (start {}, end {}, data len {})",
pd_start,
pd_end,
data.len()
)));
}
let pd_data = &data[pd_start..pd_end];
let mut entries = Vec::new();
let mut pos = 0usize;
while pos + 4 <= pd_data.len() {
let entry = u32::from_le_bytes([
pd_data[pos],
pd_data[pos + 1],
pd_data[pos + 2],
pd_data[pos + 3],
]);
let persist_id = entry & 0xFFFFF;
let c_persist = (entry >> 20) & 0xFFF;
pos += 4;
for j in 0..c_persist {
if pos + 4 > pd_data.len() {
return Err(Error::ppt97(format!(
"persist directory parse: entry (pid={}, cPersist={}) offset out of range",
persist_id, c_persist
)));
}
let persist_offset = u32::from_le_bytes([
pd_data[pos],
pd_data[pos + 1],
pd_data[pos + 2],
pd_data[pos + 3],
]);
entries.push((persist_id + j, persist_offset));
pos += 4;
}
}
Ok(entries)
}
pub fn find_main_masters(data: &[u8]) -> Result<Vec<usize>> {
let mut masters = Vec::new();
let mut pos = 0usize;
while pos + 8 <= data.len() {
let (ver, _, rec_type, rec_len) = parse_record_header(data, pos)?;
let is_container = ver == 0xF;
let total_len = 8usize + rec_len as usize;
if is_container && rec_type == RT_MAIN_MASTER {
masters.push(pos);
}
if total_len == 0 {
break;
}
pos += total_len;
if !is_container && rec_len == 0 {
break;
}
}
Ok(masters)
}
pub fn find_ppdrawing_in_master(data: &[u8], master_offset: usize) -> Result<Option<usize>> {
let (_, _, _, master_len) = parse_record_header(data, master_offset)?;
let master_end = master_offset + 8 + master_len as usize;
let mut pos = master_offset + 8;
while pos + 8 <= master_end {
let (ver, _, rec_type, rec_len) = parse_record_header(data, pos)?;
let is_container = ver == 0xF;
let total_len = 8usize + rec_len as usize;
if is_container && rec_type == RT_PPDRAWING {
return Ok(Some(pos));
}
if total_len == 0 {
break;
}
pos += total_len;
if !is_container && rec_len == 0 {
break;
}
}
Ok(None)
}