use crate::ole::ppt::package::{PptError, Result};
use crate::ole::ppt::records::PptRecord;
use crate::ole::consts::PptRecordType;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct PersistPtrHolder {
slide_locations: HashMap<u32, u32>,
}
impl PersistPtrHolder {
pub fn parse(record: &PptRecord) -> Result<Self> {
if record.record_type != PptRecordType::PersistPtrHolder {
return Err(PptError::InvalidFormat(
format!("Expected PersistPtrHolder, got {:?}", record.record_type)
));
}
Self::parse_data(&record.data)
}
fn parse_data(data: &[u8]) -> Result<Self> {
let estimated_capacity = data.len() / 8;
let mut slide_locations = HashMap::with_capacity(estimated_capacity);
let mut chunks = data.chunks_exact(4);
while let Some(info_bytes) = chunks.next() {
let info = u32::from_le_bytes(info_bytes.try_into().unwrap());
let base_persist_id = info & 0x000F_FFFF;
let entry_count = (info >> 20) & 0x0FFF;
for i in 0..entry_count {
if let Some(offset_bytes) = chunks.next() {
let offset = u32::from_le_bytes(offset_bytes.try_into().unwrap());
slide_locations.insert(base_persist_id + i, offset);
} else {
break;
}
}
}
Ok(Self { slide_locations })
}
#[inline]
pub fn get_slide_location(&self, persist_id: u32) -> Option<u32> {
self.slide_locations.get(&persist_id).copied()
}
pub fn get_known_slide_ids(&self) -> Vec<u32> {
let mut ids: Vec<u32> = self.slide_locations.keys().copied().collect();
ids.sort_unstable();
ids
}
#[inline]
pub fn slide_locations(&self) -> &HashMap<u32, u32> {
&self.slide_locations
}
#[inline]
pub fn slide_count(&self) -> usize {
self.slide_locations.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.slide_locations.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_persist_ptr_holder_parsing() {
let mut data = Vec::new();
data.extend_from_slice(&0x00200000u32.to_le_bytes()); data.extend_from_slice(&1000u32.to_le_bytes()); data.extend_from_slice(&2000u32.to_le_bytes());
let record = PptRecord {
record_type: PptRecordType::PersistPtrHolder,
record_type_raw: 6001,
version: 0,
instance: 0,
data_length: data.len() as u32,
data,
children: vec![],
};
let holder = PersistPtrHolder::parse(&record).unwrap();
assert_eq!(holder.slide_count(), 2);
assert_eq!(holder.get_slide_location(0), Some(1000));
assert_eq!(holder.get_slide_location(1), Some(2000));
assert_eq!(holder.get_slide_location(2), None);
}
#[test]
fn test_persist_ptr_holder_multiple_groups() {
let mut data = Vec::new();
data.extend_from_slice(&0x00200000u32.to_le_bytes()); data.extend_from_slice(&1000u32.to_le_bytes());
data.extend_from_slice(&2000u32.to_le_bytes());
data.extend_from_slice(&0x0010000Au32.to_le_bytes()); data.extend_from_slice(&3000u32.to_le_bytes());
let record = PptRecord {
record_type: PptRecordType::PersistPtrHolder,
record_type_raw: 6001,
version: 0,
instance: 0,
data_length: data.len() as u32,
data,
children: vec![],
};
let holder = PersistPtrHolder::parse(&record).unwrap();
assert_eq!(holder.slide_count(), 3);
assert_eq!(holder.get_slide_location(0), Some(1000));
assert_eq!(holder.get_slide_location(1), Some(2000));
assert_eq!(holder.get_slide_location(10), Some(3000));
}
}