diff --git a/Storage/Disk/DiskImage/Formats/AcornADF.cpp b/Storage/Disk/DiskImage/Formats/AcornADF.cpp index 27eb68b1a..663af4376 100644 --- a/Storage/Disk/DiskImage/Formats/AcornADF.cpp +++ b/Storage/Disk/DiskImage/Formats/AcornADF.cpp @@ -10,36 +10,71 @@ #include "Utility/ImplicitSectors.hpp" -namespace { - constexpr int sectors_per_track = 16; - constexpr int sector_size = 1; -} - using namespace Storage::Disk; AcornADF::AcornADF(const std::string &file_name) : MFMSectorDump(file_name) { // Check that the disk image contains a whole number of sector. using sizeT = decltype(file_.stats().st_size); - if(file_.stats().st_size % sizeT(128 << sector_size)) throw Error::InvalidFormat; + const auto size = file_.stats().st_size; + + if(size < 1024) throw Error::InvalidFormat; + + // Definitely true: a directory signature of 'Hugo' can be read by both 8-bit + // machines and the Archimedes. 'Nick' can be read only by the Archimedes. + // + // https://mdfs.net/Docs/Comp/Disk/Format/ADFS then falsely states that: + // + // The type of ADFS filesystem can be determined by looking for the "Hugo"/ + // "Nick" identifier that marks the start of the root directory 512 bytes into + // the filesystem and 1024 bytes in. + // + // In terms of .ADF files: + // + // all 8-bit files seem to have 'Hugo' at offset 513; + // ADFS-D (early Arc, late BBC Master) has 'Nick' or 'Hugo' at 1025; but + // ADFS-E (most Arc) has 'Hugo' at 2049. + // + // Even allowing for the document having failed to account for the directory ID, + // I can't reconcile that 2049 offset with being 1024 bytes into the file system. + // + // That document claims that ADFS-D and ADFS-E are logically interleaved but + // https://github.com/android444/fluxengine/blob/master/doc/disk-acornadfs.md + // states that: + // + // Acorn logical block numbering goes all the way up side 0 and then all the + // way up side 1. However, FluxEngine uses traditional disk images with alternating + // sides, with the blocks from track 0 side 0 then track 0 side 1 then track 1 side 0 etc. + // Most Acorn emulators will use both formats, but they might require nudging as the side + // order can't be reliably autodetected. + // + // So then .ADF files might be track-interleaved and might not be. + + switch(size) { + default: + sector_size_ = 1; + sectors_per_track_ = 16; + break; + } // Check that the disk image is at least large enough to hold an ADFS catalogue. - if(file_.stats().st_size < 7 * sizeT(128 << sector_size)) throw Error::InvalidFormat; + if(file_.stats().st_size < 7 * sizeT(128 << sector_size_)) throw Error::InvalidFormat; // Check that the initial directory's 'Hugo's or 'Nick's are present. + // TODO: check other locations, to pick sector size and count. file_.seek(513, SEEK_SET); uint8_t bytes[4]; file_.read(bytes, 4); - if(memcmp(bytes, "Hugo", 4)) throw Error::InvalidFormat; + if(memcmp(bytes, "Hugo", 4) && memcmp(bytes, "Nick", 4)) throw Error::InvalidFormat; file_.seek(0x6fb, SEEK_SET); file_.read(bytes, 4); - if(memcmp(bytes, "Hugo", 4)) throw Error::InvalidFormat; + if(memcmp(bytes, "Hugo", 4) && memcmp(bytes, "Nick", 4)) throw Error::InvalidFormat; // Pick a number of heads; treat this image as double sided if it's too large to be single-sided. - head_count_ = 1 + (file_.stats().st_size > sectors_per_track * sizeT(128 << sector_size) * 80); + head_count_ = 1 + (file_.stats().st_size > sectors_per_track_ * sizeT(128 << sector_size_) * 80); // Announce disk geometry. - set_geometry(sectors_per_track, sector_size, 0, Encodings::MFM::Density::Double); + set_geometry(sectors_per_track_, sector_size_, 0, Encodings::MFM::Density::Double); } HeadPosition AcornADF::get_maximum_head_position() { @@ -51,5 +86,5 @@ int AcornADF::get_head_count() { } long AcornADF::get_file_offset_for_position(Track::Address address) { - return (address.position.as_int() * head_count_ + address.head) * (128 << sector_size) * sectors_per_track; + return (address.position.as_int() * head_count_ + address.head) * (128 << sector_size_) * sectors_per_track_; } diff --git a/Storage/Disk/DiskImage/Formats/AcornADF.hpp b/Storage/Disk/DiskImage/Formats/AcornADF.hpp index 54a8d12e7..68a446ec7 100644 --- a/Storage/Disk/DiskImage/Formats/AcornADF.hpp +++ b/Storage/Disk/DiskImage/Formats/AcornADF.hpp @@ -33,6 +33,8 @@ class AcornADF: public MFMSectorDump { private: long get_file_offset_for_position(Track::Address address) final; int head_count_ = 1; + uint8_t sector_size_ = 1; + int sectors_per_track_ = 16; }; }