const SIG_DDM: &[u8; 2] = b"ER";
const SIG_PM: &[u8; 2] = b"PM";
const MAX_PARTITIONS: u32 = 256;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ApmPartition {
pub name: String,
pub type_name: String,
pub start_block: u32,
pub block_count: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ApplePartitionMap {
pub block_size: u32,
pub partitions: Vec<ApmPartition>,
}
impl ApplePartitionMap {
#[must_use]
pub fn hfs_partition(&self) -> Option<&ApmPartition> {
self.partitions
.iter()
.find(|p| p.type_name.starts_with("Apple_HFS"))
}
}
#[must_use]
pub fn parse(data: &[u8]) -> Option<ApplePartitionMap> {
if data.len() < 512 || &data[0..2] != SIG_DDM {
return None;
}
let block_size = u32::from(be16(&data[2..4]));
let bs = block_size as usize;
if bs == 0 {
return None;
}
let first = bs;
if data.len() < first + 8 || &data[first..first + 2] != SIG_PM {
return None;
}
let map_count = be32(&data[first + 4..first + 8]).min(MAX_PARTITIONS);
let mut partitions = Vec::new();
for i in 0..map_count {
let off = bs * (1 + i as usize);
if data.len() < off + 80 || &data[off..off + 2] != SIG_PM {
break;
}
partitions.push(ApmPartition {
start_block: be32(&data[off + 8..off + 12]),
block_count: be32(&data[off + 12..off + 16]),
name: cstr(&data[off + 16..off + 48]),
type_name: cstr(&data[off + 48..off + 80]),
});
}
Some(ApplePartitionMap {
block_size,
partitions,
})
}
fn cstr(bytes: &[u8]) -> String {
let end = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
bytes[..end].iter().map(|&b| b as char).collect()
}
fn be16(b: &[u8]) -> u16 {
u16::from_be_bytes([b[0], b[1]])
}
fn be32(b: &[u8]) -> u32 {
u32::from_be_bytes([b[0], b[1], b[2], b[3]])
}