use std::io::{Read, Seek, Write};
use cfb::CompoundFile;
use crate::error::{Error, Result};
const HDR_SECTOR_SIZE_OFF: usize = 30;
const HDR_FIRST_MINI_FAT_OFF: usize = 60;
const HDR_NUM_MINI_FAT_OFF: usize = 64;
const HDR_DIFAT_OFF: usize = 76;
const MAXREGSECT: u32 = 0xFFFFFFFA;
const FREESECT: u32 = 0xFFFFFFFF;
const ENDOFCHAIN: u32 = 0xFFFFFFFE;
pub fn write_stream<F: Read + Write + Seek>(
comp: &mut CompoundFile<F>,
path: &str,
data: &[u8],
) -> Result<()> {
let mut stream = comp.create_stream(path).map_err(|e| {
Error::ppt97(format!(
"write_stream: create_stream {} failed: {}",
path, e
))
})?;
stream
.write_all(data)
.map_err(|e| Error::ppt97(format!("write_stream: write {} failed: {}", path, e)))?;
stream
.flush()
.map_err(|e| Error::ppt97(format!("write_stream: flush {} failed: {}", path, e)))?;
drop(stream);
Ok(())
}
pub fn fix_mini_fat(#[allow(clippy::ptr_arg)] data: &mut Vec<u8>) -> Result<()> {
if data.len() < 512 {
return Err(Error::ppt97(
"fix_mini_fat: data too short (less than 512 bytes, not a valid OLE2 container)"
.to_string(),
));
}
let sector_shift =
u16::from_le_bytes([data[HDR_SECTOR_SIZE_OFF], data[HDR_SECTOR_SIZE_OFF + 1]]);
let sector_size = 1usize << sector_shift;
let entries_per_sector = sector_size / 4;
let first_mini_fat_sector = u32::from_le_bytes([
data[HDR_FIRST_MINI_FAT_OFF],
data[HDR_FIRST_MINI_FAT_OFF + 1],
data[HDR_FIRST_MINI_FAT_OFF + 2],
data[HDR_FIRST_MINI_FAT_OFF + 3],
]);
let num_mini_fat_sectors = u32::from_le_bytes([
data[HDR_NUM_MINI_FAT_OFF],
data[HDR_NUM_MINI_FAT_OFF + 1],
data[HDR_NUM_MINI_FAT_OFF + 2],
data[HDR_NUM_MINI_FAT_OFF + 3],
]);
if num_mini_fat_sectors <= 1 || first_mini_fat_sector >= MAXREGSECT {
return Ok(());
}
let mut difat: Vec<u32> = Vec::new();
for i in 0..109 {
let off = HDR_DIFAT_OFF + i * 4;
let val = u32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]]);
if val != FREESECT {
difat.push(val);
}
}
let mut fat: Vec<u32> = Vec::new();
for sec in &difat {
let offset = 512 + (*sec as usize) * sector_size;
if offset + sector_size > data.len() {
return Err(Error::ppt97(format!(
"fix_mini_fat: FAT sector {} out of range (offset {}, data len {})",
sec,
offset,
data.len()
)));
}
for j in 0..entries_per_sector {
let off = offset + j * 4;
let val = u32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]]);
fat.push(val);
}
}
let mut mini_fat_chain: Vec<u32> = Vec::new();
let mut sec = first_mini_fat_sector;
while sec != ENDOFCHAIN && sec < MAXREGSECT && (sec as usize) < fat.len() {
mini_fat_chain.push(sec);
let next = fat[sec as usize];
if next == ENDOFCHAIN {
break;
}
sec = next;
}
if mini_fat_chain.len() <= 1 {
return Ok(()); }
let mut all_free = true;
for &s in &mini_fat_chain[1..] {
let offset = 512 + (s as usize) * sector_size;
if offset + sector_size > data.len() {
all_free = false;
break;
}
for j in 0..entries_per_sector {
let off = offset + j * 4;
let val = u32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]]);
if val != FREESECT {
all_free = false;
break;
}
}
if !all_free {
break;
}
}
if !all_free {
return Ok(()); }
let first_sec = mini_fat_chain[0];
let fat_sector_idx = (first_sec as usize) / entries_per_sector;
let entry_in_sector = (first_sec as usize) % entries_per_sector;
if fat_sector_idx >= difat.len() {
return Err(Error::ppt97(format!(
"fix_mini_fat: cannot locate first mini FAT sector {} in DIFAT",
first_sec
)));
}
let fat_sector = difat[fat_sector_idx];
let fat_entry_offset = 512 + (fat_sector as usize) * sector_size + entry_in_sector * 4;
if fat_entry_offset + 4 > data.len() {
return Err(Error::ppt97(format!(
"fix_mini_fat: FAT entry offset {} out of range",
fat_entry_offset
)));
}
data[fat_entry_offset..fat_entry_offset + 4].copy_from_slice(&ENDOFCHAIN.to_le_bytes());
for &s in &mini_fat_chain[1..] {
let fat_sector_idx = (s as usize) / entries_per_sector;
let entry_in_sector = (s as usize) % entries_per_sector;
if fat_sector_idx >= difat.len() {
continue;
}
let fat_sector = difat[fat_sector_idx];
let fat_entry_offset = 512 + (fat_sector as usize) * sector_size + entry_in_sector * 4;
if fat_entry_offset + 4 > data.len() {
continue;
}
data[fat_entry_offset..fat_entry_offset + 4].copy_from_slice(&FREESECT.to_le_bytes());
}
data[HDR_NUM_MINI_FAT_OFF..HDR_NUM_MINI_FAT_OFF + 4].copy_from_slice(&1u32.to_le_bytes());
Ok(())
}