pub mod entry;
pub mod filesystem;
pub mod iso9660;
pub mod hfs;
pub mod hfsplus;
pub mod mac_alias;
pub mod efs;
pub use efs::EfsFilesystem;
pub use entry::{EntryType, FileEntry};
pub use filesystem::{Filesystem, FilesystemError};
pub use hfs::HfsFilesystem;
pub use hfsplus::HfsPlusFilesystem;
pub use iso9660::Iso9660Filesystem;
use crate::detect::DiscImageInfo;
use crate::error::OpticaldiscsError;
use crate::formats::{DiscFormat, FilesystemType};
use crate::sector_reader::SectorReader;
pub fn open_disc_filesystem(info: &DiscImageInfo) -> Result<Box<dyn Filesystem>, FilesystemError> {
let mut reader = open_sector_reader(info)?;
match info.filesystem {
FilesystemType::Iso9660 => Ok(Box::new(Iso9660Filesystem::new(reader)?)),
FilesystemType::Efs => {
let partition_offset = info.efs_partition_offset.ok_or_else(|| {
FilesystemError::InvalidData(
"EFS detected but partition offset not recorded".into(),
)
})?;
Ok(Box::new(EfsFilesystem::new(reader, partition_offset)?))
}
FilesystemType::Hfs | FilesystemType::HfsPlus => {
let raw_offset = crate::apm::find_hfs_partition_offset(reader.as_mut()).unwrap_or(0);
let (resolved_fs, resolved_offset) =
crate::detect::resolve_apple_hfs(reader.as_mut(), raw_offset);
match resolved_fs {
FilesystemType::Hfs => Ok(Box::new(HfsFilesystem::new(reader, raw_offset)?)),
FilesystemType::HfsPlus => {
Ok(Box::new(HfsPlusFilesystem::new(reader, resolved_offset)?))
}
_ => Err(FilesystemError::Unsupported),
}
}
_ => Err(FilesystemError::Unsupported),
}
}
fn open_sector_reader(info: &DiscImageInfo) -> Result<Box<dyn SectorReader>, FilesystemError> {
let path = &info.path;
match info.format {
DiscFormat::Iso => {
let reader = crate::sector_reader::IsoSectorReader::new(path).map_err(disc_err)?;
Ok(Box::new(reader))
}
DiscFormat::BinCue => {
let cue_path = resolve_cue_path(path)?;
let tracks = crate::bincue::parse_cue_tracks(&cue_path)
.map_err(|e| FilesystemError::InvalidData(e.to_string()))?;
let data_track = tracks
.iter()
.find(|t| t.is_data())
.ok_or_else(|| FilesystemError::InvalidData("no data track in CUE sheet".into()))?
.clone();
let reader =
crate::sector_reader::BinCueSectorReader::open(&data_track).map_err(disc_err)?;
Ok(Box::new(reader))
}
DiscFormat::Chd => {
let chd_info = crate::chd::open_chd(path).map_err(disc_err)?;
let track = chd_info
.find_first_data_track()
.ok_or(FilesystemError::Unsupported)?
.clone();
let reader =
crate::sector_reader::ChdSectorReader::open(path, &track).map_err(disc_err)?;
Ok(Box::new(reader))
}
DiscFormat::MdsMdf => Err(FilesystemError::Unsupported),
}
}
fn resolve_cue_path(path: &std::path::Path) -> Result<std::path::PathBuf, FilesystemError> {
let is_bin = path
.extension()
.and_then(|e| e.to_str())
.map(str::to_ascii_lowercase)
.as_deref()
== Some("bin");
if is_bin {
let stem = path.file_stem().unwrap_or_default();
let cue = path.with_file_name(format!("{}.cue", stem.to_string_lossy()));
if cue.exists() {
Ok(cue)
} else {
Err(FilesystemError::NotFound(format!(
"no matching .cue found for {}",
path.display()
)))
}
} else {
Ok(path.to_path_buf())
}
}
fn disc_err(e: OpticaldiscsError) -> FilesystemError {
match e {
OpticaldiscsError::Io(io_err) => FilesystemError::Io(io_err),
e => FilesystemError::InvalidData(e.to_string()),
}
}