use std::io::{self, Read, Seek, SeekFrom};
use crate::IsoError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SectorMode {
Iso2048,
Raw2352,
}
impl SectorMode {
pub fn detect<R: Read + Seek>(reader: &mut R) -> Result<Self, IsoError> {
if probe_cd001(reader, 16 * 2048 + 1)? {
return Ok(Self::Iso2048);
}
if probe_cd001(reader, 16 * 2352 + 16 + 1)? {
if has_sync_pattern(reader, 0)? {
return Ok(Self::Raw2352);
}
}
Err(IsoError::NotAnIso)
}
pub const fn physical_sector_size(self) -> u64 {
match self {
Self::Iso2048 => 2048,
Self::Raw2352 => 2352,
}
}
pub const fn data_offset(self) -> u64 {
match self {
Self::Iso2048 => 0,
Self::Raw2352 => 16, }
}
pub fn user_data_pos(self, lba: u64) -> u64 {
lba * self.physical_sector_size() + self.data_offset()
}
}
pub fn read_sector_data<R: Read + Seek>(
reader: &mut R,
mode: SectorMode,
lba: u64,
buf: &mut [u8],
) -> io::Result<()> {
debug_assert!(
buf.len() <= 2048,
"cannot read more than one sector at a time"
);
reader.seek(SeekFrom::Start(mode.user_data_pos(lba)))?;
reader.read_exact(buf)
}
fn probe_cd001<R: Read + Seek>(reader: &mut R, pos: u64) -> io::Result<bool> {
let mut sig = [0u8; 5];
reader.seek(SeekFrom::Start(pos))?;
match reader.read_exact(&mut sig) {
Ok(()) => Ok(&sig == b"CD001"),
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => Ok(false),
Err(e) => Err(e),
}
}
fn has_sync_pattern<R: Read + Seek>(reader: &mut R, sector_start: u64) -> io::Result<bool> {
const SYNC: [u8; 12] = [
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
];
let mut buf = [0u8; 12];
reader.seek(SeekFrom::Start(sector_start))?;
match reader.read_exact(&mut buf) {
Ok(()) => Ok(buf == SYNC),
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => Ok(false),
Err(e) => Err(e),
}
}