From f443fd44b51a9be68f2bfa1eb22d777fb4facb3a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 May 2018 18:30:55 -0400 Subject: [PATCH 1/3] Introduces support for writing NIBs. --- Storage/Disk/DiskImage/Formats/NIB.cpp | 60 ++++++++++++++++++++++++-- Storage/Disk/DiskImage/Formats/NIB.hpp | 10 +++-- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/NIB.cpp b/Storage/Disk/DiskImage/Formats/NIB.cpp index 6cf53b9c4..81049673f 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.cpp +++ b/Storage/Disk/DiskImage/Formats/NIB.cpp @@ -9,6 +9,7 @@ #include "NIB.hpp" #include "../../Track/PCMTrack.hpp" +#include "../../Track/TrackSerialiser.hpp" #include "../../Encodings/AppleGCR/Encoder.hpp" #include @@ -36,13 +37,25 @@ HeadPosition NIB::get_maximum_head_position() { return HeadPosition(number_of_tracks); } +bool NIB::get_is_read_only() { + return file_.get_is_known_read_only(); +} + +long NIB::file_offset(Track::Address address) { + return static_cast(address.position.as_int()) * track_length; +} + std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Disk::Track::Address address) { // NIBs contain data for even-numbered tracks underneath a single head only. if(address.head) return nullptr; - const long file_track = static_cast(address.position.as_int()); - file_.seek(file_track * track_length, SEEK_SET); - std::vector track_data = file_.read(track_length); + long offset = file_offset(address); + std::vector track_data; + { + std::lock_guard lock_guard(file_.get_file_access_mutex()); + file_.seek(offset, SEEK_SET); + track_data = file_.read(track_length); + } // NIB files leave sync bytes implicit and make no guarantees // about overall track positioning. So the approach taken here @@ -100,3 +113,44 @@ std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Di return std::make_shared(segment); } + +void NIB::set_tracks(const std::map> &tracks) { + std::map> tracks_by_address; + + // Convert to a map from address to a vector of data that contains the NIB representation + // of the track. + for(const auto &pair: tracks) { + // Grab the track bit stream. + auto segment = Storage::Disk::track_serialisation(*pair.second, Storage::Time(1, 50000)); + + // Process to eliminate all sync bits. + std::vector track; + track.reserve(track_length); + uint8_t shifter = 0; + for(unsigned int bit = 0; bit < segment.number_of_bits; ++bit) { + shifter = static_cast((shifter << 1) | segment.bit(bit)); + if(shifter & 0x80) { + track.push_back(shifter); + shifter = 0; + } + } + + // Pad out to track_length. + if(track.size() > track_length) { + track.resize(track_length); + } else { + while(track.size() < track_length) { + track.push_back(0xff); + } + } + + tracks_by_address[pair.first] = std::move(track); + } + + // Lock the file and spool out. + std::lock_guard lock_guard(file_.get_file_access_mutex()); + for(const auto &track: tracks_by_address) { + file_.seek(file_offset(track.first), SEEK_SET); + file_.write(track.second); + } +} diff --git a/Storage/Disk/DiskImage/Formats/NIB.hpp b/Storage/Disk/DiskImage/Formats/NIB.hpp index 4cb7734be..f8ef04ef8 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.hpp +++ b/Storage/Disk/DiskImage/Formats/NIB.hpp @@ -17,21 +17,23 @@ namespace Disk { /*! Provides a @c DiskImage describing an Apple NIB disk image: - mostly a bit stream capture, but syncs are implicitly packed - into 8 bits instead of 9. + a bit stream capture that omits sync zeroes, and doesn't define + the means for full reconstruction. */ class NIB: public DiskImage { public: NIB(const std::string &file_name); + // Implemented to satisfy @c DiskImage. HeadPosition get_maximum_head_position() override; - std::shared_ptr<::Storage::Disk::Track> get_track_at_position(::Storage::Disk::Track::Address address) override; + void set_tracks(const std::map> &tracks) override; + bool get_is_read_only() override; private: FileHolder file_; long get_file_offset_for_position(Track::Address address); - + long file_offset(Track::Address address); }; } From 772812b35f303ff9cca73d634f9c3c6a63d5fed0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 May 2018 18:31:20 -0400 Subject: [PATCH 2/3] Corrects improper textual reference to interface names. --- Storage/Disk/DiskImage/Formats/AppleDSK.hpp | 2 +- Storage/Disk/DiskImage/Formats/WOZ.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.hpp b/Storage/Disk/DiskImage/Formats/AppleDSK.hpp index 9d7403c44..420ef8a38 100644 --- a/Storage/Disk/DiskImage/Formats/AppleDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.hpp @@ -31,7 +31,7 @@ class AppleDSK: public DiskImage { */ AppleDSK(const std::string &file_name); - // Implemented to satisfy @c Disk. + // Implemented to satisfy @c DiskImage. HeadPosition get_maximum_head_position() override; std::shared_ptr get_track_at_position(Track::Address address) override; void set_tracks(const std::map> &tracks) override; diff --git a/Storage/Disk/DiskImage/Formats/WOZ.hpp b/Storage/Disk/DiskImage/Formats/WOZ.hpp index b6904e1af..6e4885de1 100644 --- a/Storage/Disk/DiskImage/Formats/WOZ.hpp +++ b/Storage/Disk/DiskImage/Formats/WOZ.hpp @@ -25,7 +25,7 @@ class WOZ: public DiskImage { public: WOZ(const std::string &file_name); - // Implemented to satisfy @c Disk. + // Implemented to satisfy @c DiskImage. HeadPosition get_maximum_head_position() override; int get_head_count() override; std::shared_ptr get_track_at_position(Track::Address address) override; From d45231c1a8bcb5cb6e890945ac7c19d07ae867e7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 May 2018 18:40:15 -0400 Subject: [PATCH 3/3] Introduces an additional validation test. Thereby satisfying the TODO. --- Storage/Disk/DiskImage/Formats/NIB.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Storage/Disk/DiskImage/Formats/NIB.cpp b/Storage/Disk/DiskImage/Formats/NIB.cpp index 81049673f..830873435 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.cpp +++ b/Storage/Disk/DiskImage/Formats/NIB.cpp @@ -30,7 +30,13 @@ NIB::NIB(const std::string &file_name) : throw Error::InvalidFormat; } - // TODO: all other validation. I.e. does this look like a GCR disk? + // A real NIB should have every single top bit set. Yes, 1/8th of the + // file size is a complete waste. But it provides a hook for validation. + while(true) { + uint8_t next = file_.get8(); + if(file_.eof()) break; + if(!(next & 0x80)) throw Error::InvalidFormat; + } } HeadPosition NIB::get_maximum_head_position() {