// // NIB.cpp // Clock Signal // // Created by Thomas Harte on 21/04/2018. // Copyright 2018 Thomas Harte. All rights reserved. // #include "NIB.hpp" #include "../../Track/PCMTrack.hpp" #include "../../Encodings/AppleGCR/Encoder.hpp" #include using namespace Storage::Disk; namespace { const long track_length = 6656; const std::size_t number_of_tracks = 35; } NIB::NIB(const std::string &file_name) : file_(file_name) { // A NIB should be 35 tracks, each 6656 bytes long. if(file_.stats().st_size != track_length*number_of_tracks) { throw Error::InvalidFormat; } // TODO: all other validation. I.e. does this look like a GCR disk? } HeadPosition NIB::get_maximum_head_position() { return HeadPosition(number_of_tracks); } 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); // NIB files leave sync bytes implicit and make no guarantees // about overall track positioning. So the approach taken here // is to look for the epilogue sequence (which concludes all Apple // tracks and headers), then treat all following FFs as a sync // region, then switch back to ordinary behaviour as soon as a // non-FF appears. PCMSegment segment; std::size_t start_index = 0; std::set sync_starts; // Establish where syncs start by finding instances of 0xd5 0xaa and then regressing // from each along all preceding FFs. for(size_t index = 0; index < track_data.size(); ++index) { if(track_data[index] == 0xd5 && track_data[(index+1)%track_data.size()] == 0xaa) { size_t start = index - 1; size_t length = 0; while(track_data[start] == 0xff) { start = (start + track_data.size() - 1) % track_data.size(); ++length; } if(length >= 5) { sync_starts.insert((start + 1) % track_data.size()); if(start > index) start_index = start; } } } if(start_index) { segment += Encodings::AppleGCR::six_and_two_sync(static_cast(start_index)); } std::size_t index = start_index; for(const auto &location: sync_starts) { // Write from index to sync_start. PCMSegment data_segment; data_segment.data.insert( data_segment.data.end(), track_data.begin() + static_cast(index), track_data.begin() + static_cast(location)); data_segment.number_of_bits = static_cast(data_segment.data.size() * 8); segment += data_segment; // Add a sync from sync_start to end of 0xffs. if(location == track_length-1) break; index = location; while(index < track_length && track_data[index] == 0xff) ++index; segment += Encodings::AppleGCR::six_and_two_sync(static_cast(index - location)); } return std::make_shared(segment); }