pub mod disk35;
pub mod disk525;
pub mod dsk_d13;
pub mod dsk_do;
pub mod dsk_po;
pub mod dot2mg;
pub mod nib;
pub mod woz;
pub mod woz1;
pub mod woz2;
pub mod imd;
pub mod names;
pub mod meta;
use std::str::FromStr;
use std::fmt;
use log::{info,error};
use crate::fs;
use crate::{STDRESULT,DYNERR};
#[derive(thiserror::Error,Debug)]
pub enum Error {
#[error("unknown kind of disk")]
UnknownDiskKind,
#[error("unknown image type")]
UnknownImageType,
#[error("track count did not match request")]
TrackCountMismatch,
#[error("image size did not match the request")]
ImageSizeMismatch,
#[error("image type not compatible with request")]
ImageTypeMismatch,
#[error("unable to access sector")]
SectorAccess,
#[error("metadata mismatch")]
MetadataMismatch
}
#[derive(thiserror::Error,Debug)]
pub enum NibbleError {
#[error("could not interpret track data")]
BadTrack,
#[error("invalid byte while decoding")]
InvalidByte,
#[error("bad checksum found in a sector")]
BadChecksum,
#[error("could not find bit pattern")]
BitPatternNotFound,
#[error("sector not found")]
SectorNotFound,
#[error("nibble type appeared in wrong context")]
NibbleType
}
#[derive(PartialEq,Eq,Clone,Copy)]
pub enum FluxCode {
None,
FM,
GCR,
MFM
}
#[derive(PartialEq,Eq,Clone,Copy)]
pub enum NibbleCode {
None,
N44,
N53,
N62
}
#[derive(PartialEq,Eq,Clone,Copy)]
pub struct BlockLayout {
block_size: usize,
block_count: usize
}
#[derive(PartialEq,Eq,Clone,Copy)]
pub struct TrackLayout {
cylinders: [usize;5],
sides: [usize;5],
sectors: [usize;5],
sector_size: [usize;5],
flux_code: [FluxCode;5],
nib_code: [NibbleCode;5]
}
#[derive(PartialEq,Eq,Clone,Copy)]
pub enum DiskKind {
Unknown,
LogicalBlocks(BlockLayout),
LogicalSectors(TrackLayout),
D35(TrackLayout),
D525(TrackLayout),
D8(TrackLayout)
}
#[derive(PartialEq,Clone,Copy)]
pub enum DiskImageType {
D13,
DO,
PO,
WOZ1,
WOZ2,
IMD,
DOT2MG,
NIB
}
impl TrackLayout {
pub fn track_count(&self) -> usize {
let mut ans = 0;
for i in 0..5 {
ans += self.cylinders[i] * self.sides[i];
}
ans
}
pub fn sides(&self) -> usize {
*self.sides.iter().max().unwrap()
}
pub fn zone(&self,track_num: usize) -> usize {
let mut tcount: [usize;5] = [0;5];
tcount[0] = self.cylinders[0] * self.sides[0];
for i in 1..5 {
tcount[i] = tcount[i-1] + self.cylinders[i] * self.sides[i];
}
match track_num {
n if n < tcount[0] => 0,
n if n < tcount[1] => 1,
n if n < tcount[2] => 2,
n if n < tcount[3] => 3,
_ => 4
}
}
}
impl fmt::Display for TrackLayout {
fn fmt(&self,f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut cyls = 0;
for c in self.cylinders {
cyls += c;
}
write!(f,"{} cylinders",cyls)
}
}
impl fmt::Display for DiskKind {
fn fmt(&self,f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
DiskKind::LogicalBlocks(lay) => write!(f,"Logical disk, {} blocks",lay.block_count),
DiskKind::LogicalSectors(lay) => write!(f,"Logical disk, {}",lay),
names::A2_400_KIND => write!(f,"Apple 3.5 inch 400K"),
names::A2_800_KIND => write!(f,"Apple 3.5 inch 800K"),
names::A2_DOS32_KIND => write!(f,"Apple 5.25 inch 13 sector"),
names::A2_DOS33_KIND => write!(f,"Apple 5.25 inch 16 sector"),
names::IBM_CPM1_KIND => write!(f,"IBM 8 inch SSSD"),
names::OSBORNE1_SD_KIND => write!(f,"IBM 5.25 inch SSSD"),
names::OSBORNE1_DD_KIND | names::KAYPROII_KIND => write!(f,"IBM 5.25 inch SSDD"),
names::KAYPRO4_KIND => write!(f,"IBM 5.25 inch DSDD"),
names::TRS80_M2_CPM_KIND => write!(f,"IBM 8 inch SSDD"),
names::NABU_CPM_KIND => write!(f,"IBM 8 inch DSDD"),
DiskKind::D35(lay) => write!(f,"3.5 inch {}",lay),
DiskKind::D525(lay) => write!(f,"5.25 inch {}",lay),
DiskKind::D8(lay) => write!(f,"8 inch {}",lay),
DiskKind::Unknown => write!(f,"unknown")
}
}
}
impl FromStr for DiskKind {
type Err = Error;
fn from_str(s: &str) -> Result<Self,Self::Err> {
match s {
"8in" => Ok(names::IBM_CPM1_KIND),
"8in-trs80" => Ok(names::TRS80_M2_CPM_KIND),
"8in-nabu" => Ok(names::NABU_CPM_KIND),
"5.25in-osb-sd" => Ok(names::OSBORNE1_SD_KIND),
"5.25in-osb-dd" => Ok(names::OSBORNE1_DD_KIND),
"5.25in-kayii" => Ok(names::KAYPROII_KIND),
"5.25in-kay4" => Ok(names::KAYPRO4_KIND),
"5.25in" => Ok(names::A2_DOS33_KIND), "3.5in" => Ok(names::A2_800_KIND),
"hdmax" => Ok(names::A2_HD_MAX),
"3.5in-ss" => Ok(names::A2_400_KIND),
"3.5in-ds" => Ok(names::A2_800_KIND),
_ => Err(Error::UnknownDiskKind)
}
}
}
impl FromStr for DiskImageType {
type Err = Error;
fn from_str(s: &str) -> Result<Self,Self::Err> {
match s {
"d13" => Ok(Self::D13),
"do" => Ok(Self::DO),
"po" => Ok(Self::PO),
"woz1" => Ok(Self::WOZ1),
"woz2" => Ok(Self::WOZ2),
"imd" => Ok(Self::IMD),
"2mg" => Ok(Self::DOT2MG),
"2img" => Ok(Self::DOT2MG),
"nib" => Ok(Self::NIB),
_ => Err(Error::UnknownImageType)
}
}
}
impl fmt::Display for DiskImageType {
fn fmt(&self,f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::D13 => write!(f,"d13"),
Self::DO => write!(f,"do"),
Self::PO => write!(f,"po"),
Self::WOZ1 => write!(f,"woz1"),
Self::WOZ2 => write!(f,"woz2"),
Self::IMD => write!(f,"imd"),
Self::DOT2MG => write!(f,"2mg"),
Self::NIB => write!(f,"nib")
}
}
}
pub trait TrackBits {
fn id(&self) -> usize;
fn bit_count(&self) -> usize;
fn reset(&mut self);
fn get_bit_ptr(&self) -> usize;
fn set_bit_ptr(&mut self,displ: usize);
fn write_sector(&mut self,bits: &mut [u8],dat: &[u8],track: u8,sector: u8) -> Result<(),NibbleError>;
fn read_sector(&mut self,bits: &[u8],track: u8,sector: u8) -> Result<Vec<u8>,NibbleError>;
fn to_nibbles(&mut self,bits: &[u8]) -> Vec<u8>;
}
pub trait DiskImage {
fn track_count(&self) -> usize;
fn byte_capacity(&self) -> usize;
fn what_am_i(&self) -> DiskImageType;
fn file_extensions(&self) -> Vec<String>;
fn kind(&self) -> DiskKind;
fn change_kind(&mut self,kind: DiskKind);
fn from_bytes(buf: &Vec<u8>) -> Option<Self> where Self: Sized;
fn to_bytes(&mut self) -> Vec<u8>;
fn read_block(&mut self,addr: fs::Block) -> Result<Vec<u8>,DYNERR>;
fn write_block(&mut self, addr: fs::Block, dat: &[u8]) -> STDRESULT;
fn read_sector(&mut self,cyl: usize,head: usize,sec: usize) -> Result<Vec<u8>,DYNERR>;
fn write_sector(&mut self,cyl: usize,head: usize,sec: usize,dat: &[u8]) -> STDRESULT;
fn get_track_buf(&mut self,cyl: usize,head: usize) -> Result<Vec<u8>,DYNERR>;
fn set_track_buf(&mut self,cyl: usize,head: usize,dat: &[u8]) -> STDRESULT;
fn get_track_nibbles(&mut self,cyl: usize,head: usize) -> Result<Vec<u8>,DYNERR>;
fn display_track(&self,bytes: &[u8]) -> String;
fn get_metadata(&self,indent: u16) -> String {
let mut root = json::JsonValue::new_object();
let typ = self.what_am_i().to_string();
root[typ] = json::JsonValue::new_object();
if indent==0 {
json::stringify(root)
} else {
json::stringify_pretty(root, indent)
}
}
fn put_metadata(&mut self,key_path: &Vec<String>, _val: &json::JsonValue) -> STDRESULT {
meta::test_metadata(key_path,self.what_am_i())
}
}
pub fn is_dos_size(dsk: &Vec<u8>,allowed_track_counts: &Vec<usize>,sectors: usize) -> STDRESULT {
let bytes = dsk.len();
for tracks in allowed_track_counts {
if bytes==tracks*sectors*256 {
return Ok(());
}
}
info!("image size was {}",bytes);
return Err(Box::new(Error::ImageSizeMismatch));
}
pub fn quantize_block(src: &[u8],quantum: usize) -> Vec<u8> {
let mut padded: Vec<u8> = Vec::new();
for i in 0..quantum {
if i<src.len() {
padded.push(src[i])
} else {
padded.push(0);
}
}
return padded;
}