use crate::{
io::{ReadSeek, ReadWriteSeek},
DiskImage,
DiskImageError,
DiskImageFileFormat,
LoadingCallback,
};
use bitflags::bitflags;
pub mod compression;
pub mod f86;
pub mod hfe;
pub mod imd;
pub mod kryoflux;
mod mfi;
pub mod mfm;
pub mod pfi;
pub mod pri;
pub mod psi;
pub mod raw;
pub mod scp;
pub mod tc;
pub mod td0;
bitflags! {
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[rustfmt::skip]
pub struct FormatCaps: u32 {
const CAP_VARIABLE_SPT = 0b0000_0000_0000_0001; const CAP_VARIABLE_SSPT = 0b0000_0000_0000_0010; const CAP_ADDRESS_CRC = 0b0000_0000_0000_0100; const CAP_DATA_CRC = 0b0000_0000_0000_1000; const CAP_DATA_DELETED = 0b0000_0000_0001_0000; const CAP_SID_OVERRIDE = 0b0000_0000_0010_0000; const CAP_COMMENT = 0b0000_0000_0100_0000; const CAP_TRACK_ENCODING = 0b0000_0000_1000_0000; const CAP_TRACK_DATA_RATE = 0b0000_0001_0000_0000; const CAP_WEAK_BITS = 0b0000_0010_0000_0000; const CAP_HOLES = 0b0000_0100_0000_0000; const CAP_ENCODING_FM = 0b0000_1000_0000_0000; const CAP_ENCODING_MFM = 0b0001_0000_0000_0000; const CAP_ENCODING_GCR = 0b0010_0000_0000_0000; const CAP_NO_DAM = 0b0100_0000_0000_0000; }
}
pub fn bitstream_flags() -> FormatCaps {
FormatCaps::CAP_VARIABLE_SPT
| FormatCaps::CAP_VARIABLE_SSPT
| FormatCaps::CAP_ADDRESS_CRC
| FormatCaps::CAP_DATA_CRC
| FormatCaps::CAP_DATA_DELETED
| FormatCaps::CAP_SID_OVERRIDE
| FormatCaps::CAP_NO_DAM
}
pub enum ParserWriteCompatibility {
Ok,
DataLoss,
Incompatible,
UnsupportedFormat,
}
pub(crate) const IMAGE_FORMATS: [DiskImageFileFormat; 12] = [
DiskImageFileFormat::ImageDisk,
DiskImageFileFormat::TeleDisk,
DiskImageFileFormat::PceSectorImage,
DiskImageFileFormat::PceBitstreamImage,
DiskImageFileFormat::MfmBitstreamImage,
DiskImageFileFormat::HfeImage,
DiskImageFileFormat::F86Image,
DiskImageFileFormat::TransCopyImage,
DiskImageFileFormat::SuperCardPro,
DiskImageFileFormat::MameFloppyImage,
DiskImageFileFormat::KryofluxStream,
DiskImageFileFormat::RawSectorImage,
];
pub fn supported_extensions() -> Vec<&'static str> {
IMAGE_FORMATS.iter().flat_map(|f| f.extensions()).collect()
}
pub fn format_from_ext(ext: &str) -> Option<DiskImageFileFormat> {
for format in IMAGE_FORMATS.iter() {
if format.extensions().contains(&ext.to_lowercase().as_str()) {
return Some(*format);
}
}
None
}
pub fn formats_from_caps(caps: FormatCaps) -> Vec<(DiskImageFileFormat, Vec<String>)> {
let format_vec = IMAGE_FORMATS
.iter()
.filter(|f| caps.is_empty() || f.capabilities().contains(caps))
.map(|f| (*f, f.extensions().iter().map(|s| s.to_string()).collect()))
.collect();
format_vec
}
pub fn filter_writable(image: &DiskImage, formats: Vec<DiskImageFileFormat>) -> Vec<DiskImageFileFormat> {
formats
.into_iter()
.filter(|f| matches!(f.can_write(image), ParserWriteCompatibility::Ok))
.collect()
}
pub trait ImageParser {
fn capabilities(&self) -> FormatCaps;
fn detect<RWS: ReadSeek>(&self, image_buf: RWS) -> bool;
fn extensions(&self) -> Vec<&'static str>;
fn load_image<RWS: ReadSeek>(
&self,
read_buf: RWS,
image: &mut DiskImage,
callback: Option<LoadingCallback>,
) -> Result<(), DiskImageError>;
fn can_write(&self, image: &DiskImage) -> ParserWriteCompatibility;
fn save_image<RWS: ReadWriteSeek>(self, image: &mut DiskImage, image_buf: &mut RWS) -> Result<(), DiskImageError>;
}
impl ImageParser for DiskImageFileFormat {
fn capabilities(&self) -> FormatCaps {
match self {
DiskImageFileFormat::RawSectorImage => raw::RawFormat::capabilities(),
DiskImageFileFormat::ImageDisk => imd::ImdFormat::capabilities(),
DiskImageFileFormat::TeleDisk => td0::Td0Format::capabilities(),
DiskImageFileFormat::PceSectorImage => psi::PsiFormat::capabilities(),
DiskImageFileFormat::PceBitstreamImage => pri::PriFormat::capabilities(),
DiskImageFileFormat::MfmBitstreamImage => mfm::MfmFormat::capabilities(),
DiskImageFileFormat::HfeImage => hfe::HfeFormat::capabilities(),
DiskImageFileFormat::F86Image => f86::F86Format::capabilities(),
DiskImageFileFormat::TransCopyImage => tc::TCFormat::capabilities(),
DiskImageFileFormat::SuperCardPro => scp::ScpFormat::capabilities(),
DiskImageFileFormat::PceFluxImage => pfi::PfiFormat::capabilities(),
DiskImageFileFormat::KryofluxStream => kryoflux::KfxFormat::capabilities(),
DiskImageFileFormat::MameFloppyImage => mfi::MfiFormat::capabilities(),
}
}
fn detect<RWS: ReadSeek>(&self, image_buf: RWS) -> bool {
match self {
DiskImageFileFormat::RawSectorImage => raw::RawFormat::detect(image_buf),
DiskImageFileFormat::ImageDisk => imd::ImdFormat::detect(image_buf),
DiskImageFileFormat::TeleDisk => td0::Td0Format::detect(image_buf),
DiskImageFileFormat::PceSectorImage => psi::PsiFormat::detect(image_buf),
DiskImageFileFormat::PceBitstreamImage => pri::PriFormat::detect(image_buf),
DiskImageFileFormat::MfmBitstreamImage => mfm::MfmFormat::detect(image_buf),
DiskImageFileFormat::HfeImage => hfe::HfeFormat::detect(image_buf),
DiskImageFileFormat::F86Image => f86::F86Format::detect(image_buf),
DiskImageFileFormat::TransCopyImage => tc::TCFormat::detect(image_buf),
DiskImageFileFormat::SuperCardPro => scp::ScpFormat::detect(image_buf),
DiskImageFileFormat::PceFluxImage => pfi::PfiFormat::detect(image_buf),
DiskImageFileFormat::KryofluxStream => kryoflux::KfxFormat::detect(image_buf),
DiskImageFileFormat::MameFloppyImage => mfi::MfiFormat::detect(image_buf),
}
}
fn extensions(&self) -> Vec<&'static str> {
match self {
DiskImageFileFormat::RawSectorImage => raw::RawFormat::extensions(),
DiskImageFileFormat::ImageDisk => imd::ImdFormat::extensions(),
DiskImageFileFormat::TeleDisk => td0::Td0Format::extensions(),
DiskImageFileFormat::PceSectorImage => psi::PsiFormat::extensions(),
DiskImageFileFormat::PceBitstreamImage => pri::PriFormat::extensions(),
DiskImageFileFormat::MfmBitstreamImage => mfm::MfmFormat::extensions(),
DiskImageFileFormat::HfeImage => hfe::HfeFormat::extensions(),
DiskImageFileFormat::F86Image => f86::F86Format::extensions(),
DiskImageFileFormat::TransCopyImage => tc::TCFormat::extensions(),
DiskImageFileFormat::SuperCardPro => scp::ScpFormat::extensions(),
DiskImageFileFormat::PceFluxImage => pfi::PfiFormat::extensions(),
DiskImageFileFormat::KryofluxStream => kryoflux::KfxFormat::extensions(),
DiskImageFileFormat::MameFloppyImage => mfi::MfiFormat::extensions(),
}
}
fn load_image<RWS: ReadSeek>(
&self,
read_buf: RWS,
image: &mut DiskImage,
callback: Option<LoadingCallback>,
) -> Result<(), DiskImageError> {
match self {
DiskImageFileFormat::RawSectorImage => raw::RawFormat::load_image(read_buf, image, callback),
DiskImageFileFormat::ImageDisk => imd::ImdFormat::load_image(read_buf, image, callback),
DiskImageFileFormat::TeleDisk => td0::Td0Format::load_image(read_buf, image, callback),
DiskImageFileFormat::PceSectorImage => psi::PsiFormat::load_image(read_buf, image, callback),
DiskImageFileFormat::PceBitstreamImage => pri::PriFormat::load_image(read_buf, image, callback),
DiskImageFileFormat::MfmBitstreamImage => mfm::MfmFormat::load_image(read_buf, image, callback),
DiskImageFileFormat::HfeImage => hfe::HfeFormat::load_image(read_buf, image, callback),
DiskImageFileFormat::F86Image => f86::F86Format::load_image(read_buf, image, callback),
DiskImageFileFormat::TransCopyImage => tc::TCFormat::load_image(read_buf, image, callback),
DiskImageFileFormat::SuperCardPro => scp::ScpFormat::load_image(read_buf, image, callback),
DiskImageFileFormat::PceFluxImage => pfi::PfiFormat::load_image(read_buf, image, callback),
DiskImageFileFormat::KryofluxStream => kryoflux::KfxFormat::load_image(read_buf, image, callback),
DiskImageFileFormat::MameFloppyImage => mfi::MfiFormat::load_image(read_buf, image, callback),
}
}
fn can_write(&self, image: &DiskImage) -> ParserWriteCompatibility {
match self {
DiskImageFileFormat::RawSectorImage => raw::RawFormat::can_write(image),
DiskImageFileFormat::ImageDisk => imd::ImdFormat::can_write(image),
DiskImageFileFormat::TeleDisk => td0::Td0Format::can_write(image),
DiskImageFileFormat::PceSectorImage => psi::PsiFormat::can_write(image),
DiskImageFileFormat::PceBitstreamImage => pri::PriFormat::can_write(image),
DiskImageFileFormat::MfmBitstreamImage => mfm::MfmFormat::can_write(image),
DiskImageFileFormat::HfeImage => hfe::HfeFormat::can_write(image),
DiskImageFileFormat::F86Image => f86::F86Format::can_write(image),
DiskImageFileFormat::TransCopyImage => tc::TCFormat::can_write(image),
DiskImageFileFormat::SuperCardPro => scp::ScpFormat::can_write(image),
DiskImageFileFormat::PceFluxImage => pfi::PfiFormat::can_write(image),
DiskImageFileFormat::KryofluxStream => kryoflux::KfxFormat::can_write(image),
DiskImageFileFormat::MameFloppyImage => mfi::MfiFormat::can_write(image),
}
}
fn save_image<RWS: ReadWriteSeek>(self, image: &mut DiskImage, write_buf: &mut RWS) -> Result<(), DiskImageError> {
match self {
DiskImageFileFormat::RawSectorImage => raw::RawFormat::save_image(image, write_buf),
DiskImageFileFormat::ImageDisk => imd::ImdFormat::save_image(image, write_buf),
DiskImageFileFormat::TeleDisk => td0::Td0Format::save_image(image, write_buf),
DiskImageFileFormat::PceSectorImage => psi::PsiFormat::save_image(image, write_buf),
DiskImageFileFormat::PceBitstreamImage => pri::PriFormat::save_image(image, write_buf),
DiskImageFileFormat::MfmBitstreamImage => mfm::MfmFormat::save_image(image, write_buf),
DiskImageFileFormat::HfeImage => hfe::HfeFormat::save_image(image, write_buf),
DiskImageFileFormat::F86Image => f86::F86Format::save_image(image, write_buf),
DiskImageFileFormat::TransCopyImage => tc::TCFormat::save_image(image, write_buf),
DiskImageFileFormat::SuperCardPro => scp::ScpFormat::save_image(image, write_buf),
DiskImageFileFormat::PceFluxImage => pfi::PfiFormat::save_image(image, write_buf),
DiskImageFileFormat::KryofluxStream => kryoflux::KfxFormat::save_image(image, write_buf),
DiskImageFileFormat::MameFloppyImage => mfi::MfiFormat::save_image(image, write_buf),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_from_ext_tc() {
let ext = "tc";
let expected_format = DiskImageFileFormat::TransCopyImage;
let result = format_from_ext(ext);
assert_eq!(result, Some(expected_format));
}
}