// // HFE.cpp // Clock Signal // // Created by Thomas Harte on 17/08/2017. // Copyright © 2017 Thomas Harte. All rights reserved. // #include "HFE.hpp" #include "../../Track/PCMTrack.hpp" #include "../../Track/TrackSerialiser.hpp" #include "../../../Data/BitReverse.hpp" using namespace Storage::Disk; HFE::HFE(const std::string &file_name) : file_(file_name) { if(!file_.check_signature("HXCPICFE")) throw Error::InvalidFormat; if(file_.get8()) throw Error::UnknownVersion; track_count_ = file_.get8(); head_count_ = file_.get8(); file_.seek(7, SEEK_CUR); track_list_offset_ = static_cast(file_.get16le()) << 9; } HFE::~HFE() { } int HFE::get_head_position_count() { return track_count_; } int HFE::get_head_count() { return head_count_; } /*! Seeks to the beginning of the track at @c position underneath @c head, returning its length in bytes. To read the track, start from the current file position, read 256 bytes, skip 256 bytes, read 256 bytes, skip 256 bytes, etc. */ uint16_t HFE::seek_track(Track::Address address) { // Get track position and length from the lookup table; data is then always interleaved // based on an assumption of two heads. file_.seek(track_list_offset_ + address.position * 4, SEEK_SET); long track_offset = static_cast(file_.get16le()) << 9; uint16_t track_length = file_.get16le(); file_.seek(track_offset, SEEK_SET); if(address.head) file_.seek(256, SEEK_CUR); return track_length / 2; } std::shared_ptr HFE::get_track_at_position(Track::Address address) { PCMSegment segment; { std::lock_guard lock_guard(file_.get_file_access_mutex()); uint16_t track_length = seek_track(address); segment.data.resize(track_length); segment.number_of_bits = track_length * 8; uint16_t c = 0; while(c < track_length) { uint16_t length = static_cast(std::min(256, track_length - c)); file_.read(&segment.data[c], length); c += length; file_.seek(256, SEEK_CUR); } } // Flip bytes; HFE's preference is that the least-significant bit // is serialised first, but PCMTrack posts the most-significant first. Storage::Data::BitReverse::reverse(segment.data); std::shared_ptr track(new PCMTrack(segment)); return track; } void HFE::set_tracks(const std::map> &tracks) { for(auto &track : tracks) { std::unique_lock lock_guard(file_.get_file_access_mutex()); uint16_t track_length = seek_track(track.first); lock_guard.unlock(); PCMSegment segment = Storage::Disk::track_serialisation(*track.second, Storage::Time(1, track_length * 8)); Storage::Data::BitReverse::reverse(segment.data); uint16_t data_length = std::min(static_cast(segment.data.size()), track_length); lock_guard.lock(); seek_track(track.first); uint16_t c = 0; while(c < data_length) { uint16_t length = static_cast(std::min(256, data_length - c)); file_.write(&segment.data[c], length); c += length; file_.seek(256, SEEK_CUR); } lock_guard.unlock(); } } bool HFE::get_is_read_only() { return file_.get_is_known_read_only(); }