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/NIB.cpp b/Storage/Disk/DiskImage/Formats/NIB.cpp index 6cf53b9c4..830873435 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 @@ -29,20 +30,38 @@ 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() { 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 +119,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); }; } 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;