From 31c2548804e8360ee951326ed5506800effb7ad8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 21 Nov 2016 20:14:09 +0800 Subject: [PATCH 1/2] Created a base class for the boilerplate `fopen` stuff, switched as many classes as possible to its use, switched to postfix underscores and non-camelCase names. --- .../Clock Signal.xcodeproj/project.pbxproj | 6 + Storage/Disk/Formats/AcornADF.cpp | 32 ++-- Storage/Disk/Formats/AcornADF.hpp | 8 +- Storage/Disk/Formats/D64.cpp | 36 ++--- Storage/Disk/Formats/D64.hpp | 10 +- Storage/Disk/Formats/G64.cpp | 59 +++----- Storage/Disk/Formats/G64.hpp | 10 +- Storage/Disk/Formats/SSD.cpp | 43 ++---- Storage/Disk/Formats/SSD.hpp | 10 +- Storage/FileHolder.cpp | 23 +++ Storage/FileHolder.hpp | 34 +++++ Storage/Tape/Formats/CommodoreTAP.cpp | 76 +++++----- Storage/Tape/Formats/CommodoreTAP.hpp | 15 +- Storage/Tape/Formats/OricTAP.cpp | 113 ++++++-------- Storage/Tape/Formats/OricTAP.hpp | 21 ++- Storage/Tape/Formats/TapePRG.cpp | 142 +++++++++--------- Storage/Tape/Formats/TapePRG.hpp | 25 ++- Storage/Tape/Formats/TapeUEF.cpp | 122 +++++++-------- Storage/Tape/Formats/TapeUEF.hpp | 12 +- 19 files changed, 388 insertions(+), 409 deletions(-) create mode 100644 Storage/FileHolder.cpp create mode 100644 Storage/FileHolder.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 0f69dbae3..39f28c2c8 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -53,6 +53,7 @@ 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */; }; 4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B59199A1DAC6C46005BB85C /* OricTAP.cpp */; }; 4B5A12571DD55862007A2231 /* Disassembler6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5A12551DD55862007A2231 /* Disassembler6502.cpp */; }; + 4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */; }; 4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */; }; 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; }; 4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; }; @@ -499,6 +500,8 @@ 4B59199B1DAC6C46005BB85C /* OricTAP.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OricTAP.hpp; sourceTree = ""; }; 4B5A12551DD55862007A2231 /* Disassembler6502.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Disassembler6502.cpp; path = ../../StaticAnalyser/Disassembler/Disassembler6502.cpp; sourceTree = ""; }; 4B5A12561DD55862007A2231 /* Disassembler6502.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Disassembler6502.hpp; path = ../../StaticAnalyser/Disassembler/Disassembler6502.hpp; sourceTree = ""; }; + 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileHolder.cpp; sourceTree = ""; }; + 4B5FADB91DE3151600AEC565 /* FileHolder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FileHolder.hpp; sourceTree = ""; }; 4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = ""; }; 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = ""; }; 4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = ""; }; @@ -1190,6 +1193,8 @@ 4B8805F81DCFF6CD003085B1 /* Data */, 4BAB62AA1D3272D200DF5BA0 /* Disk */, 4B69FB3A1C4D908A00B5F0AA /* Tape */, + 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */, + 4B5FADB91DE3151600AEC565 /* FileHolder.hpp */, ); name = Storage; path = ../../Storage; @@ -2333,6 +2338,7 @@ 4BBF99141C8FBA6F0075DAFB /* TextureBuilder.cpp in Sources */, 4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */, 4BCF1FA81DADC5250039D2E7 /* CSOric.mm in Sources */, + 4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */, 4B6C73BD1D387AE500AFCFCA /* DiskController.cpp in Sources */, 4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */, 4B4DC8281D2C2470003C5BF8 /* C1540.cpp in Sources */, diff --git a/Storage/Disk/Formats/AcornADF.cpp b/Storage/Disk/Formats/AcornADF.cpp index c82d27e65..98779de80 100644 --- a/Storage/Disk/Formats/AcornADF.cpp +++ b/Storage/Disk/Formats/AcornADF.cpp @@ -18,35 +18,25 @@ namespace { using namespace Storage::Disk; -AcornADF::AcornADF(const char *file_name) : _file(nullptr) +AcornADF::AcornADF(const char *file_name) : + Storage::FileHolder(file_name) { - struct stat file_stats; - stat(file_name, &file_stats); - // very loose validation: the file needs to be a multiple of 256 bytes // and not ungainly large - if(file_stats.st_size % bytes_per_sector) throw ErrorNotAcornADF; - if(file_stats.st_size < 7 * bytes_per_sector) throw ErrorNotAcornADF; - - _file = fopen(file_name, "rb"); - if(!_file) throw ErrorCantOpen; + if(file_stats_.st_size % bytes_per_sector) throw ErrorNotAcornADF; + if(file_stats_.st_size < 7 * bytes_per_sector) throw ErrorNotAcornADF; // check that the initial directory's 'Hugo's are present - fseek(_file, 513, SEEK_SET); + fseek(file_, 513, SEEK_SET); uint8_t bytes[4]; - fread(bytes, 1, 4, _file); + fread(bytes, 1, 4, file_); if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw ErrorNotAcornADF; - fseek(_file, 0x6fb, SEEK_SET); - fread(bytes, 1, 4, _file); + fseek(file_, 0x6fb, SEEK_SET); + fread(bytes, 1, 4, file_); if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw ErrorNotAcornADF; } -AcornADF::~AcornADF() -{ - if(_file) fclose(_file); -} - unsigned int AcornADF::get_head_position_count() { return 80; @@ -63,7 +53,7 @@ std::shared_ptr AcornADF::get_track_at_position(unsigned int head, unsign if(head >= 2) return track; long file_offset = (position * 1 + head) * bytes_per_sector * sectors_per_track; - fseek(_file, file_offset, SEEK_SET); + fseek(file_, file_offset, SEEK_SET); std::vector sectors; for(int sector = 0; sector < sectors_per_track; sector++) @@ -74,8 +64,8 @@ std::shared_ptr AcornADF::get_track_at_position(unsigned int head, unsign new_sector.sector = (uint8_t)sector; new_sector.data.resize(bytes_per_sector); - fread(&new_sector.data[0], 1, bytes_per_sector, _file); - if(feof(_file)) + fread(&new_sector.data[0], 1, bytes_per_sector, file_); + if(feof(file_)) break; sectors.push_back(std::move(new_sector)); diff --git a/Storage/Disk/Formats/AcornADF.hpp b/Storage/Disk/Formats/AcornADF.hpp index 64aeb54b1..17455e007 100644 --- a/Storage/Disk/Formats/AcornADF.hpp +++ b/Storage/Disk/Formats/AcornADF.hpp @@ -10,6 +10,7 @@ #define AcornADF_hpp #include "../Disk.hpp" +#include "../../FileHolder.hpp" namespace Storage { namespace Disk { @@ -17,7 +18,7 @@ namespace Disk { /*! Provies a @c Disk containing an ADF disk image — a decoded sector dump of an Acorn ADFS disk. */ -class AcornADF: public Disk { +class AcornADF: public Disk, public Storage::FileHolder { public: /*! Construct an @c AcornADF containing content from the file with name @c file_name. @@ -26,10 +27,8 @@ class AcornADF: public Disk { @throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image. */ AcornADF(const char *file_name); - ~AcornADF(); enum { - ErrorCantOpen, ErrorNotAcornADF, }; @@ -37,9 +36,6 @@ class AcornADF: public Disk { unsigned int get_head_position_count(); unsigned int get_head_count(); std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); - - private: - FILE *_file; }; } diff --git a/Storage/Disk/Formats/D64.cpp b/Storage/Disk/Formats/D64.cpp index f0397b10b..50f54257b 100644 --- a/Storage/Disk/Formats/D64.cpp +++ b/Storage/Disk/Formats/D64.cpp @@ -15,42 +15,30 @@ using namespace Storage::Disk; -D64::D64(const char *file_name) +D64::D64(const char *file_name) : + Storage::FileHolder(file_name) { - struct stat file_stats; - stat(file_name, &file_stats); - // in D64, this is it for validation without imposing potential false-negative tests — check that // the file size appears to be correct. Stone-age stuff. - if(file_stats.st_size != 174848 && file_stats.st_size != 196608) + if(file_stats_.st_size != 174848 && file_stats_.st_size != 196608) throw ErrorNotD64; - _number_of_tracks = (file_stats.st_size == 174848) ? 35 : 40; - - _file = fopen(file_name, "rb"); - - if(!_file) - throw ErrorNotD64; + number_of_tracks_ = (file_stats_.st_size == 174848) ? 35 : 40; // then, ostensibly, this is a valid file. Hmmm. Pick a disk ID as a function of the file_name, // being the most stable thing available - _disk_id = 0; + disk_id_ = 0; while(*file_name) { - _disk_id ^= file_name[0]; - _disk_id = (uint16_t)((_disk_id << 2) ^ (_disk_id >> 13)); + disk_id_ ^= file_name[0]; + disk_id_ = (uint16_t)((disk_id_ << 2) ^ (disk_id_ >> 13)); file_name++; } } -D64::~D64() -{ - if(_file) fclose(_file); -} - unsigned int D64::get_head_position_count() { - return _number_of_tracks*2; + return number_of_tracks_*2; } std::shared_ptr D64::get_track_at_position(unsigned int head, unsigned int position) @@ -75,7 +63,7 @@ std::shared_ptr D64::get_track_at_position(unsigned int head, unsigned in } // seek to start of data - fseek(_file, offset_to_track * 256, SEEK_SET); + fseek(file_, offset_to_track * 256, SEEK_SET); // build up a PCM sampling of the GCR version of this track @@ -114,14 +102,14 @@ std::shared_ptr D64::get_track_at_position(unsigned int head, unsigned in uint8_t sector_number = (uint8_t)(sector); // sectors count from 0 uint8_t track_number = (uint8_t)((position >> 1) + 1); // tracks count from 1 - uint8_t checksum = (uint8_t)(sector_number ^ track_number ^ _disk_id ^ (_disk_id >> 8)); + uint8_t checksum = (uint8_t)(sector_number ^ track_number ^ disk_id_ ^ (disk_id_ >> 8)); uint8_t header_start[4] = { 0x08, checksum, sector_number, track_number }; Encodings::CommodoreGCR::encode_block(§or_data[3], header_start); uint8_t header_end[4] = { - (uint8_t)(_disk_id & 0xff), (uint8_t)(_disk_id >> 8), 0, 0 + (uint8_t)(disk_id_ & 0xff), (uint8_t)(disk_id_ >> 8), 0, 0 }; Encodings::CommodoreGCR::encode_block(§or_data[8], header_end); @@ -134,7 +122,7 @@ std::shared_ptr D64::get_track_at_position(unsigned int head, unsigned in // get the actual contents uint8_t source_data[256]; - fread(source_data, 1, 256, _file); + fread(source_data, 1, 256, file_); // compute the latest checksum checksum = 0; diff --git a/Storage/Disk/Formats/D64.hpp b/Storage/Disk/Formats/D64.hpp index 7b3b20291..83621faf1 100644 --- a/Storage/Disk/Formats/D64.hpp +++ b/Storage/Disk/Formats/D64.hpp @@ -10,6 +10,7 @@ #define D64_hpp #include "../Disk.hpp" +#include "../../FileHolder.hpp" namespace Storage { namespace Disk { @@ -17,7 +18,7 @@ namespace Disk { /*! Provies a @c Disk containing a D64 disk image — a decoded sector dump of a C1540-format disk. */ -class D64: public Disk { +class D64: public Disk, public Storage::FileHolder { public: /*! Construct a @c D64 containing content from the file with name @c file_name. @@ -26,10 +27,8 @@ class D64: public Disk { @throws ErrorNotD64 if the file doesn't appear to contain a .D64 format image. */ D64(const char *file_name); - ~D64(); enum { - ErrorCantOpen, ErrorNotD64, }; @@ -38,9 +37,8 @@ class D64: public Disk { std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); private: - FILE *_file; - unsigned int _number_of_tracks; - uint16_t _disk_id; + unsigned int number_of_tracks_; + uint16_t disk_id_; }; } diff --git a/Storage/Disk/Formats/G64.cpp b/Storage/Disk/Formats/G64.cpp index 86df3022f..df0559058 100644 --- a/Storage/Disk/Formats/G64.cpp +++ b/Storage/Disk/Formats/G64.cpp @@ -14,44 +14,35 @@ using namespace Storage::Disk; -G64::G64(const char *file_name) +G64::G64(const char *file_name) : + Storage::FileHolder(file_name) { - _file = fopen(file_name, "rb"); - - if(!_file) - throw ErrorCantOpen; - // read and check the file signature char signature[8]; - if(fread(signature, 1, 8, _file) != 8) + if(fread(signature, 1, 8, file_) != 8) throw ErrorNotG64; if(memcmp(signature, "GCR-1541", 8)) throw ErrorNotG64; // check the version number - int version = fgetc(_file); + int version = fgetc(file_); if(version != 0) { throw ErrorUnknownVersion; } // get the number of tracks and track size - _number_of_tracks = (uint8_t)fgetc(_file); - _maximum_track_size = (uint16_t)fgetc(_file); - _maximum_track_size |= (uint16_t)fgetc(_file) << 8; -} - -G64::~G64() -{ - if(_file) fclose(_file); + number_of_tracks_ = (uint8_t)fgetc(file_); + maximum_track_size_ = (uint16_t)fgetc(file_); + maximum_track_size_ |= (uint16_t)fgetc(file_) << 8; } unsigned int G64::get_head_position_count() { // give at least 84 tracks, to yield the normal geometry but, // if there are more, shove them in - return _number_of_tracks > 84 ? _number_of_tracks : 84; + return number_of_tracks_ > 84 ? number_of_tracks_ : 84; } std::shared_ptr G64::get_track_at_position(unsigned int head, unsigned int position) @@ -60,55 +51,55 @@ std::shared_ptr G64::get_track_at_position(unsigned int head, unsigned in // if there's definitely no track here, return the empty track // (TODO: should be supplying one with an index hole?) - if(position >= _number_of_tracks) return resulting_track; + if(position >= number_of_tracks_) return resulting_track; if(head >= 1) return resulting_track; // seek to this track's entry in the track table - fseek(_file, (long)((position * 4) + 0xc), SEEK_SET); + fseek(file_, (long)((position * 4) + 0xc), SEEK_SET); // read the track offset uint32_t track_offset; - track_offset = (uint32_t)fgetc(_file); - track_offset |= (uint32_t)fgetc(_file) << 8; - track_offset |= (uint32_t)fgetc(_file) << 16; - track_offset |= (uint32_t)fgetc(_file) << 24; + track_offset = (uint32_t)fgetc(file_); + track_offset |= (uint32_t)fgetc(file_) << 8; + track_offset |= (uint32_t)fgetc(file_) << 16; + track_offset |= (uint32_t)fgetc(file_) << 24; // if the track offset is zero, this track doesn't exist, so... if(!track_offset) return resulting_track; // seek to the track start - fseek(_file, (int)track_offset, SEEK_SET); + fseek(file_, (int)track_offset, SEEK_SET); // get the real track length uint16_t track_length; - track_length = (uint16_t)fgetc(_file); - track_length |= (uint16_t)fgetc(_file) << 8; + track_length = (uint16_t)fgetc(file_); + track_length |= (uint16_t)fgetc(file_) << 8; // grab the byte contents of this track std::vector track_contents(track_length); - fread(&track_contents[0], 1, track_length, _file); + fread(&track_contents[0], 1, track_length, file_); // seek to this track's entry in the speed zone table - fseek(_file, (long)((position * 4) + 0x15c), SEEK_SET); + fseek(file_, (long)((position * 4) + 0x15c), SEEK_SET); // read the speed zone offsrt uint32_t speed_zone_offset; - speed_zone_offset = (uint32_t)fgetc(_file); - speed_zone_offset |= (uint32_t)fgetc(_file) << 8; - speed_zone_offset |= (uint32_t)fgetc(_file) << 16; - speed_zone_offset |= (uint32_t)fgetc(_file) << 24; + speed_zone_offset = (uint32_t)fgetc(file_); + speed_zone_offset |= (uint32_t)fgetc(file_) << 8; + speed_zone_offset |= (uint32_t)fgetc(file_) << 16; + speed_zone_offset |= (uint32_t)fgetc(file_) << 24; // if the speed zone is not constant, create a track based on the whole table; otherwise create one that's constant if(speed_zone_offset > 3) { // seek to start of speed zone - fseek(_file, (int)speed_zone_offset, SEEK_SET); + fseek(file_, (int)speed_zone_offset, SEEK_SET); uint16_t speed_zone_length = (track_length + 3) >> 2; // read the speed zone bytes uint8_t speed_zone_contents[speed_zone_length]; - fread(speed_zone_contents, 1, speed_zone_length, _file); + fread(speed_zone_contents, 1, speed_zone_length, file_); // divide track into appropriately timed PCMSegments std::vector segments; diff --git a/Storage/Disk/Formats/G64.hpp b/Storage/Disk/Formats/G64.hpp index 35db52efb..31ca5ca60 100644 --- a/Storage/Disk/Formats/G64.hpp +++ b/Storage/Disk/Formats/G64.hpp @@ -10,6 +10,7 @@ #define G64_hpp #include "../Disk.hpp" +#include "../../FileHolder.hpp" namespace Storage { namespace Disk { @@ -17,7 +18,7 @@ namespace Disk { /*! Provies a @c Disk containing a G64 disk image — a raw but perfectly-clocked GCR stream. */ -class G64: public Disk { +class G64: public Disk, public Storage::FileHolder { public: /*! Construct a @c G64 containing content from the file with name @c file_name. @@ -27,7 +28,6 @@ class G64: public Disk { @throws ErrorUnknownVersion if this file appears to be a .G64 but has an unrecognised version number. */ G64(const char *file_name); - ~G64(); enum { ErrorCantOpen, @@ -40,10 +40,8 @@ class G64: public Disk { std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); private: - FILE *_file; - - uint8_t _number_of_tracks; - uint16_t _maximum_track_size; + uint8_t number_of_tracks_; + uint16_t maximum_track_size_; }; } diff --git a/Storage/Disk/Formats/SSD.cpp b/Storage/Disk/Formats/SSD.cpp index 333fac821..aaa6e467a 100644 --- a/Storage/Disk/Formats/SSD.cpp +++ b/Storage/Disk/Formats/SSD.cpp @@ -13,51 +13,40 @@ using namespace Storage::Disk; -SSD::SSD(const char *file_name) : _file(nullptr) +SSD::SSD(const char *file_name) : + Storage::FileHolder(file_name) { - struct stat file_stats; - stat(file_name, &file_stats); - // very loose validation: the file needs to be a multiple of 256 bytes // and not ungainly large - if(file_stats.st_size & 255) throw ErrorNotSSD; - if(file_stats.st_size < 512) throw ErrorNotSSD; - if(file_stats.st_size > 800*256) throw ErrorNotSSD; - - _file = fopen(file_name, "rb"); - - if(!_file) throw ErrorCantOpen; + if(file_stats_.st_size & 255) throw ErrorNotSSD; + if(file_stats_.st_size < 512) throw ErrorNotSSD; + if(file_stats_.st_size > 800*256) throw ErrorNotSSD; // this has two heads if the suffix is .dsd, one if it's .ssd - _head_count = (tolower(file_name[strlen(file_name) - 3]) == 'd') ? 2 : 1; - _track_count = (unsigned int)(file_stats.st_size / (256 * 10)); - if(_track_count < 40) _track_count = 40; - else if(_track_count < 80) _track_count = 80; -} - -SSD::~SSD() -{ - if(_file) fclose(_file); + head_count_ = (tolower(file_name[strlen(file_name) - 3]) == 'd') ? 2 : 1; + track_count_ = (unsigned int)(file_stats_.st_size / (256 * 10)); + if(track_count_ < 40) track_count_ = 40; + else if(track_count_ < 80) track_count_ = 80; } unsigned int SSD::get_head_position_count() { - return _track_count; + return track_count_; } unsigned int SSD::get_head_count() { - return _head_count; + return head_count_; } std::shared_ptr SSD::get_track_at_position(unsigned int head, unsigned int position) { std::shared_ptr track; - if(head >= _head_count) return track; - long file_offset = (position * _head_count + head) * 256 * 10; - fseek(_file, file_offset, SEEK_SET); + if(head >= head_count_) return track; + long file_offset = (position * head_count_ + head) * 256 * 10; + fseek(file_, file_offset, SEEK_SET); std::vector sectors; for(int sector = 0; sector < 10; sector++) @@ -68,8 +57,8 @@ std::shared_ptr SSD::get_track_at_position(unsigned int head, unsigned in new_sector.sector = (uint8_t)sector; new_sector.data.resize(256); - fread(&new_sector.data[0], 1, 256, _file); - if(feof(_file)) + fread(&new_sector.data[0], 1, 256, file_); + if(feof(file_)) break; sectors.push_back(std::move(new_sector)); diff --git a/Storage/Disk/Formats/SSD.hpp b/Storage/Disk/Formats/SSD.hpp index 543e554ea..074f57af0 100644 --- a/Storage/Disk/Formats/SSD.hpp +++ b/Storage/Disk/Formats/SSD.hpp @@ -10,6 +10,7 @@ #define SSD_hpp #include "../Disk.hpp" +#include "../../FileHolder.hpp" namespace Storage { namespace Disk { @@ -17,7 +18,7 @@ namespace Disk { /*! Provies a @c Disk containing a DSD or SSD disk image — a decoded sector dump of an Acorn DFS disk. */ -class SSD: public Disk { +class SSD: public Disk, public Storage::FileHolder { public: /*! Construct an @c SSD containing content from the file with name @c file_name. @@ -26,10 +27,8 @@ class SSD: public Disk { @throws ErrorNotSSD if the file doesn't appear to contain a .SSD format image. */ SSD(const char *file_name); - ~SSD(); enum { - ErrorCantOpen, ErrorNotSSD, }; @@ -39,9 +38,8 @@ class SSD: public Disk { std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); private: - FILE *_file; - unsigned int _head_count; - unsigned int _track_count; + unsigned int head_count_; + unsigned int track_count_; }; } diff --git a/Storage/FileHolder.cpp b/Storage/FileHolder.cpp new file mode 100644 index 000000000..8b08eaaba --- /dev/null +++ b/Storage/FileHolder.cpp @@ -0,0 +1,23 @@ +// +// FileHolder.cpp +// Clock Signal +// +// Created by Thomas Harte on 21/11/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "FileHolder.hpp" + +using namespace Storage; + +FileHolder::~FileHolder() +{ + if(file_) fclose(file_); +} + +FileHolder::FileHolder(const char *file_name) : file_(nullptr) +{ + stat(file_name, &file_stats_); + file_ = fopen(file_name, "rb"); + if(!file_) throw ErrorCantOpen; +} diff --git a/Storage/FileHolder.hpp b/Storage/FileHolder.hpp new file mode 100644 index 000000000..b31d24790 --- /dev/null +++ b/Storage/FileHolder.hpp @@ -0,0 +1,34 @@ +// +// FileHolder.hpp +// Clock Signal +// +// Created by Thomas Harte on 21/11/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef FileHolder_hpp +#define FileHolder_hpp + +#include +#include + +namespace Storage { + +class FileHolder { + public: + enum { + ErrorCantOpen = -1 + }; + + virtual ~FileHolder(); + + protected: + FileHolder(const char *file_name); + + FILE *file_; + struct stat file_stats_; +}; + +} + +#endif /* FileHolder_hpp */ diff --git a/Storage/Tape/Formats/CommodoreTAP.cpp b/Storage/Tape/Formats/CommodoreTAP.cpp index 9f7c50697..783c2487b 100644 --- a/Storage/Tape/Formats/CommodoreTAP.cpp +++ b/Storage/Tape/Formats/CommodoreTAP.cpp @@ -12,100 +12,92 @@ using namespace Storage::Tape; -CommodoreTAP::CommodoreTAP(const char *file_name) : _is_at_end(false) +CommodoreTAP::CommodoreTAP(const char *file_name) : + is_at_end_(false), + Storage::FileHolder(file_name) { - _file = fopen(file_name, "rb"); - - if(!_file) - throw ErrorNotCommodoreTAP; - // read and check the file signature char signature[12]; - if(fread(signature, 1, 12, _file) != 12) + if(fread(signature, 1, 12, file_) != 12) throw ErrorNotCommodoreTAP; if(memcmp(signature, "C64-TAPE-RAW", 12)) throw ErrorNotCommodoreTAP; // check the file version - int version = fgetc(_file); + int version = fgetc(file_); switch(version) { - case 0: _updated_layout = false; break; - case 1: _updated_layout = true; break; + case 0: updated_layout_ = false; break; + case 1: updated_layout_ = true; break; default: throw ErrorNotCommodoreTAP; } // skip reserved bytes - fseek(_file, 3, SEEK_CUR); + fseek(file_, 3, SEEK_CUR); // read file size - _file_size = (uint32_t)fgetc(_file); - _file_size |= (uint32_t)(fgetc(_file) << 8); - _file_size |= (uint32_t)(fgetc(_file) << 16); - _file_size |= (uint32_t)(fgetc(_file) << 24); + file_size_ = (uint32_t)fgetc(file_); + file_size_ |= (uint32_t)(fgetc(file_) << 8); + file_size_ |= (uint32_t)(fgetc(file_) << 16); + file_size_ |= (uint32_t)(fgetc(file_) << 24); // set up for pulse output at the PAL clock rate, with each high and // low being half of whatever length values will be read; pretend that // a high pulse has just been distributed to imply that the next thing // that needs to happen is a length check - _current_pulse.length.clock_rate = 985248 * 2; - _current_pulse.type = Pulse::High; -} - -CommodoreTAP::~CommodoreTAP() -{ - fclose(_file); + current_pulse_.length.clock_rate = 985248 * 2; + current_pulse_.type = Pulse::High; } void CommodoreTAP::virtual_reset() { - fseek(_file, 0x14, SEEK_SET); - _current_pulse.type = Pulse::High; - _is_at_end = false; + fseek(file_, 0x14, SEEK_SET); + current_pulse_.type = Pulse::High; + is_at_end_ = false; } bool CommodoreTAP::is_at_end() { - return _is_at_end; + return is_at_end_; } Storage::Tape::Tape::Pulse CommodoreTAP::virtual_get_next_pulse() { - if(_is_at_end) + if(is_at_end_) { - return _current_pulse; + return current_pulse_; } - if(_current_pulse.type == Pulse::High) + if(current_pulse_.type == Pulse::High) { uint32_t next_length; - uint8_t next_byte = (uint8_t)fgetc(_file); - if(!_updated_layout || next_byte > 0) + uint8_t next_byte = (uint8_t)fgetc(file_); + if(!updated_layout_ || next_byte > 0) { next_length = (uint32_t)next_byte << 3; } else { - next_length = (uint32_t)fgetc(_file); - next_length |= (uint32_t)(fgetc(_file) << 8); - next_length |= (uint32_t)(fgetc(_file) << 16); + next_length = (uint32_t)fgetc(file_); + next_length |= (uint32_t)(fgetc(file_) << 8); + next_length |= (uint32_t)(fgetc(file_) << 16); } - if(feof(_file)) + if(feof(file_)) { - _is_at_end = true; - _current_pulse.length.length = _current_pulse.length.clock_rate; - _current_pulse.type = Pulse::Zero; + is_at_end_ = true; + current_pulse_.length.length = current_pulse_.length.clock_rate; + current_pulse_.type = Pulse::Zero; } else { - _current_pulse.length.length = next_length; - _current_pulse.type = Pulse::Low; + current_pulse_.length.length = next_length; + current_pulse_.type = Pulse::Low; } } else - _current_pulse.type = Pulse::High; + current_pulse_.type = Pulse::High; - return _current_pulse; + return current_pulse_; } diff --git a/Storage/Tape/Formats/CommodoreTAP.hpp b/Storage/Tape/Formats/CommodoreTAP.hpp index 0f62e8040..b55feac55 100644 --- a/Storage/Tape/Formats/CommodoreTAP.hpp +++ b/Storage/Tape/Formats/CommodoreTAP.hpp @@ -10,7 +10,8 @@ #define CommodoreTAP_hpp #include "../Tape.hpp" -#include +#include "../../FileHolder.hpp" +#include namespace Storage { namespace Tape { @@ -18,7 +19,7 @@ namespace Tape { /*! Provides a @c Tape containing a Commodore-format tape image, which is simply a timed list of downward-going zero crossings. */ -class CommodoreTAP: public Tape { +class CommodoreTAP: public Tape, public Storage::FileHolder { public: /*! Constructs a @c CommodoreTAP containing content from the file with name @c file_name. @@ -26,7 +27,6 @@ class CommodoreTAP: public Tape { @throws ErrorNotCommodoreTAP if this file could not be opened and recognised as a valid Commodore-format TAP. */ CommodoreTAP(const char *file_name); - ~CommodoreTAP(); enum { ErrorNotCommodoreTAP @@ -39,12 +39,11 @@ class CommodoreTAP: public Tape { void virtual_reset(); Pulse virtual_get_next_pulse(); - FILE *_file; - bool _updated_layout; - uint32_t _file_size; + bool updated_layout_; + uint32_t file_size_; - Pulse _current_pulse; - bool _is_at_end; + Pulse current_pulse_; + bool is_at_end_; }; } diff --git a/Storage/Tape/Formats/OricTAP.cpp b/Storage/Tape/Formats/OricTAP.cpp index 81ff63dda..b540efcfc 100644 --- a/Storage/Tape/Formats/OricTAP.cpp +++ b/Storage/Tape/Formats/OricTAP.cpp @@ -12,20 +12,12 @@ using namespace Storage::Tape; -OricTAP::OricTAP(const char *file_name) : _file(NULL) +OricTAP::OricTAP(const char *file_name) : + Storage::FileHolder(file_name) { - struct stat file_stats; - stat(file_name, &file_stats); - _file_length = (size_t)file_stats.st_size; - - _file = fopen(file_name, "rb"); - - if(!_file) - throw ErrorNotOricTAP; - // read and check the file signature uint8_t signature[4]; - if(fread(signature, 1, 4, _file) != 4) + if(fread(signature, 1, 4, file_) != 4) throw ErrorNotOricTAP; if(signature[0] != 0x16 || signature[1] != 0x16 || signature[2] != 0x16 || signature[3] != 0x24) @@ -35,45 +27,40 @@ OricTAP::OricTAP(const char *file_name) : _file(NULL) virtual_reset(); } -OricTAP::~OricTAP() -{ - if(_file) fclose(_file); -} - void OricTAP::virtual_reset() { - fseek(_file, 0, SEEK_SET); - _bit_count = 13; - _phase = _next_phase = LeadIn; - _phase_counter = 0; - _pulse_counter = 0; + fseek(file_, 0, SEEK_SET); + bit_count_ = 13; + phase_ = next_phase_ = LeadIn; + phase_counter_ = 0; + pulse_counter_ = 0; } Tape::Pulse OricTAP::virtual_get_next_pulse() { // Each byte byte is written as 13 bits: 0, eight bits of data, parity, three 1s. - if(_bit_count == 13) + if(bit_count_ == 13) { - if(_next_phase != _phase) + if(next_phase_ != phase_) { - _phase = _next_phase; - _phase_counter = 0; + phase_ = next_phase_; + phase_counter_ = 0; } - _bit_count = 0; + bit_count_ = 0; uint8_t next_byte = 0; - switch(_phase) + switch(phase_) { case LeadIn: - next_byte = _phase_counter < 258 ? 0x16 : 0x24; - _phase_counter++; - if(_phase_counter == 259) // 256 artificial bytes plus the three in the file = 259 + next_byte = phase_counter_ < 258 ? 0x16 : 0x24; + phase_counter_++; + if(phase_counter_ == 259) // 256 artificial bytes plus the three in the file = 259 { while(1) { - if(fgetc(_file) != 0x16) break; + if(fgetc(file_) != 0x16) break; } - _next_phase = Header; + next_phase_ = Header; } break; @@ -86,44 +73,44 @@ Tape::Pulse OricTAP::virtual_get_next_pulse() // [6, 7]: start address of data // 8: "unused" (on the Oric 1) // [9...]: filename, up to NULL byte - next_byte = (uint8_t)fgetc(_file); + next_byte = (uint8_t)fgetc(file_); - if(_phase_counter == 4) _data_end_address = (uint16_t)(next_byte << 8); - if(_phase_counter == 5) _data_end_address |= next_byte; - if(_phase_counter == 6) _data_start_address = (uint16_t)(next_byte << 8); - if(_phase_counter == 7) _data_start_address |= next_byte; + if(phase_counter_ == 4) data_end_address_ = (uint16_t)(next_byte << 8); + if(phase_counter_ == 5) data_end_address_ |= next_byte; + if(phase_counter_ == 6) data_start_address_ = (uint16_t)(next_byte << 8); + if(phase_counter_ == 7) data_start_address_ |= next_byte; - if(_phase_counter >= 9 && !next_byte) // advance after the filename-ending NULL byte + if(phase_counter_ >= 9 && !next_byte) // advance after the filename-ending NULL byte { - _next_phase = Gap; + next_phase_ = Gap; } - if(feof(_file)) + if(feof(file_)) { - _next_phase = End; + next_phase_ = End; } - _phase_counter++; + phase_counter_++; break; case Gap: - _phase_counter++; - if(_phase_counter == 8) + phase_counter_++; + if(phase_counter_ == 8) { - _next_phase = Data; + next_phase_ = Data; } break; case Data: - next_byte = (uint8_t)fgetc(_file); - _phase_counter++; - if(_phase_counter >= (_data_end_address - _data_start_address)+1) + next_byte = (uint8_t)fgetc(file_); + phase_counter_++; + if(phase_counter_ >= (data_end_address_ - data_start_address_)+1) { if(next_byte == 0x16) { - _next_phase = LeadIn; + next_phase_ = LeadIn; } - else if(feof(_file)) + else if(feof(file_)) { - _next_phase = End; + next_phase_ = End; } } break; @@ -136,7 +123,7 @@ Tape::Pulse OricTAP::virtual_get_next_pulse() parity ^= (parity >> 4); parity ^= (parity >> 2); parity ^= (parity >> 1); - _current_value = (uint16_t)(((uint16_t)next_byte << 1) | ((parity&1) << 9) | (7 << 10)); + current_value_ = (uint16_t)(((uint16_t)next_byte << 1) | ((parity&1) << 9) | (7 << 10)); } // In slow mode, a 0 is 4 periods of 1200 Hz, a 1 is 8 periods at 2400 Hz. @@ -146,7 +133,7 @@ Tape::Pulse OricTAP::virtual_get_next_pulse() pulse.length.clock_rate = 4800; int next_bit; - switch(_phase) + switch(phase_) { case End: pulse.type = Pulse::Zero; @@ -154,13 +141,13 @@ Tape::Pulse OricTAP::virtual_get_next_pulse() return pulse; case Gap: - _bit_count = 13; - pulse.type = (_phase_counter&1) ? Pulse::Low : Pulse::High; + bit_count_ = 13; + pulse.type = (phase_counter_&1) ? Pulse::Low : Pulse::High; pulse.length.length = 100; return pulse; default: - next_bit = _current_value & 1; + next_bit = current_value_ & 1; break; } @@ -170,20 +157,20 @@ Tape::Pulse OricTAP::virtual_get_next_pulse() } else { - pulse.length.length = _pulse_counter ? 2 : 1; + pulse.length.length = pulse_counter_ ? 2 : 1; } - pulse.type = _pulse_counter ? Pulse::High : Pulse::Low; // TODO + pulse.type = pulse_counter_ ? Pulse::High : Pulse::Low; // TODO - _pulse_counter ^= 1; - if(!_pulse_counter) + pulse_counter_ ^= 1; + if(!pulse_counter_) { - _current_value >>= 1; - _bit_count++; + current_value_ >>= 1; + bit_count_++; } return pulse; } bool OricTAP::is_at_end() { - return _phase == End; + return phase_ == End; } diff --git a/Storage/Tape/Formats/OricTAP.hpp b/Storage/Tape/Formats/OricTAP.hpp index f425b6541..53b56a7f2 100644 --- a/Storage/Tape/Formats/OricTAP.hpp +++ b/Storage/Tape/Formats/OricTAP.hpp @@ -10,7 +10,8 @@ #define OricTAP_hpp #include "../Tape.hpp" -#include +#include "../../FileHolder.hpp" +#include namespace Storage { namespace Tape { @@ -18,7 +19,7 @@ namespace Tape { /*! Provides a @c Tape containing an Oric-format tape image, which is a byte stream capture. */ -class OricTAP: public Tape { +class OricTAP: public Tape, public Storage::FileHolder { public: /*! Constructs an @c OricTAP containing content from the file with name @c file_name. @@ -26,7 +27,6 @@ class OricTAP: public Tape { @throws ErrorNotOricTAP if this file could not be opened and recognised as a valid Oric-format TAP. */ OricTAP(const char *file_name); - ~OricTAP(); enum { ErrorNotOricTAP @@ -39,19 +39,16 @@ class OricTAP: public Tape { void virtual_reset(); Pulse virtual_get_next_pulse(); - FILE *_file; - size_t _file_length; - // byte serialisation and output - uint16_t _current_value; - int _bit_count; - int _pulse_counter; + uint16_t current_value_; + int bit_count_; + int pulse_counter_; enum Phase { LeadIn, Header, Data, Gap, End - } _phase, _next_phase; - int _phase_counter; - uint16_t _data_end_address, _data_start_address; + } phase_, next_phase_; + int phase_counter_; + uint16_t data_end_address_, data_start_address_; }; } diff --git a/Storage/Tape/Formats/TapePRG.cpp b/Storage/Tape/Formats/TapePRG.cpp index 7bba22e35..1e4cd278a 100644 --- a/Storage/Tape/Formats/TapePRG.cpp +++ b/Storage/Tape/Formats/TapePRG.cpp @@ -48,32 +48,26 @@ using namespace Storage::Tape; -PRG::PRG(const char *file_name) : _file(nullptr), _bitPhase(3), _filePhase(FilePhaseLeadIn), _phaseOffset(0), _copy_mask(0x80) +PRG::PRG(const char *file_name) : + bit_phase_(3), + file_phase_(FilePhaseLeadIn), + phase_offset_(0), + copy_mask_(0x80), + Storage::FileHolder(file_name) { - struct stat file_stats; - stat(file_name, &file_stats); - // There's really no way to validate other than that if this file is larger than 64kb, // of if load address + length > 65536 then it's broken. - if(file_stats.st_size >= 65538 || file_stats.st_size < 3) + if(file_stats_.st_size >= 65538 || file_stats_.st_size < 3) throw ErrorBadFormat; - _file = fopen(file_name, "rb"); - if(!_file) throw ErrorBadFormat; + load_address_ = (uint16_t)fgetc(file_); + load_address_ |= (uint16_t)fgetc(file_) << 8; + length_ = (uint16_t)(file_stats_.st_size - 2); - _load_address = (uint16_t)fgetc(_file); - _load_address |= (uint16_t)fgetc(_file) << 8; - _length = (uint16_t)(file_stats.st_size - 2); - - if (_load_address + _length >= 65536) + if (load_address_ + length_ >= 65536) throw ErrorBadFormat; } -PRG::~PRG() -{ - if(_file) fclose(_file); -} - Storage::Tape::Tape::Pulse PRG::virtual_get_next_pulse() { // these are all microseconds per pole @@ -82,19 +76,19 @@ Storage::Tape::Tape::Pulse PRG::virtual_get_next_pulse() static const unsigned int one_length = 247; static const unsigned int marker_length = 328; - _bitPhase = (_bitPhase+1)&3; - if(!_bitPhase) get_next_output_token(); + bit_phase_ = (bit_phase_+1)&3; + if(!bit_phase_) get_next_output_token(); Tape::Pulse pulse; pulse.length.clock_rate = 1000000; - pulse.type = (_bitPhase&1) ? Tape::Pulse::High : Tape::Pulse::Low; - switch(_outputToken) + pulse.type = (bit_phase_&1) ? Tape::Pulse::High : Tape::Pulse::Low; + switch(output_token_) { case Leader: pulse.length.length = leader_zero_length; break; - case Zero: pulse.length.length = (_bitPhase&2) ? one_length : zero_length; break; - case One: pulse.length.length = (_bitPhase&2) ? zero_length : one_length; break; - case WordMarker: pulse.length.length = (_bitPhase&2) ? one_length : marker_length; break; - case EndOfBlock: pulse.length.length = (_bitPhase&2) ? zero_length : marker_length; break; + case Zero: pulse.length.length = (bit_phase_&2) ? one_length : zero_length; break; + case One: pulse.length.length = (bit_phase_&2) ? zero_length : one_length; break; + case WordMarker: pulse.length.length = (bit_phase_&2) ? one_length : marker_length; break; + case EndOfBlock: pulse.length.length = (bit_phase_&2) ? zero_length : marker_length; break; case Silence: pulse.type = Tape::Pulse::Zero; pulse.length.length = 5000; break; } return pulse; @@ -102,16 +96,16 @@ Storage::Tape::Tape::Pulse PRG::virtual_get_next_pulse() void PRG::virtual_reset() { - _bitPhase = 3; - fseek(_file, 2, SEEK_SET); - _filePhase = FilePhaseLeadIn; - _phaseOffset = 0; - _copy_mask = 0x80; + bit_phase_ = 3; + fseek(file_, 2, SEEK_SET); + file_phase_ = FilePhaseLeadIn; + phase_offset_ = 0; + copy_mask_ = 0x80; } bool PRG::is_at_end() { - return _filePhase == FilePhaseAtEnd; + return file_phase_ == FilePhaseAtEnd; } void PRG::get_next_output_token() @@ -121,54 +115,54 @@ void PRG::get_next_output_token() static const int leadin_length = 20000; static const int block_leadin_length = 5000; - if(_filePhase == FilePhaseHeaderDataGap || _filePhase == FilePhaseAtEnd) + if(file_phase_ == FilePhaseHeaderDataGap || file_phase_ == FilePhaseAtEnd) { - _outputToken = Silence; - if(_filePhase != FilePhaseAtEnd) _filePhase = FilePhaseData; + output_token_ = Silence; + if(file_phase_ != FilePhaseAtEnd) file_phase_ = FilePhaseData; return; } // the lead-in is 20,000 instances of the lead-in pair; every other phase begins with 5000 // before doing whatever it should be doing - if(_filePhase == FilePhaseLeadIn || _phaseOffset < block_leadin_length) + if(file_phase_ == FilePhaseLeadIn || phase_offset_ < block_leadin_length) { - _outputToken = Leader; - _phaseOffset++; - if(_filePhase == FilePhaseLeadIn && _phaseOffset == leadin_length) + output_token_ = Leader; + phase_offset_++; + if(file_phase_ == FilePhaseLeadIn && phase_offset_ == leadin_length) { - _phaseOffset = 0; - _filePhase = (_filePhase == FilePhaseLeadIn) ? FilePhaseHeader : FilePhaseData; + phase_offset_ = 0; + file_phase_ = (file_phase_ == FilePhaseLeadIn) ? FilePhaseHeader : FilePhaseData; } return; } // determine whether a new byte needs to be queued up - int block_offset = _phaseOffset - block_leadin_length; + int block_offset = phase_offset_ - block_leadin_length; int bit_offset = block_offset % 10; int byte_offset = block_offset / 10; - _phaseOffset++; + phase_offset_++; if(!bit_offset && ( - (_filePhase == FilePhaseHeader && byte_offset == block_length + countdown_bytes + 1) || - feof(_file) + (file_phase_ == FilePhaseHeader && byte_offset == block_length + countdown_bytes + 1) || + feof(file_) ) ) { - _outputToken = EndOfBlock; - _phaseOffset = 0; + output_token_ = EndOfBlock; + phase_offset_ = 0; - switch(_filePhase) + switch(file_phase_) { default: break; case FilePhaseHeader: - _copy_mask ^= 0x80; - if(_copy_mask) _filePhase = FilePhaseHeaderDataGap; + copy_mask_ ^= 0x80; + if(copy_mask_) file_phase_ = FilePhaseHeaderDataGap; break; case FilePhaseData: - _copy_mask ^= 0x80; - fseek(_file, 2, SEEK_SET); - if(_copy_mask) _filePhase = FilePhaseAtEnd; + copy_mask_ ^= 0x80; + fseek(file_, 2, SEEK_SET); + if(copy_mask_) file_phase_ = FilePhaseAtEnd; break; } return; @@ -179,34 +173,34 @@ void PRG::get_next_output_token() // the first nine bytes are countdown; the high bit is set if this is a header if(byte_offset < countdown_bytes) { - _output_byte = (uint8_t)(countdown_bytes - byte_offset) | _copy_mask; + output_byte_ = (uint8_t)(countdown_bytes - byte_offset) | copy_mask_; } else { - if(_filePhase == FilePhaseHeader) + if(file_phase_ == FilePhaseHeader) { if(byte_offset == countdown_bytes + block_length) { - _output_byte = _check_digit; + output_byte_ = check_digit_; } else { - if(byte_offset == countdown_bytes) _check_digit = 0; - if(_filePhase == FilePhaseHeader) + if(byte_offset == countdown_bytes) check_digit_ = 0; + if(file_phase_ == FilePhaseHeader) { switch(byte_offset - countdown_bytes) { - case 0: _output_byte = 0x03; break; - case 1: _output_byte = _load_address & 0xff; break; - case 2: _output_byte = (_load_address >> 8)&0xff; break; - case 3: _output_byte = (_load_address + _length) & 0xff; break; - case 4: _output_byte = ((_load_address + _length) >> 8) & 0xff; break; + case 0: output_byte_ = 0x03; break; + case 1: output_byte_ = load_address_ & 0xff; break; + case 2: output_byte_ = (load_address_ >> 8)&0xff; break; + case 3: output_byte_ = (load_address_ + length_) & 0xff; break; + case 4: output_byte_ = ((load_address_ + length_) >> 8) & 0xff; break; - case 5: _output_byte = 0x50; break; // P - case 6: _output_byte = 0x52; break; // R - case 7: _output_byte = 0x47; break; // G + case 5: output_byte_ = 0x50; break; // P + case 6: output_byte_ = 0x52; break; // R + case 7: output_byte_ = 0x47; break; // G default: - _output_byte = 0x20; + output_byte_ = 0x20; break; } } @@ -214,32 +208,32 @@ void PRG::get_next_output_token() } else { - _output_byte = (uint8_t)fgetc(_file); - if(feof(_file)) + output_byte_ = (uint8_t)fgetc(file_); + if(feof(file_)) { - _output_byte = _check_digit; + output_byte_ = check_digit_; } } - _check_digit ^= _output_byte; + check_digit_ ^= output_byte_; } } switch(bit_offset) { case 0: - _outputToken = WordMarker; + output_token_ = WordMarker; break; default: // i.e. 1–8 - _outputToken = (_output_byte & (1 << (bit_offset - 1))) ? One : Zero; + output_token_ = (output_byte_ & (1 << (bit_offset - 1))) ? One : Zero; break; case 9: { - uint8_t parity = _output_byte; + uint8_t parity = output_byte_; parity ^= (parity >> 4); parity ^= (parity >> 2); parity ^= (parity >> 1); - _outputToken = (parity&1) ? Zero : One; + output_token_ = (parity&1) ? Zero : One; } break; } diff --git a/Storage/Tape/Formats/TapePRG.hpp b/Storage/Tape/Formats/TapePRG.hpp index f092532ed..c2be2dc9a 100644 --- a/Storage/Tape/Formats/TapePRG.hpp +++ b/Storage/Tape/Formats/TapePRG.hpp @@ -10,7 +10,8 @@ #define Storage_Tape_PRG_hpp #include "../Tape.hpp" -#include +#include "../../FileHolder.hpp" +#include namespace Storage { namespace Tape { @@ -18,7 +19,7 @@ namespace Tape { /*! Provides a @c Tape containing a .PRG, which is a direct local file. */ -class PRG: public Tape { +class PRG: public Tape, public Storage::FileHolder { public: /*! Constructs a @c T64 containing content from the file with name @c file_name, of type @c type. @@ -28,7 +29,6 @@ class PRG: public Tape { @throws ErrorBadFormat if this file could not be opened and recognised as the specified type. */ PRG(const char *file_name); - ~PRG(); enum { ErrorBadFormat @@ -41,9 +41,8 @@ class PRG: public Tape { Pulse virtual_get_next_pulse(); void virtual_reset(); - FILE *_file; - uint16_t _load_address; - uint16_t _length; + uint16_t load_address_; + uint16_t length_; enum FilePhase { FilePhaseLeadIn, @@ -51,10 +50,10 @@ class PRG: public Tape { FilePhaseHeaderDataGap, FilePhaseData, FilePhaseAtEnd - } _filePhase; - int _phaseOffset; + } file_phase_; + int phase_offset_; - int _bitPhase; + int bit_phase_; enum OutputToken { Leader, Zero, @@ -62,11 +61,11 @@ class PRG: public Tape { WordMarker, EndOfBlock, Silence - } _outputToken; + } output_token_; void get_next_output_token(); - uint8_t _output_byte; - uint8_t _check_digit; - uint8_t _copy_mask; + uint8_t output_byte_; + uint8_t check_digit_; + uint8_t copy_mask_; }; } diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index 3fe5d32cc..7a6926eb8 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -74,22 +74,22 @@ static int gzget32(gzFile file) using namespace Storage::Tape; UEF::UEF(const char *file_name) : - _time_base(1200), - _is_at_end(false), - _pulse_pointer(0), - _is_300_baud(false) + time_base_(1200), + is_at_end_(false), + pulse_pointer_(0), + is_300_baud_(false) { - _file = gzopen(file_name, "rb"); + file_ = gzopen(file_name, "rb"); char identifier[10]; - int bytes_read = gzread(_file, identifier, 10); + int bytes_read = gzread(file_, identifier, 10); if(bytes_read < 10 || strcmp(identifier, "UEF File!")) { throw ErrorNotUEF; } uint8_t version[2]; - gzread(_file, version, 2); + gzread(file_, version, 2); if(version[1] > 0 || version[0] > 10) { @@ -101,41 +101,41 @@ UEF::UEF(const char *file_name) : UEF::~UEF() { - gzclose(_file); + gzclose(file_); } #pragma mark - Public methods void UEF::virtual_reset() { - gzseek(_file, 12, SEEK_SET); - _is_at_end = false; + gzseek(file_, 12, SEEK_SET); + is_at_end_ = false; parse_next_tape_chunk(); } bool UEF::is_at_end() { - return _is_at_end; + return is_at_end_; } Storage::Tape::Tape::Pulse UEF::virtual_get_next_pulse() { Pulse next_pulse; - if(_is_at_end) + if(is_at_end_) { next_pulse.type = Pulse::Zero; - next_pulse.length.length = _time_base * 4; - next_pulse.length.clock_rate = _time_base * 4; + next_pulse.length.length = time_base_ * 4; + next_pulse.length.clock_rate = time_base_ * 4; return next_pulse; } - next_pulse = _queued_pulses[_pulse_pointer]; - _pulse_pointer++; - if(_pulse_pointer == _queued_pulses.size()) + next_pulse = queued_pulses_[pulse_pointer_]; + pulse_pointer_++; + if(pulse_pointer_ == queued_pulses_.size()) { - _queued_pulses.clear(); - _pulse_pointer = 0; + queued_pulses_.clear(); + pulse_pointer_ = 0; parse_next_tape_chunk(); } return next_pulse; @@ -145,18 +145,18 @@ Storage::Tape::Tape::Pulse UEF::virtual_get_next_pulse() void UEF::parse_next_tape_chunk() { - while(!_queued_pulses.size()) + while(queued_pulses_.empty()) { // read chunk details - uint16_t chunk_id = (uint16_t)gzget16(_file); - uint32_t chunk_length = (uint32_t)gzget32(_file); + uint16_t chunk_id = (uint16_t)gzget16(file_); + uint32_t chunk_length = (uint32_t)gzget32(file_); // figure out where the next chunk will start - z_off_t start_of_next_chunk = gztell(_file) + chunk_length; + z_off_t start_of_next_chunk = gztell(file_) + chunk_length; - if(gzeof(_file)) + if(gzeof(file_)) { - _is_at_end = true; + is_at_end_ = true; return; } @@ -176,15 +176,15 @@ void UEF::parse_next_tape_chunk() case 0x0113: // change of base rate { // TODO: something smarter than just converting this to an int - float new_time_base = gzgetfloat(_file); - _time_base = (unsigned int)roundf(new_time_base); + float new_time_base = gzgetfloat(file_); + time_base_ = (unsigned int)roundf(new_time_base); } break; case 0x0117: { - int baud_rate = gzget16(_file); - _is_300_baud = (baud_rate == 300); + int baud_rate = gzget16(file_); + is_300_baud_ = (baud_rate == 300); } break; @@ -193,7 +193,7 @@ void UEF::parse_next_tape_chunk() break; } - gzseek(_file, start_of_next_chunk, SEEK_SET); + gzseek(file_, start_of_next_chunk, SEEK_SET); } } @@ -203,17 +203,17 @@ void UEF::queue_implicit_bit_pattern(uint32_t length) { while(length--) { - queue_implicit_byte(gzget8(_file)); + queue_implicit_byte(gzget8(file_)); } } void UEF::queue_explicit_bit_pattern(uint32_t length) { - size_t length_in_bits = (length << 3) - (size_t)gzget8(_file); + size_t length_in_bits = (length << 3) - (size_t)gzget8(file_); uint8_t current_byte = 0; for(size_t bit = 0; bit < length_in_bits; bit++) { - if(!(bit&7)) current_byte = gzget8(_file); + if(!(bit&7)) current_byte = gzget8(file_); queue_bit(current_byte&1); current_byte >>= 1; } @@ -222,30 +222,30 @@ void UEF::queue_explicit_bit_pattern(uint32_t length) void UEF::queue_integer_gap() { Time duration; - duration.length = (unsigned int)gzget16(_file); - duration.clock_rate = _time_base; - _queued_pulses.emplace_back(Pulse::Zero, duration); + duration.length = (unsigned int)gzget16(file_); + duration.clock_rate = time_base_; + queued_pulses_.emplace_back(Pulse::Zero, duration); } void UEF::queue_floating_point_gap() { - float length = gzgetfloat(_file); + float length = gzgetfloat(file_); Time duration; duration.length = (unsigned int)(length * 4000000); duration.clock_rate = 4000000; - _queued_pulses.emplace_back(Pulse::Zero, duration); + queued_pulses_.emplace_back(Pulse::Zero, duration); } void UEF::queue_carrier_tone() { - unsigned int number_of_cycles = (unsigned int)gzget16(_file); + unsigned int number_of_cycles = (unsigned int)gzget16(file_); while(number_of_cycles--) queue_bit(1); } void UEF::queue_carrier_tone_with_dummy() { - unsigned int pre_cycles = (unsigned int)gzget16(_file); - unsigned int post_cycles = (unsigned int)gzget16(_file); + unsigned int pre_cycles = (unsigned int)gzget16(file_); + unsigned int post_cycles = (unsigned int)gzget16(file_); while(pre_cycles--) queue_bit(1); queue_implicit_byte(0xaa); while(post_cycles--) queue_bit(1); @@ -253,33 +253,33 @@ void UEF::queue_carrier_tone_with_dummy() void UEF::queue_security_cycles() { - int number_of_cycles = gzget24(_file); - bool first_is_pulse = gzget8(_file) == 'P'; - bool last_is_pulse = gzget8(_file) == 'P'; + int number_of_cycles = gzget24(file_); + bool first_is_pulse = gzget8(file_) == 'P'; + bool last_is_pulse = gzget8(file_) == 'P'; uint8_t current_byte = 0; for(int cycle = 0; cycle < number_of_cycles; cycle++) { - if(!(cycle&7)) current_byte = gzget8(_file); + if(!(cycle&7)) current_byte = gzget8(file_); int bit = (current_byte >> 7); current_byte <<= 1; Time duration; duration.length = bit ? 1 : 2; - duration.clock_rate = _time_base * 4; + duration.clock_rate = time_base_ * 4; if(!cycle && first_is_pulse) { - _queued_pulses.emplace_back(Pulse::High, duration); + queued_pulses_.emplace_back(Pulse::High, duration); } else if(cycle == number_of_cycles-1 && last_is_pulse) { - _queued_pulses.emplace_back(Pulse::Low, duration); + queued_pulses_.emplace_back(Pulse::Low, duration); } else { - _queued_pulses.emplace_back(Pulse::Low, duration); - _queued_pulses.emplace_back(Pulse::High, duration); + queued_pulses_.emplace_back(Pulse::Low, duration); + queued_pulses_.emplace_back(Pulse::High, duration); } } } @@ -288,9 +288,9 @@ void UEF::queue_defined_data(uint32_t length) { if(length < 3) return; - int bits_per_packet = gzget8(_file); - char parity_type = (char)gzget8(_file); - int number_of_stop_bits = gzget8(_file); + int bits_per_packet = gzget8(file_); + char parity_type = (char)gzget8(file_); + int number_of_stop_bits = gzget8(file_); bool has_extra_stop_wave = (number_of_stop_bits < 0); number_of_stop_bits = abs(number_of_stop_bits); @@ -298,7 +298,7 @@ void UEF::queue_defined_data(uint32_t length) length -= 3; while(length--) { - uint8_t byte = gzget8(_file); + uint8_t byte = gzget8(file_); uint8_t parity_value = byte; parity_value ^= (parity_value >> 4); @@ -326,9 +326,9 @@ void UEF::queue_defined_data(uint32_t length) { Time duration; duration.length = 1; - duration.clock_rate = _time_base * 4; - _queued_pulses.emplace_back(Pulse::Low, duration); - _queued_pulses.emplace_back(Pulse::High, duration); + duration.clock_rate = time_base_ * 4; + queued_pulses_.emplace_back(Pulse::Low, duration); + queued_pulses_.emplace_back(Pulse::High, duration); } } } @@ -351,7 +351,7 @@ void UEF::queue_bit(int bit) { int number_of_cycles; Time duration; - duration.clock_rate = _time_base * 4; + duration.clock_rate = time_base_ * 4; if(bit) { @@ -366,11 +366,11 @@ void UEF::queue_bit(int bit) number_of_cycles = 1; } - if(_is_300_baud) number_of_cycles *= 4; + if(is_300_baud_) number_of_cycles *= 4; while(number_of_cycles--) { - _queued_pulses.emplace_back(Pulse::Low, duration); - _queued_pulses.emplace_back(Pulse::High, duration); + queued_pulses_.emplace_back(Pulse::Low, duration); + queued_pulses_.emplace_back(Pulse::High, duration); } } diff --git a/Storage/Tape/Formats/TapeUEF.hpp b/Storage/Tape/Formats/TapeUEF.hpp index 306f5273a..300dff29a 100644 --- a/Storage/Tape/Formats/TapeUEF.hpp +++ b/Storage/Tape/Formats/TapeUEF.hpp @@ -41,13 +41,13 @@ class UEF : public Tape { void virtual_reset(); Pulse virtual_get_next_pulse(); - gzFile _file; - unsigned int _time_base; - bool _is_at_end; - bool _is_300_baud; + gzFile file_; + unsigned int time_base_; + bool is_at_end_; + bool is_300_baud_; - std::vector _queued_pulses; - size_t _pulse_pointer; + std::vector queued_pulses_; + size_t pulse_pointer_; void parse_next_tape_chunk(); From 8499783b1424f7bd95746315d283190ac19fbbb0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 21 Nov 2016 20:47:16 +0800 Subject: [PATCH 2/2] Dragged multibyte primitives and signature checks up to the base class. Implemented support for Oric MFM-style .DSK, at the file format level. --- .../Clock Signal.xcodeproj/project.pbxproj | 18 ++++-- OSBindings/Mac/Clock Signal/Info.plist | 14 ++++- StaticAnalyser/StaticAnalyser.cpp | 2 + Storage/Disk/Formats/G64.cpp | 22 ++----- Storage/Disk/Formats/OricMFMDSK.cpp | 59 +++++++++++++++++++ Storage/Disk/Formats/OricMFMDSK.hpp | 49 +++++++++++++++ Storage/FileHolder.cpp | 58 ++++++++++++++++++ Storage/FileHolder.hpp | 40 +++++++++++++ Storage/Tape/Formats/CommodoreTAP.cpp | 16 +---- Storage/Tape/Formats/OricTAP.cpp | 8 +-- Storage/Tape/Formats/TapePRG.cpp | 3 +- 11 files changed, 244 insertions(+), 45 deletions(-) create mode 100644 Storage/Disk/Formats/OricMFMDSK.cpp create mode 100644 Storage/Disk/Formats/OricMFMDSK.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 39f28c2c8..7d87faa4b 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -54,6 +54,7 @@ 4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B59199A1DAC6C46005BB85C /* OricTAP.cpp */; }; 4B5A12571DD55862007A2231 /* Disassembler6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5A12551DD55862007A2231 /* Disassembler6502.cpp */; }; 4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */; }; + 4B5FADBD1DE31D1500AEC565 /* OricMFMDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADBB1DE31D1500AEC565 /* OricMFMDSK.cpp */; }; 4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */; }; 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; }; 4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; }; @@ -502,6 +503,8 @@ 4B5A12561DD55862007A2231 /* Disassembler6502.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Disassembler6502.hpp; path = ../../StaticAnalyser/Disassembler/Disassembler6502.hpp; sourceTree = ""; }; 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileHolder.cpp; sourceTree = ""; }; 4B5FADB91DE3151600AEC565 /* FileHolder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FileHolder.hpp; sourceTree = ""; }; + 4B5FADBB1DE31D1500AEC565 /* OricMFMDSK.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OricMFMDSK.cpp; sourceTree = ""; }; + 4B5FADBC1DE31D1500AEC565 /* OricMFMDSK.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OricMFMDSK.hpp; sourceTree = ""; }; 4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = ""; }; 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = ""; }; 4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = ""; }; @@ -1280,14 +1283,16 @@ 4BAB62B21D327F7E00DF5BA0 /* Formats */ = { isa = PBXGroup; children = ( - 4BAB62B31D327F7E00DF5BA0 /* G64.cpp */, - 4BAB62B41D327F7E00DF5BA0 /* G64.hpp */, - 4B4C836E1D4F623200CD541F /* D64.cpp */, - 4B4C836F1D4F623200CD541F /* D64.hpp */, - 4BF829611D8F536B001BAE39 /* SSD.cpp */, - 4BF829621D8F536B001BAE39 /* SSD.hpp */, 4BD69F921D98760000243FE1 /* AcornADF.cpp */, 4BD69F931D98760000243FE1 /* AcornADF.hpp */, + 4B4C836E1D4F623200CD541F /* D64.cpp */, + 4B4C836F1D4F623200CD541F /* D64.hpp */, + 4BAB62B31D327F7E00DF5BA0 /* G64.cpp */, + 4BAB62B41D327F7E00DF5BA0 /* G64.hpp */, + 4B5FADBB1DE31D1500AEC565 /* OricMFMDSK.cpp */, + 4B5FADBC1DE31D1500AEC565 /* OricMFMDSK.hpp */, + 4BF829611D8F536B001BAE39 /* SSD.cpp */, + 4BF829621D8F536B001BAE39 /* SSD.hpp */, ); path = Formats; sourceTree = ""; @@ -2333,6 +2338,7 @@ 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */, 4BF8295D1D8F048B001BAE39 /* MFM.cpp in Sources */, 4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */, + 4B5FADBD1DE31D1500AEC565 /* OricMFMDSK.cpp in Sources */, 4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */, 4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */, 4BBF99141C8FBA6F0075DAFB /* TextureBuilder.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index bdc20fa2b..b7aca5989 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -49,7 +49,7 @@ rom CFBundleTypeName - Electron/BBC ROM Image + ROM Image CFBundleTypeRole Viewer LSItemContentTypes @@ -146,6 +146,18 @@ NSDocumentClass $(PRODUCT_MODULE_NAME).MachineDocument + + CFBundleTypeExtensions + + dsk + + CFBundleTypeName + Disk Image + CFBundleTypeRole + Viewer + NSDocumentClass + $(PRODUCT_MODULE_NAME).MachineDocument + CFBundleExecutable $(EXECUTABLE_NAME) diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index aaabb819e..cd26c382c 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -24,6 +24,7 @@ #include "../Storage/Disk/Formats/AcornADF.hpp" #include "../Storage/Disk/Formats/D64.hpp" #include "../Storage/Disk/Formats/G64.hpp" +#include "../Storage/Disk/Formats/OricMFMDSK.hpp" #include "../Storage/Disk/Formats/SSD.hpp" // Tapes @@ -88,6 +89,7 @@ std::list StaticAnalyser::GetTargets(const char *file_name) Format("bin", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64 Format("dsd", disks, Disk::SSD, TargetPlatform::Acorn) // DSD + Format("dsk", disks, Disk::OricMFMDSK, TargetPlatform::Oric) // DSK Format("g64", disks, Disk::G64, TargetPlatform::Commodore) // G64 // PRG diff --git a/Storage/Disk/Formats/G64.cpp b/Storage/Disk/Formats/G64.cpp index df0559058..f20598813 100644 --- a/Storage/Disk/Formats/G64.cpp +++ b/Storage/Disk/Formats/G64.cpp @@ -18,11 +18,7 @@ G64::G64(const char *file_name) : Storage::FileHolder(file_name) { // read and check the file signature - char signature[8]; - if(fread(signature, 1, 8, file_) != 8) - throw ErrorNotG64; - - if(memcmp(signature, "GCR-1541", 8)) + if(!check_signature("GCR-1541", 8)) throw ErrorNotG64; // check the version number @@ -34,8 +30,7 @@ G64::G64(const char *file_name) : // get the number of tracks and track size number_of_tracks_ = (uint8_t)fgetc(file_); - maximum_track_size_ = (uint16_t)fgetc(file_); - maximum_track_size_ |= (uint16_t)fgetc(file_) << 8; + maximum_track_size_ = fgetc16le(); } unsigned int G64::get_head_position_count() @@ -59,10 +54,7 @@ std::shared_ptr G64::get_track_at_position(unsigned int head, unsigned in // read the track offset uint32_t track_offset; - track_offset = (uint32_t)fgetc(file_); - track_offset |= (uint32_t)fgetc(file_) << 8; - track_offset |= (uint32_t)fgetc(file_) << 16; - track_offset |= (uint32_t)fgetc(file_) << 24; + track_offset = fgetc32le(); // if the track offset is zero, this track doesn't exist, so... if(!track_offset) return resulting_track; @@ -72,8 +64,7 @@ std::shared_ptr G64::get_track_at_position(unsigned int head, unsigned in // get the real track length uint16_t track_length; - track_length = (uint16_t)fgetc(file_); - track_length |= (uint16_t)fgetc(file_) << 8; + track_length = fgetc16le(); // grab the byte contents of this track std::vector track_contents(track_length); @@ -84,10 +75,7 @@ std::shared_ptr G64::get_track_at_position(unsigned int head, unsigned in // read the speed zone offsrt uint32_t speed_zone_offset; - speed_zone_offset = (uint32_t)fgetc(file_); - speed_zone_offset |= (uint32_t)fgetc(file_) << 8; - speed_zone_offset |= (uint32_t)fgetc(file_) << 16; - speed_zone_offset |= (uint32_t)fgetc(file_) << 24; + speed_zone_offset = fgetc32le(); // if the speed zone is not constant, create a track based on the whole table; otherwise create one that's constant if(speed_zone_offset > 3) diff --git a/Storage/Disk/Formats/OricMFMDSK.cpp b/Storage/Disk/Formats/OricMFMDSK.cpp new file mode 100644 index 000000000..0a849fa6d --- /dev/null +++ b/Storage/Disk/Formats/OricMFMDSK.cpp @@ -0,0 +1,59 @@ +// +// OricMFMDSK.cpp +// Clock Signal +// +// Created by Thomas Harte on 21/11/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "OricMFMDSK.hpp" +#include "../PCMTrack.hpp" + +using namespace Storage::Disk; + +OricMFMDSK::OricMFMDSK(const char *file_name) : + Storage::FileHolder(file_name) +{ + if(!check_signature("MFM_DISK", 8)) + throw ErrorNotOricMFMDSK; + + head_count_ = fgetc32le(); + track_count_ = fgetc32le(); + geometry_type_ = fgetc32le(); + + if(geometry_type_ > 1) + throw ErrorNotOricMFMDSK; +} + +unsigned int OricMFMDSK::get_head_position_count() +{ + return track_count_; +} + +unsigned int OricMFMDSK::get_head_count() +{ + return head_count_; +} + +std::shared_ptr OricMFMDSK::get_track_at_position(unsigned int head, unsigned int position) +{ + long offset = 0; + switch(geometry_type_) + { + case 0: + offset = (head * track_count_) + position; + break; + case 1: + offset = (position * track_count_ * head_count_) + head; + break; + } + fseek(file_, (offset * 6400) + 256, SEEK_SET); + + PCMSegment segment; + segment.number_of_bits = 6250*8; + segment.data.resize(6250); + fread(segment.data.data(), 1, 6250, file_); + + std::shared_ptr track(new PCMTrack(segment)); + return track; +} diff --git a/Storage/Disk/Formats/OricMFMDSK.hpp b/Storage/Disk/Formats/OricMFMDSK.hpp new file mode 100644 index 000000000..4cc8c9860 --- /dev/null +++ b/Storage/Disk/Formats/OricMFMDSK.hpp @@ -0,0 +1,49 @@ +// +// OricMFMDSK.hpp +// Clock Signal +// +// Created by Thomas Harte on 21/11/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef OricMFMDSK_hpp +#define OricMFMDSK_hpp + +#include "../Disk.hpp" +#include "../../FileHolder.hpp" + +namespace Storage { +namespace Disk { + +/*! + Provies a @c Disk containing an Oric MFM-stype disk image — an MFM bit stream. +*/ +class OricMFMDSK: public Disk, public Storage::FileHolder { + public: + /*! + Construct an @c AcornADF containing content from the file with name @c file_name. + + @throws ErrorCantOpen if this file can't be opened. + @throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image. + */ + OricMFMDSK(const char *file_name); + + enum { + ErrorNotOricMFMDSK, + }; + + // implemented to satisfy @c Disk + unsigned int get_head_position_count(); + unsigned int get_head_count(); + std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); + + private: + uint32_t head_count_; + uint32_t track_count_; + uint32_t geometry_type_; +}; + +} +} + +#endif /* OricMFMDSK_hpp */ diff --git a/Storage/FileHolder.cpp b/Storage/FileHolder.cpp index 8b08eaaba..b5bd16bb3 100644 --- a/Storage/FileHolder.cpp +++ b/Storage/FileHolder.cpp @@ -8,6 +8,8 @@ #include "FileHolder.hpp" +#include + using namespace Storage; FileHolder::~FileHolder() @@ -21,3 +23,59 @@ FileHolder::FileHolder(const char *file_name) : file_(nullptr) file_ = fopen(file_name, "rb"); if(!file_) throw ErrorCantOpen; } + +bool FileHolder::check_signature(const char *signature, size_t length) +{ + if(!length) length = strlen(signature)+1; + + // read and check the file signature + char stored_signature[12]; + if(fread(stored_signature, 1, length, file_) != length) return false; + if(memcmp(stored_signature, signature, length)) return false; + return true; +} + +uint32_t FileHolder::fgetc32le() +{ + uint32_t result = (uint32_t)fgetc(file_); + result |= (uint32_t)(fgetc(file_) << 8); + result |= (uint32_t)(fgetc(file_) << 16); + result |= (uint32_t)(fgetc(file_) << 24); + + return result; +} + +uint32_t FileHolder::fgetc24le() +{ + uint32_t result = (uint32_t)fgetc(file_); + result |= (uint32_t)(fgetc(file_) << 8); + result |= (uint32_t)(fgetc(file_) << 16); + + return result; +} + +uint16_t FileHolder::fgetc16le() +{ + uint16_t result = (uint16_t)fgetc(file_); + result |= (uint16_t)(fgetc(file_) << 8); + + return result; +} + +uint32_t FileHolder::fgetc32be() +{ + uint32_t result = (uint32_t)(fgetc(file_) << 24); + result |= (uint32_t)(fgetc(file_) << 16); + result |= (uint32_t)(fgetc(file_) << 8); + result |= (uint32_t)fgetc(file_); + + return result; +} + +uint16_t FileHolder::fgetc16be() +{ + uint16_t result = (uint16_t)(fgetc(file_) << 8); + result |= (uint16_t)fgetc(file_); + + return result; +} diff --git a/Storage/FileHolder.hpp b/Storage/FileHolder.hpp index b31d24790..ef853a844 100644 --- a/Storage/FileHolder.hpp +++ b/Storage/FileHolder.hpp @@ -11,6 +11,7 @@ #include #include +#include namespace Storage { @@ -25,6 +26,45 @@ class FileHolder { protected: FileHolder(const char *file_name); + /*! + Reads @c length bytes from the file and compares them to the first + @c length bytes of @c signature. If @c length is 0, it is computed + as the length of @c signature up to and including the terminating null. + + @returns @c true if the bytes read match the signature; @c false otherwise. + */ + bool check_signature(const char *signature, size_t length); + + /*! + Performs @c fgetc four times on @c file_, casting each result to a @c uint32_t + and returning the four assembled in little endian order. + */ + uint32_t fgetc32le(); + + /*! + Performs @c fgetc three times on @c file_, casting each result to a @c uint32_t + and returning the three assembled in little endian order. + */ + uint32_t fgetc24le(); + + /*! + Performs @c fgetc two times on @c file_, casting each result to a @c uint32_t + and returning the two assembled in little endian order. + */ + uint16_t fgetc16le(); + + /*! + Performs @c fgetc four times on @c file_, casting each result to a @c uint32_t + and returning the four assembled in big endian order. + */ + uint32_t fgetc32be(); + + /*! + Performs @c fgetc two times on @c file_, casting each result to a @c uint32_t + and returning the two assembled in big endian order. + */ + uint16_t fgetc16be(); + FILE *file_; struct stat file_stats_; }; diff --git a/Storage/Tape/Formats/CommodoreTAP.cpp b/Storage/Tape/Formats/CommodoreTAP.cpp index 783c2487b..f74f0bb88 100644 --- a/Storage/Tape/Formats/CommodoreTAP.cpp +++ b/Storage/Tape/Formats/CommodoreTAP.cpp @@ -16,12 +16,7 @@ CommodoreTAP::CommodoreTAP(const char *file_name) : is_at_end_(false), Storage::FileHolder(file_name) { - // read and check the file signature - char signature[12]; - if(fread(signature, 1, 12, file_) != 12) - throw ErrorNotCommodoreTAP; - - if(memcmp(signature, "C64-TAPE-RAW", 12)) + if(!check_signature("C64-TAPE-RAW", 12)) throw ErrorNotCommodoreTAP; // check the file version @@ -37,10 +32,7 @@ CommodoreTAP::CommodoreTAP(const char *file_name) : fseek(file_, 3, SEEK_CUR); // read file size - file_size_ = (uint32_t)fgetc(file_); - file_size_ |= (uint32_t)(fgetc(file_) << 8); - file_size_ |= (uint32_t)(fgetc(file_) << 16); - file_size_ |= (uint32_t)(fgetc(file_) << 24); + file_size_ = fgetc32le(); // set up for pulse output at the PAL clock rate, with each high and // low being half of whatever length values will be read; pretend that @@ -79,9 +71,7 @@ Storage::Tape::Tape::Pulse CommodoreTAP::virtual_get_next_pulse() } else { - next_length = (uint32_t)fgetc(file_); - next_length |= (uint32_t)(fgetc(file_) << 8); - next_length |= (uint32_t)(fgetc(file_) << 16); + next_length = fgetc24le(); } if(feof(file_)) diff --git a/Storage/Tape/Formats/OricTAP.cpp b/Storage/Tape/Formats/OricTAP.cpp index b540efcfc..9da3714ec 100644 --- a/Storage/Tape/Formats/OricTAP.cpp +++ b/Storage/Tape/Formats/OricTAP.cpp @@ -15,12 +15,8 @@ using namespace Storage::Tape; OricTAP::OricTAP(const char *file_name) : Storage::FileHolder(file_name) { - // read and check the file signature - uint8_t signature[4]; - if(fread(signature, 1, 4, file_) != 4) - throw ErrorNotOricTAP; - - if(signature[0] != 0x16 || signature[1] != 0x16 || signature[2] != 0x16 || signature[3] != 0x24) + // check the file signature + if(!check_signature("\x16\x16\x16\x24", 4)) throw ErrorNotOricTAP; // then rewind and start again diff --git a/Storage/Tape/Formats/TapePRG.cpp b/Storage/Tape/Formats/TapePRG.cpp index 1e4cd278a..1407396b1 100644 --- a/Storage/Tape/Formats/TapePRG.cpp +++ b/Storage/Tape/Formats/TapePRG.cpp @@ -60,8 +60,7 @@ PRG::PRG(const char *file_name) : if(file_stats_.st_size >= 65538 || file_stats_.st_size < 3) throw ErrorBadFormat; - load_address_ = (uint16_t)fgetc(file_); - load_address_ |= (uint16_t)fgetc(file_) << 8; + load_address_ = fgetc16le(); length_ = (uint16_t)(file_stats_.st_size - 2); if (load_address_ + length_ >= 65536)