From 345b32d6e32f2ecb0f41532d3e5e0b41ef480ba9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 3 Oct 2019 22:41:20 -0400 Subject: [PATCH] Implements read-only MSA support. --- OSBindings/Mac/Clock Signal/Info.plist | 4 ++ Storage/Disk/DiskImage/Formats/MSA.cpp | 65 ++++++++++++++++++- Storage/Disk/DiskImage/Formats/MSA.hpp | 9 +++ .../Formats/Utility/ImplicitSectors.cpp | 2 +- .../Formats/Utility/ImplicitSectors.hpp | 4 +- 5 files changed, 79 insertions(+), 5 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index c6ef0500a..20f8b00df 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -386,6 +386,8 @@ Owner LSTypeIsPackage + NSDocumentClass + $(PRODUCT_MODULE_NAME).MachineDocument CFBundleTypeExtensions @@ -406,6 +408,8 @@ Owner LSTypeIsPackage + NSDocumentClass + $(PRODUCT_MODULE_NAME).MachineDocument CFBundleTypeExtensions diff --git a/Storage/Disk/DiskImage/Formats/MSA.cpp b/Storage/Disk/DiskImage/Formats/MSA.cpp index 9e24a87a3..ce7729290 100644 --- a/Storage/Disk/DiskImage/Formats/MSA.cpp +++ b/Storage/Disk/DiskImage/Formats/MSA.cpp @@ -8,16 +8,77 @@ #include "MSA.hpp" +#include "Utility/ImplicitSectors.hpp" + using namespace Storage::Disk; MSA::MSA(const std::string &file_name) : file_(file_name) { + const auto signature = file_.get16be(); + if(signature != 0x0e0f) throw Error::InvalidFormat; + + sectors_per_track_ = file_.get16be(); + sides_ = 1 + file_.get16be(); + starting_track_ = file_.get16be(); + ending_track_ = file_.get16be(); + + // Create the uncompressed track list. + while(true) { + const auto data_length = file_.get16be(); + if(file_.eof()) break; + + if(data_length == sectors_per_track_ * 512) { + // This is an uncompressed track. + uncompressed_tracks_.push_back(file_.read(data_length)); + } else { + // This is an RLE-compressed track. + std::vector track; + track.reserve(sectors_per_track_ * 512); + uint16_t pointer = 0; + while(pointer < data_length) { + const auto byte = file_.get8(); + + // Compression scheme: if the byte E5 is encountered, an RLE run follows. + // An RLE run is encoded as the byte to repeat plus a 16-bit repeat count. + if(byte != 0xe5) { + track.push_back(byte); + ++pointer; + continue; + } + + pointer += 4; + if(pointer > data_length) break; + + const auto value = file_.get8(); + auto count = file_.get16be(); + while(count--) { + track.push_back(value); + } + } + + if(pointer != data_length || track.size() != sectors_per_track_ * 512) throw Error::InvalidFormat; + uncompressed_tracks_.push_back(std::move(track)); + } + } + + if(uncompressed_tracks_.size() != (ending_track_ - starting_track_ + 1)*sides_) throw Error::InvalidFormat; } std::shared_ptr<::Storage::Disk::Track> MSA::get_track_at_position(::Storage::Disk::Track::Address address) { - return nullptr; + if(address.head >= sides_) return nullptr; + + const auto position = address.position.as_int(); + if(position < starting_track_) return nullptr; + if(position >= ending_track_) return nullptr; + + const auto &track = uncompressed_tracks_[size_t(position) * size_t(sides_) + size_t(address.head)]; + return track_for_sectors(track.data(), sectors_per_track_, uint8_t(position), uint8_t(address.head), 0, 2, true); } HeadPosition MSA::get_maximum_head_position() { - return HeadPosition(10); + return HeadPosition(ending_track_); +} + +int MSA::get_head_count() { + return sides_; } diff --git a/Storage/Disk/DiskImage/Formats/MSA.hpp b/Storage/Disk/DiskImage/Formats/MSA.hpp index 076f85201..5bd8542f0 100644 --- a/Storage/Disk/DiskImage/Formats/MSA.hpp +++ b/Storage/Disk/DiskImage/Formats/MSA.hpp @@ -12,6 +12,8 @@ #include "../DiskImage.hpp" #include "../../../FileHolder.hpp" +#include + namespace Storage { namespace Disk { @@ -25,10 +27,17 @@ class MSA final: public DiskImage { // Implemented to satisfy @c DiskImage. HeadPosition get_maximum_head_position() override; + int get_head_count() override; std::shared_ptr<::Storage::Disk::Track> get_track_at_position(::Storage::Disk::Track::Address address) override; private: FileHolder file_; + uint16_t sectors_per_track_; + uint16_t sides_; + uint16_t starting_track_; + uint16_t ending_track_; + + std::vector> uncompressed_tracks_; }; diff --git a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp index 1b3b6ae41..20851efa3 100644 --- a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp +++ b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp @@ -18,7 +18,7 @@ using namespace Storage::Disk; -std::shared_ptr Storage::Disk::track_for_sectors(uint8_t *const source, int number_of_sectors, uint8_t track, uint8_t side, uint8_t first_sector, uint8_t size, bool is_double_density) { +std::shared_ptr Storage::Disk::track_for_sectors(const uint8_t *const source, int number_of_sectors, uint8_t track, uint8_t side, uint8_t first_sector, uint8_t size, bool is_double_density) { std::vector sectors; off_t byte_size = static_cast(128 << size); diff --git a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.hpp b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.hpp index e656f24af..8db583ee6 100644 --- a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.hpp +++ b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.hpp @@ -16,8 +16,8 @@ namespace Storage { namespace Disk { -std::shared_ptr track_for_sectors(uint8_t *const source, int number_of_sectors, uint8_t track, uint8_t side, uint8_t first_sector, uint8_t size, bool is_double_density); -void decode_sectors(Track &track, uint8_t *const destination, uint8_t first_sector, uint8_t last_sector, uint8_t sector_size, bool is_double_density); +std::shared_ptr track_for_sectors(const uint8_t *source, int number_of_sectors, uint8_t track, uint8_t side, uint8_t first_sector, uint8_t size, bool is_double_density); +void decode_sectors(Track &track, uint8_t *destination, uint8_t first_sector, uint8_t last_sector, uint8_t sector_size, bool is_double_density); } }