1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-23 03:32:32 +00:00

Merge pull request #79 from TomHarte/OricDSK

Creates a base class for file format implementations which seeks to collect boilerplate stuff
This commit is contained in:
Thomas Harte 2016-11-21 20:51:48 +08:00 committed by GitHub
commit fc1afe9351
23 changed files with 608 additions and 430 deletions

View File

@ -53,6 +53,8 @@
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */; }; 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */; };
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B59199A1DAC6C46005BB85C /* OricTAP.cpp */; }; 4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B59199A1DAC6C46005BB85C /* OricTAP.cpp */; };
4B5A12571DD55862007A2231 /* Disassembler6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5A12551DD55862007A2231 /* Disassembler6502.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 */; }; 4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */; };
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; }; 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; };
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; }; 4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; };
@ -499,6 +501,10 @@
4B59199B1DAC6C46005BB85C /* OricTAP.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OricTAP.hpp; sourceTree = "<group>"; }; 4B59199B1DAC6C46005BB85C /* OricTAP.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OricTAP.hpp; sourceTree = "<group>"; };
4B5A12551DD55862007A2231 /* Disassembler6502.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Disassembler6502.cpp; path = ../../StaticAnalyser/Disassembler/Disassembler6502.cpp; sourceTree = "<group>"; }; 4B5A12551DD55862007A2231 /* Disassembler6502.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Disassembler6502.cpp; path = ../../StaticAnalyser/Disassembler/Disassembler6502.cpp; sourceTree = "<group>"; };
4B5A12561DD55862007A2231 /* Disassembler6502.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Disassembler6502.hpp; path = ../../StaticAnalyser/Disassembler/Disassembler6502.hpp; sourceTree = "<group>"; }; 4B5A12561DD55862007A2231 /* Disassembler6502.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Disassembler6502.hpp; path = ../../StaticAnalyser/Disassembler/Disassembler6502.hpp; sourceTree = "<group>"; };
4B5FADB81DE3151600AEC565 /* FileHolder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileHolder.cpp; sourceTree = "<group>"; };
4B5FADB91DE3151600AEC565 /* FileHolder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FileHolder.hpp; sourceTree = "<group>"; };
4B5FADBB1DE31D1500AEC565 /* OricMFMDSK.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OricMFMDSK.cpp; sourceTree = "<group>"; };
4B5FADBC1DE31D1500AEC565 /* OricMFMDSK.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OricMFMDSK.hpp; sourceTree = "<group>"; };
4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = "<group>"; }; 4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = "<group>"; };
4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = "<group>"; }; 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = "<group>"; };
4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = "<group>"; }; 4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = "<group>"; };
@ -1190,6 +1196,8 @@
4B8805F81DCFF6CD003085B1 /* Data */, 4B8805F81DCFF6CD003085B1 /* Data */,
4BAB62AA1D3272D200DF5BA0 /* Disk */, 4BAB62AA1D3272D200DF5BA0 /* Disk */,
4B69FB3A1C4D908A00B5F0AA /* Tape */, 4B69FB3A1C4D908A00B5F0AA /* Tape */,
4B5FADB81DE3151600AEC565 /* FileHolder.cpp */,
4B5FADB91DE3151600AEC565 /* FileHolder.hpp */,
); );
name = Storage; name = Storage;
path = ../../Storage; path = ../../Storage;
@ -1275,14 +1283,16 @@
4BAB62B21D327F7E00DF5BA0 /* Formats */ = { 4BAB62B21D327F7E00DF5BA0 /* Formats */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4BAB62B31D327F7E00DF5BA0 /* G64.cpp */,
4BAB62B41D327F7E00DF5BA0 /* G64.hpp */,
4B4C836E1D4F623200CD541F /* D64.cpp */,
4B4C836F1D4F623200CD541F /* D64.hpp */,
4BF829611D8F536B001BAE39 /* SSD.cpp */,
4BF829621D8F536B001BAE39 /* SSD.hpp */,
4BD69F921D98760000243FE1 /* AcornADF.cpp */, 4BD69F921D98760000243FE1 /* AcornADF.cpp */,
4BD69F931D98760000243FE1 /* AcornADF.hpp */, 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; path = Formats;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2328,11 +2338,13 @@
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */, 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */,
4BF8295D1D8F048B001BAE39 /* MFM.cpp in Sources */, 4BF8295D1D8F048B001BAE39 /* MFM.cpp in Sources */,
4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */, 4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */,
4B5FADBD1DE31D1500AEC565 /* OricMFMDSK.cpp in Sources */,
4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */, 4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */,
4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */, 4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */,
4BBF99141C8FBA6F0075DAFB /* TextureBuilder.cpp in Sources */, 4BBF99141C8FBA6F0075DAFB /* TextureBuilder.cpp in Sources */,
4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */, 4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */,
4BCF1FA81DADC5250039D2E7 /* CSOric.mm in Sources */, 4BCF1FA81DADC5250039D2E7 /* CSOric.mm in Sources */,
4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */,
4B6C73BD1D387AE500AFCFCA /* DiskController.cpp in Sources */, 4B6C73BD1D387AE500AFCFCA /* DiskController.cpp in Sources */,
4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */, 4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */,
4B4DC8281D2C2470003C5BF8 /* C1540.cpp in Sources */, 4B4DC8281D2C2470003C5BF8 /* C1540.cpp in Sources */,

View File

@ -49,7 +49,7 @@
<string>rom</string> <string>rom</string>
</array> </array>
<key>CFBundleTypeName</key> <key>CFBundleTypeName</key>
<string>Electron/BBC ROM Image</string> <string>ROM Image</string>
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Viewer</string> <string>Viewer</string>
<key>LSItemContentTypes</key> <key>LSItemContentTypes</key>
@ -146,6 +146,18 @@
<key>NSDocumentClass</key> <key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string> <string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
</dict> </dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>dsk</string>
</array>
<key>CFBundleTypeName</key>
<string>Disk Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
</dict>
</array> </array>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>

View File

@ -24,6 +24,7 @@
#include "../Storage/Disk/Formats/AcornADF.hpp" #include "../Storage/Disk/Formats/AcornADF.hpp"
#include "../Storage/Disk/Formats/D64.hpp" #include "../Storage/Disk/Formats/D64.hpp"
#include "../Storage/Disk/Formats/G64.hpp" #include "../Storage/Disk/Formats/G64.hpp"
#include "../Storage/Disk/Formats/OricMFMDSK.hpp"
#include "../Storage/Disk/Formats/SSD.hpp" #include "../Storage/Disk/Formats/SSD.hpp"
// Tapes // Tapes
@ -88,6 +89,7 @@ std::list<Target> StaticAnalyser::GetTargets(const char *file_name)
Format("bin", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN Format("bin", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN
Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64 Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64
Format("dsd", disks, Disk::SSD, TargetPlatform::Acorn) // DSD Format("dsd", disks, Disk::SSD, TargetPlatform::Acorn) // DSD
Format("dsk", disks, Disk::OricMFMDSK, TargetPlatform::Oric) // DSK
Format("g64", disks, Disk::G64, TargetPlatform::Commodore) // G64 Format("g64", disks, Disk::G64, TargetPlatform::Commodore) // G64
// PRG // PRG

View File

@ -18,35 +18,25 @@ namespace {
using namespace Storage::Disk; 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 // very loose validation: the file needs to be a multiple of 256 bytes
// and not ungainly large // and not ungainly large
if(file_stats.st_size % bytes_per_sector) throw ErrorNotAcornADF; if(file_stats_.st_size % bytes_per_sector) throw ErrorNotAcornADF;
if(file_stats.st_size < 7 * 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;
// check that the initial directory's 'Hugo's are present // check that the initial directory's 'Hugo's are present
fseek(_file, 513, SEEK_SET); fseek(file_, 513, SEEK_SET);
uint8_t bytes[4]; 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; if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw ErrorNotAcornADF;
fseek(_file, 0x6fb, SEEK_SET); fseek(file_, 0x6fb, SEEK_SET);
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; 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() unsigned int AcornADF::get_head_position_count()
{ {
return 80; return 80;
@ -63,7 +53,7 @@ std::shared_ptr<Track> AcornADF::get_track_at_position(unsigned int head, unsign
if(head >= 2) return track; if(head >= 2) return track;
long file_offset = (position * 1 + head) * bytes_per_sector * sectors_per_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<Storage::Encodings::MFM::Sector> sectors; std::vector<Storage::Encodings::MFM::Sector> sectors;
for(int sector = 0; sector < sectors_per_track; sector++) for(int sector = 0; sector < sectors_per_track; sector++)
@ -74,8 +64,8 @@ std::shared_ptr<Track> AcornADF::get_track_at_position(unsigned int head, unsign
new_sector.sector = (uint8_t)sector; new_sector.sector = (uint8_t)sector;
new_sector.data.resize(bytes_per_sector); new_sector.data.resize(bytes_per_sector);
fread(&new_sector.data[0], 1, bytes_per_sector, _file); fread(&new_sector.data[0], 1, bytes_per_sector, file_);
if(feof(_file)) if(feof(file_))
break; break;
sectors.push_back(std::move(new_sector)); sectors.push_back(std::move(new_sector));

View File

@ -10,6 +10,7 @@
#define AcornADF_hpp #define AcornADF_hpp
#include "../Disk.hpp" #include "../Disk.hpp"
#include "../../FileHolder.hpp"
namespace Storage { namespace Storage {
namespace Disk { 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. 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: public:
/*! /*!
Construct an @c AcornADF containing content from the file with name @c file_name. 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. @throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image.
*/ */
AcornADF(const char *file_name); AcornADF(const char *file_name);
~AcornADF();
enum { enum {
ErrorCantOpen,
ErrorNotAcornADF, ErrorNotAcornADF,
}; };
@ -37,9 +36,6 @@ class AcornADF: public Disk {
unsigned int get_head_position_count(); unsigned int get_head_position_count();
unsigned int get_head_count(); unsigned int get_head_count();
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position); std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
FILE *_file;
}; };
} }

View File

@ -15,42 +15,30 @@
using namespace Storage::Disk; 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 // 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. // 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; throw ErrorNotD64;
_number_of_tracks = (file_stats.st_size == 174848) ? 35 : 40; number_of_tracks_ = (file_stats_.st_size == 174848) ? 35 : 40;
_file = fopen(file_name, "rb");
if(!_file)
throw ErrorNotD64;
// then, ostensibly, this is a valid file. Hmmm. Pick a disk ID as a function of the file_name, // 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 // being the most stable thing available
_disk_id = 0; disk_id_ = 0;
while(*file_name) while(*file_name)
{ {
_disk_id ^= file_name[0]; disk_id_ ^= file_name[0];
_disk_id = (uint16_t)((_disk_id << 2) ^ (_disk_id >> 13)); disk_id_ = (uint16_t)((disk_id_ << 2) ^ (disk_id_ >> 13));
file_name++; file_name++;
} }
} }
D64::~D64()
{
if(_file) fclose(_file);
}
unsigned int D64::get_head_position_count() unsigned int D64::get_head_position_count()
{ {
return _number_of_tracks*2; return number_of_tracks_*2;
} }
std::shared_ptr<Track> D64::get_track_at_position(unsigned int head, unsigned int position) std::shared_ptr<Track> D64::get_track_at_position(unsigned int head, unsigned int position)
@ -75,7 +63,7 @@ std::shared_ptr<Track> D64::get_track_at_position(unsigned int head, unsigned in
} }
// seek to start of data // 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 // build up a PCM sampling of the GCR version of this track
@ -114,14 +102,14 @@ std::shared_ptr<Track> D64::get_track_at_position(unsigned int head, unsigned in
uint8_t sector_number = (uint8_t)(sector); // sectors count from 0 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 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] = { uint8_t header_start[4] = {
0x08, checksum, sector_number, track_number 0x08, checksum, sector_number, track_number
}; };
Encodings::CommodoreGCR::encode_block(&sector_data[3], header_start); Encodings::CommodoreGCR::encode_block(&sector_data[3], header_start);
uint8_t header_end[4] = { 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(&sector_data[8], header_end); Encodings::CommodoreGCR::encode_block(&sector_data[8], header_end);
@ -134,7 +122,7 @@ std::shared_ptr<Track> D64::get_track_at_position(unsigned int head, unsigned in
// get the actual contents // get the actual contents
uint8_t source_data[256]; uint8_t source_data[256];
fread(source_data, 1, 256, _file); fread(source_data, 1, 256, file_);
// compute the latest checksum // compute the latest checksum
checksum = 0; checksum = 0;

View File

@ -10,6 +10,7 @@
#define D64_hpp #define D64_hpp
#include "../Disk.hpp" #include "../Disk.hpp"
#include "../../FileHolder.hpp"
namespace Storage { namespace Storage {
namespace Disk { 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. 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: public:
/*! /*!
Construct a @c D64 containing content from the file with name @c file_name. 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. @throws ErrorNotD64 if the file doesn't appear to contain a .D64 format image.
*/ */
D64(const char *file_name); D64(const char *file_name);
~D64();
enum { enum {
ErrorCantOpen,
ErrorNotD64, ErrorNotD64,
}; };
@ -38,9 +37,8 @@ class D64: public Disk {
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position); std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private: private:
FILE *_file; unsigned int number_of_tracks_;
unsigned int _number_of_tracks; uint16_t disk_id_;
uint16_t _disk_id;
}; };
} }

View File

@ -14,44 +14,30 @@
using namespace Storage::Disk; 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 // read and check the file signature
char signature[8]; if(!check_signature("GCR-1541", 8))
if(fread(signature, 1, 8, _file) != 8)
throw ErrorNotG64;
if(memcmp(signature, "GCR-1541", 8))
throw ErrorNotG64; throw ErrorNotG64;
// check the version number // check the version number
int version = fgetc(_file); int version = fgetc(file_);
if(version != 0) if(version != 0)
{ {
throw ErrorUnknownVersion; throw ErrorUnknownVersion;
} }
// get the number of tracks and track size // get the number of tracks and track size
_number_of_tracks = (uint8_t)fgetc(_file); number_of_tracks_ = (uint8_t)fgetc(file_);
_maximum_track_size = (uint16_t)fgetc(_file); maximum_track_size_ = fgetc16le();
_maximum_track_size |= (uint16_t)fgetc(_file) << 8;
}
G64::~G64()
{
if(_file) fclose(_file);
} }
unsigned int G64::get_head_position_count() unsigned int G64::get_head_position_count()
{ {
// give at least 84 tracks, to yield the normal geometry but, // give at least 84 tracks, to yield the normal geometry but,
// if there are more, shove them in // 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<Track> G64::get_track_at_position(unsigned int head, unsigned int position) std::shared_ptr<Track> G64::get_track_at_position(unsigned int head, unsigned int position)
@ -60,55 +46,48 @@ std::shared_ptr<Track> G64::get_track_at_position(unsigned int head, unsigned in
// if there's definitely no track here, return the empty track // if there's definitely no track here, return the empty track
// (TODO: should be supplying one with an index hole?) // (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; if(head >= 1) return resulting_track;
// seek to this track's entry in the track table // 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 // read the track offset
uint32_t track_offset; uint32_t track_offset;
track_offset = (uint32_t)fgetc(_file); track_offset = fgetc32le();
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 the track offset is zero, this track doesn't exist, so...
if(!track_offset) return resulting_track; if(!track_offset) return resulting_track;
// seek to the track start // seek to the track start
fseek(_file, (int)track_offset, SEEK_SET); fseek(file_, (int)track_offset, SEEK_SET);
// get the real track length // get the real track length
uint16_t track_length; uint16_t track_length;
track_length = (uint16_t)fgetc(_file); track_length = fgetc16le();
track_length |= (uint16_t)fgetc(_file) << 8;
// grab the byte contents of this track // grab the byte contents of this track
std::vector<uint8_t> track_contents(track_length); std::vector<uint8_t> 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 // 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 // read the speed zone offsrt
uint32_t speed_zone_offset; uint32_t speed_zone_offset;
speed_zone_offset = (uint32_t)fgetc(_file); speed_zone_offset = fgetc32le();
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 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) if(speed_zone_offset > 3)
{ {
// seek to start of speed zone // 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; uint16_t speed_zone_length = (track_length + 3) >> 2;
// read the speed zone bytes // read the speed zone bytes
uint8_t speed_zone_contents[speed_zone_length]; 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 // divide track into appropriately timed PCMSegments
std::vector<PCMSegment> segments; std::vector<PCMSegment> segments;

View File

@ -10,6 +10,7 @@
#define G64_hpp #define G64_hpp
#include "../Disk.hpp" #include "../Disk.hpp"
#include "../../FileHolder.hpp"
namespace Storage { namespace Storage {
namespace Disk { namespace Disk {
@ -17,7 +18,7 @@ namespace Disk {
/*! /*!
Provies a @c Disk containing a G64 disk image a raw but perfectly-clocked GCR stream. 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: public:
/*! /*!
Construct a @c G64 containing content from the file with name @c file_name. 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. @throws ErrorUnknownVersion if this file appears to be a .G64 but has an unrecognised version number.
*/ */
G64(const char *file_name); G64(const char *file_name);
~G64();
enum { enum {
ErrorCantOpen, ErrorCantOpen,
@ -40,10 +40,8 @@ class G64: public Disk {
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position); std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private: private:
FILE *_file; uint8_t number_of_tracks_;
uint16_t maximum_track_size_;
uint8_t _number_of_tracks;
uint16_t _maximum_track_size;
}; };
} }

View File

@ -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<Track> 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<PCMTrack> track(new PCMTrack(segment));
return track;
}

View File

@ -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<Track> 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 */

View File

@ -13,51 +13,40 @@
using namespace Storage::Disk; 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 // very loose validation: the file needs to be a multiple of 256 bytes
// and not ungainly large // and not ungainly large
if(file_stats.st_size & 255) throw ErrorNotSSD; if(file_stats_.st_size & 255) throw ErrorNotSSD;
if(file_stats.st_size < 512) throw ErrorNotSSD; if(file_stats_.st_size < 512) throw ErrorNotSSD;
if(file_stats.st_size > 800*256) throw ErrorNotSSD; if(file_stats_.st_size > 800*256) throw ErrorNotSSD;
_file = fopen(file_name, "rb");
if(!_file) throw ErrorCantOpen;
// this has two heads if the suffix is .dsd, one if it's .ssd // 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; head_count_ = (tolower(file_name[strlen(file_name) - 3]) == 'd') ? 2 : 1;
_track_count = (unsigned int)(file_stats.st_size / (256 * 10)); track_count_ = (unsigned int)(file_stats_.st_size / (256 * 10));
if(_track_count < 40) _track_count = 40; if(track_count_ < 40) track_count_ = 40;
else if(_track_count < 80) _track_count = 80; else if(track_count_ < 80) track_count_ = 80;
}
SSD::~SSD()
{
if(_file) fclose(_file);
} }
unsigned int SSD::get_head_position_count() unsigned int SSD::get_head_position_count()
{ {
return _track_count; return track_count_;
} }
unsigned int SSD::get_head_count() unsigned int SSD::get_head_count()
{ {
return _head_count; return head_count_;
} }
std::shared_ptr<Track> SSD::get_track_at_position(unsigned int head, unsigned int position) std::shared_ptr<Track> SSD::get_track_at_position(unsigned int head, unsigned int position)
{ {
std::shared_ptr<Track> track; std::shared_ptr<Track> track;
if(head >= _head_count) return track; if(head >= head_count_) return track;
long file_offset = (position * _head_count + head) * 256 * 10; long file_offset = (position * head_count_ + head) * 256 * 10;
fseek(_file, file_offset, SEEK_SET); fseek(file_, file_offset, SEEK_SET);
std::vector<Storage::Encodings::MFM::Sector> sectors; std::vector<Storage::Encodings::MFM::Sector> sectors;
for(int sector = 0; sector < 10; sector++) for(int sector = 0; sector < 10; sector++)
@ -68,8 +57,8 @@ std::shared_ptr<Track> SSD::get_track_at_position(unsigned int head, unsigned in
new_sector.sector = (uint8_t)sector; new_sector.sector = (uint8_t)sector;
new_sector.data.resize(256); new_sector.data.resize(256);
fread(&new_sector.data[0], 1, 256, _file); fread(&new_sector.data[0], 1, 256, file_);
if(feof(_file)) if(feof(file_))
break; break;
sectors.push_back(std::move(new_sector)); sectors.push_back(std::move(new_sector));

View File

@ -10,6 +10,7 @@
#define SSD_hpp #define SSD_hpp
#include "../Disk.hpp" #include "../Disk.hpp"
#include "../../FileHolder.hpp"
namespace Storage { namespace Storage {
namespace Disk { 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. 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: public:
/*! /*!
Construct an @c SSD containing content from the file with name @c file_name. 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. @throws ErrorNotSSD if the file doesn't appear to contain a .SSD format image.
*/ */
SSD(const char *file_name); SSD(const char *file_name);
~SSD();
enum { enum {
ErrorCantOpen,
ErrorNotSSD, ErrorNotSSD,
}; };
@ -39,9 +38,8 @@ class SSD: public Disk {
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position); std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private: private:
FILE *_file; unsigned int head_count_;
unsigned int _head_count; unsigned int track_count_;
unsigned int _track_count;
}; };
} }

81
Storage/FileHolder.cpp Normal file
View File

@ -0,0 +1,81 @@
//
// FileHolder.cpp
// Clock Signal
//
// Created by Thomas Harte on 21/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "FileHolder.hpp"
#include <cstring>
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;
}
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;
}

74
Storage/FileHolder.hpp Normal file
View File

@ -0,0 +1,74 @@
//
// 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 <sys/stat.h>
#include <cstdio>
#include <cstdint>
namespace Storage {
class FileHolder {
public:
enum {
ErrorCantOpen = -1
};
virtual ~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_;
};
}
#endif /* FileHolder_hpp */

View File

@ -12,100 +12,82 @@
using namespace Storage::Tape; 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(!check_signature("C64-TAPE-RAW", 12))
if(!_file)
throw ErrorNotCommodoreTAP;
// 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))
throw ErrorNotCommodoreTAP; throw ErrorNotCommodoreTAP;
// check the file version // check the file version
int version = fgetc(_file); int version = fgetc(file_);
switch(version) switch(version)
{ {
case 0: _updated_layout = false; break; case 0: updated_layout_ = false; break;
case 1: _updated_layout = true; break; case 1: updated_layout_ = true; break;
default: throw ErrorNotCommodoreTAP; default: throw ErrorNotCommodoreTAP;
} }
// skip reserved bytes // skip reserved bytes
fseek(_file, 3, SEEK_CUR); fseek(file_, 3, SEEK_CUR);
// read file size // read file size
_file_size = (uint32_t)fgetc(_file); file_size_ = fgetc32le();
_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 // 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 // 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 // a high pulse has just been distributed to imply that the next thing
// that needs to happen is a length check // that needs to happen is a length check
_current_pulse.length.clock_rate = 985248 * 2; current_pulse_.length.clock_rate = 985248 * 2;
_current_pulse.type = Pulse::High; current_pulse_.type = Pulse::High;
}
CommodoreTAP::~CommodoreTAP()
{
fclose(_file);
} }
void CommodoreTAP::virtual_reset() void CommodoreTAP::virtual_reset()
{ {
fseek(_file, 0x14, SEEK_SET); fseek(file_, 0x14, SEEK_SET);
_current_pulse.type = Pulse::High; current_pulse_.type = Pulse::High;
_is_at_end = false; is_at_end_ = false;
} }
bool CommodoreTAP::is_at_end() bool CommodoreTAP::is_at_end()
{ {
return _is_at_end; return is_at_end_;
} }
Storage::Tape::Tape::Pulse CommodoreTAP::virtual_get_next_pulse() 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; uint32_t next_length;
uint8_t next_byte = (uint8_t)fgetc(_file); uint8_t next_byte = (uint8_t)fgetc(file_);
if(!_updated_layout || next_byte > 0) if(!updated_layout_ || next_byte > 0)
{ {
next_length = (uint32_t)next_byte << 3; next_length = (uint32_t)next_byte << 3;
} }
else else
{ {
next_length = (uint32_t)fgetc(_file); next_length = fgetc24le();
next_length |= (uint32_t)(fgetc(_file) << 8);
next_length |= (uint32_t)(fgetc(_file) << 16);
} }
if(feof(_file)) if(feof(file_))
{ {
_is_at_end = true; is_at_end_ = true;
_current_pulse.length.length = _current_pulse.length.clock_rate; current_pulse_.length.length = current_pulse_.length.clock_rate;
_current_pulse.type = Pulse::Zero; current_pulse_.type = Pulse::Zero;
} }
else else
{ {
_current_pulse.length.length = next_length; current_pulse_.length.length = next_length;
_current_pulse.type = Pulse::Low; current_pulse_.type = Pulse::Low;
} }
} }
else else
_current_pulse.type = Pulse::High; current_pulse_.type = Pulse::High;
return _current_pulse; return current_pulse_;
} }

View File

@ -10,7 +10,8 @@
#define CommodoreTAP_hpp #define CommodoreTAP_hpp
#include "../Tape.hpp" #include "../Tape.hpp"
#include <stdint.h> #include "../../FileHolder.hpp"
#include <cstdint>
namespace Storage { namespace Storage {
namespace Tape { 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. 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: public:
/*! /*!
Constructs a @c CommodoreTAP containing content from the file with name @c file_name. 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. @throws ErrorNotCommodoreTAP if this file could not be opened and recognised as a valid Commodore-format TAP.
*/ */
CommodoreTAP(const char *file_name); CommodoreTAP(const char *file_name);
~CommodoreTAP();
enum { enum {
ErrorNotCommodoreTAP ErrorNotCommodoreTAP
@ -39,12 +39,11 @@ class CommodoreTAP: public Tape {
void virtual_reset(); void virtual_reset();
Pulse virtual_get_next_pulse(); Pulse virtual_get_next_pulse();
FILE *_file; bool updated_layout_;
bool _updated_layout; uint32_t file_size_;
uint32_t _file_size;
Pulse _current_pulse; Pulse current_pulse_;
bool _is_at_end; bool is_at_end_;
}; };
} }

View File

@ -12,68 +12,51 @@
using namespace Storage::Tape; 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; // check the file signature
stat(file_name, &file_stats); if(!check_signature("\x16\x16\x16\x24", 4))
_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)
throw ErrorNotOricTAP;
if(signature[0] != 0x16 || signature[1] != 0x16 || signature[2] != 0x16 || signature[3] != 0x24)
throw ErrorNotOricTAP; throw ErrorNotOricTAP;
// then rewind and start again // then rewind and start again
virtual_reset(); virtual_reset();
} }
OricTAP::~OricTAP()
{
if(_file) fclose(_file);
}
void OricTAP::virtual_reset() void OricTAP::virtual_reset()
{ {
fseek(_file, 0, SEEK_SET); fseek(file_, 0, SEEK_SET);
_bit_count = 13; bit_count_ = 13;
_phase = _next_phase = LeadIn; phase_ = next_phase_ = LeadIn;
_phase_counter = 0; phase_counter_ = 0;
_pulse_counter = 0; pulse_counter_ = 0;
} }
Tape::Pulse OricTAP::virtual_get_next_pulse() Tape::Pulse OricTAP::virtual_get_next_pulse()
{ {
// Each byte byte is written as 13 bits: 0, eight bits of data, parity, three 1s. // 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_ = next_phase_;
_phase_counter = 0; phase_counter_ = 0;
} }
_bit_count = 0; bit_count_ = 0;
uint8_t next_byte = 0; uint8_t next_byte = 0;
switch(_phase) switch(phase_)
{ {
case LeadIn: case LeadIn:
next_byte = _phase_counter < 258 ? 0x16 : 0x24; next_byte = phase_counter_ < 258 ? 0x16 : 0x24;
_phase_counter++; phase_counter_++;
if(_phase_counter == 259) // 256 artificial bytes plus the three in the file = 259 if(phase_counter_ == 259) // 256 artificial bytes plus the three in the file = 259
{ {
while(1) while(1)
{ {
if(fgetc(_file) != 0x16) break; if(fgetc(file_) != 0x16) break;
} }
_next_phase = Header; next_phase_ = Header;
} }
break; break;
@ -86,44 +69,44 @@ Tape::Pulse OricTAP::virtual_get_next_pulse()
// [6, 7]: start address of data // [6, 7]: start address of data
// 8: "unused" (on the Oric 1) // 8: "unused" (on the Oric 1)
// [9...]: filename, up to NULL byte // [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_ == 4) data_end_address_ = (uint16_t)(next_byte << 8);
if(_phase_counter == 5) _data_end_address |= next_byte; if(phase_counter_ == 5) data_end_address_ |= next_byte;
if(_phase_counter == 6) _data_start_address = (uint16_t)(next_byte << 8); if(phase_counter_ == 6) data_start_address_ = (uint16_t)(next_byte << 8);
if(_phase_counter == 7) _data_start_address |= next_byte; 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; break;
case Gap: case Gap:
_phase_counter++; phase_counter_++;
if(_phase_counter == 8) if(phase_counter_ == 8)
{ {
_next_phase = Data; next_phase_ = Data;
} }
break; break;
case Data: case Data:
next_byte = (uint8_t)fgetc(_file); next_byte = (uint8_t)fgetc(file_);
_phase_counter++; phase_counter_++;
if(_phase_counter >= (_data_end_address - _data_start_address)+1) if(phase_counter_ >= (data_end_address_ - data_start_address_)+1)
{ {
if(next_byte == 0x16) 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; break;
@ -136,7 +119,7 @@ Tape::Pulse OricTAP::virtual_get_next_pulse()
parity ^= (parity >> 4); parity ^= (parity >> 4);
parity ^= (parity >> 2); parity ^= (parity >> 2);
parity ^= (parity >> 1); 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. // In slow mode, a 0 is 4 periods of 1200 Hz, a 1 is 8 periods at 2400 Hz.
@ -146,7 +129,7 @@ Tape::Pulse OricTAP::virtual_get_next_pulse()
pulse.length.clock_rate = 4800; pulse.length.clock_rate = 4800;
int next_bit; int next_bit;
switch(_phase) switch(phase_)
{ {
case End: case End:
pulse.type = Pulse::Zero; pulse.type = Pulse::Zero;
@ -154,13 +137,13 @@ Tape::Pulse OricTAP::virtual_get_next_pulse()
return pulse; return pulse;
case Gap: case Gap:
_bit_count = 13; bit_count_ = 13;
pulse.type = (_phase_counter&1) ? Pulse::Low : Pulse::High; pulse.type = (phase_counter_&1) ? Pulse::Low : Pulse::High;
pulse.length.length = 100; pulse.length.length = 100;
return pulse; return pulse;
default: default:
next_bit = _current_value & 1; next_bit = current_value_ & 1;
break; break;
} }
@ -170,20 +153,20 @@ Tape::Pulse OricTAP::virtual_get_next_pulse()
} }
else 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; pulse_counter_ ^= 1;
if(!_pulse_counter) if(!pulse_counter_)
{ {
_current_value >>= 1; current_value_ >>= 1;
_bit_count++; bit_count_++;
} }
return pulse; return pulse;
} }
bool OricTAP::is_at_end() bool OricTAP::is_at_end()
{ {
return _phase == End; return phase_ == End;
} }

View File

@ -10,7 +10,8 @@
#define OricTAP_hpp #define OricTAP_hpp
#include "../Tape.hpp" #include "../Tape.hpp"
#include <stdint.h> #include "../../FileHolder.hpp"
#include <cstdint>
namespace Storage { namespace Storage {
namespace Tape { namespace Tape {
@ -18,7 +19,7 @@ namespace Tape {
/*! /*!
Provides a @c Tape containing an Oric-format tape image, which is a byte stream capture. 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: public:
/*! /*!
Constructs an @c OricTAP containing content from the file with name @c file_name. 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. @throws ErrorNotOricTAP if this file could not be opened and recognised as a valid Oric-format TAP.
*/ */
OricTAP(const char *file_name); OricTAP(const char *file_name);
~OricTAP();
enum { enum {
ErrorNotOricTAP ErrorNotOricTAP
@ -39,19 +39,16 @@ class OricTAP: public Tape {
void virtual_reset(); void virtual_reset();
Pulse virtual_get_next_pulse(); Pulse virtual_get_next_pulse();
FILE *_file;
size_t _file_length;
// byte serialisation and output // byte serialisation and output
uint16_t _current_value; uint16_t current_value_;
int _bit_count; int bit_count_;
int _pulse_counter; int pulse_counter_;
enum Phase { enum Phase {
LeadIn, Header, Data, Gap, End LeadIn, Header, Data, Gap, End
} _phase, _next_phase; } phase_, next_phase_;
int _phase_counter; int phase_counter_;
uint16_t _data_end_address, _data_start_address; uint16_t data_end_address_, data_start_address_;
}; };
} }

View File

@ -48,32 +48,25 @@
using namespace Storage::Tape; 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, // 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. // 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; throw ErrorBadFormat;
_file = fopen(file_name, "rb"); load_address_ = fgetc16le();
if(!_file) throw ErrorBadFormat; length_ = (uint16_t)(file_stats_.st_size - 2);
_load_address = (uint16_t)fgetc(_file); if (load_address_ + length_ >= 65536)
_load_address |= (uint16_t)fgetc(_file) << 8;
_length = (uint16_t)(file_stats.st_size - 2);
if (_load_address + _length >= 65536)
throw ErrorBadFormat; throw ErrorBadFormat;
} }
PRG::~PRG()
{
if(_file) fclose(_file);
}
Storage::Tape::Tape::Pulse PRG::virtual_get_next_pulse() Storage::Tape::Tape::Pulse PRG::virtual_get_next_pulse()
{ {
// these are all microseconds per pole // these are all microseconds per pole
@ -82,19 +75,19 @@ Storage::Tape::Tape::Pulse PRG::virtual_get_next_pulse()
static const unsigned int one_length = 247; static const unsigned int one_length = 247;
static const unsigned int marker_length = 328; static const unsigned int marker_length = 328;
_bitPhase = (_bitPhase+1)&3; bit_phase_ = (bit_phase_+1)&3;
if(!_bitPhase) get_next_output_token(); if(!bit_phase_) get_next_output_token();
Tape::Pulse pulse; Tape::Pulse pulse;
pulse.length.clock_rate = 1000000; pulse.length.clock_rate = 1000000;
pulse.type = (_bitPhase&1) ? Tape::Pulse::High : Tape::Pulse::Low; pulse.type = (bit_phase_&1) ? Tape::Pulse::High : Tape::Pulse::Low;
switch(_outputToken) switch(output_token_)
{ {
case Leader: pulse.length.length = leader_zero_length; break; case Leader: pulse.length.length = leader_zero_length; break;
case Zero: pulse.length.length = (_bitPhase&2) ? one_length : zero_length; break; case Zero: pulse.length.length = (bit_phase_&2) ? one_length : zero_length; break;
case One: pulse.length.length = (_bitPhase&2) ? zero_length : one_length; break; case One: pulse.length.length = (bit_phase_&2) ? zero_length : one_length; break;
case WordMarker: pulse.length.length = (_bitPhase&2) ? one_length : marker_length; break; case WordMarker: pulse.length.length = (bit_phase_&2) ? one_length : marker_length; break;
case EndOfBlock: pulse.length.length = (_bitPhase&2) ? zero_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; case Silence: pulse.type = Tape::Pulse::Zero; pulse.length.length = 5000; break;
} }
return pulse; return pulse;
@ -102,16 +95,16 @@ Storage::Tape::Tape::Pulse PRG::virtual_get_next_pulse()
void PRG::virtual_reset() void PRG::virtual_reset()
{ {
_bitPhase = 3; bit_phase_ = 3;
fseek(_file, 2, SEEK_SET); fseek(file_, 2, SEEK_SET);
_filePhase = FilePhaseLeadIn; file_phase_ = FilePhaseLeadIn;
_phaseOffset = 0; phase_offset_ = 0;
_copy_mask = 0x80; copy_mask_ = 0x80;
} }
bool PRG::is_at_end() bool PRG::is_at_end()
{ {
return _filePhase == FilePhaseAtEnd; return file_phase_ == FilePhaseAtEnd;
} }
void PRG::get_next_output_token() void PRG::get_next_output_token()
@ -121,54 +114,54 @@ void PRG::get_next_output_token()
static const int leadin_length = 20000; static const int leadin_length = 20000;
static const int block_leadin_length = 5000; static const int block_leadin_length = 5000;
if(_filePhase == FilePhaseHeaderDataGap || _filePhase == FilePhaseAtEnd) if(file_phase_ == FilePhaseHeaderDataGap || file_phase_ == FilePhaseAtEnd)
{ {
_outputToken = Silence; output_token_ = Silence;
if(_filePhase != FilePhaseAtEnd) _filePhase = FilePhaseData; if(file_phase_ != FilePhaseAtEnd) file_phase_ = FilePhaseData;
return; return;
} }
// the lead-in is 20,000 instances of the lead-in pair; every other phase begins with 5000 // 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 // 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; output_token_ = Leader;
_phaseOffset++; phase_offset_++;
if(_filePhase == FilePhaseLeadIn && _phaseOffset == leadin_length) if(file_phase_ == FilePhaseLeadIn && phase_offset_ == leadin_length)
{ {
_phaseOffset = 0; phase_offset_ = 0;
_filePhase = (_filePhase == FilePhaseLeadIn) ? FilePhaseHeader : FilePhaseData; file_phase_ = (file_phase_ == FilePhaseLeadIn) ? FilePhaseHeader : FilePhaseData;
} }
return; return;
} }
// determine whether a new byte needs to be queued up // 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 bit_offset = block_offset % 10;
int byte_offset = block_offset / 10; int byte_offset = block_offset / 10;
_phaseOffset++; phase_offset_++;
if(!bit_offset && if(!bit_offset &&
( (
(_filePhase == FilePhaseHeader && byte_offset == block_length + countdown_bytes + 1) || (file_phase_ == FilePhaseHeader && byte_offset == block_length + countdown_bytes + 1) ||
feof(_file) feof(file_)
) )
) )
{ {
_outputToken = EndOfBlock; output_token_ = EndOfBlock;
_phaseOffset = 0; phase_offset_ = 0;
switch(_filePhase) switch(file_phase_)
{ {
default: break; default: break;
case FilePhaseHeader: case FilePhaseHeader:
_copy_mask ^= 0x80; copy_mask_ ^= 0x80;
if(_copy_mask) _filePhase = FilePhaseHeaderDataGap; if(copy_mask_) file_phase_ = FilePhaseHeaderDataGap;
break; break;
case FilePhaseData: case FilePhaseData:
_copy_mask ^= 0x80; copy_mask_ ^= 0x80;
fseek(_file, 2, SEEK_SET); fseek(file_, 2, SEEK_SET);
if(_copy_mask) _filePhase = FilePhaseAtEnd; if(copy_mask_) file_phase_ = FilePhaseAtEnd;
break; break;
} }
return; return;
@ -179,34 +172,34 @@ void PRG::get_next_output_token()
// the first nine bytes are countdown; the high bit is set if this is a header // the first nine bytes are countdown; the high bit is set if this is a header
if(byte_offset < countdown_bytes) 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 else
{ {
if(_filePhase == FilePhaseHeader) if(file_phase_ == FilePhaseHeader)
{ {
if(byte_offset == countdown_bytes + block_length) if(byte_offset == countdown_bytes + block_length)
{ {
_output_byte = _check_digit; output_byte_ = check_digit_;
} }
else else
{ {
if(byte_offset == countdown_bytes) _check_digit = 0; if(byte_offset == countdown_bytes) check_digit_ = 0;
if(_filePhase == FilePhaseHeader) if(file_phase_ == FilePhaseHeader)
{ {
switch(byte_offset - countdown_bytes) switch(byte_offset - countdown_bytes)
{ {
case 0: _output_byte = 0x03; break; case 0: output_byte_ = 0x03; break;
case 1: _output_byte = _load_address & 0xff; break; case 1: output_byte_ = load_address_ & 0xff; break;
case 2: _output_byte = (_load_address >> 8)&0xff; break; case 2: output_byte_ = (load_address_ >> 8)&0xff; break;
case 3: _output_byte = (_load_address + _length) & 0xff; break; case 3: output_byte_ = (load_address_ + length_) & 0xff; break;
case 4: _output_byte = ((_load_address + _length) >> 8) & 0xff; break; case 4: output_byte_ = ((load_address_ + length_) >> 8) & 0xff; break;
case 5: _output_byte = 0x50; break; // P case 5: output_byte_ = 0x50; break; // P
case 6: _output_byte = 0x52; break; // R case 6: output_byte_ = 0x52; break; // R
case 7: _output_byte = 0x47; break; // G case 7: output_byte_ = 0x47; break; // G
default: default:
_output_byte = 0x20; output_byte_ = 0x20;
break; break;
} }
} }
@ -214,32 +207,32 @@ void PRG::get_next_output_token()
} }
else else
{ {
_output_byte = (uint8_t)fgetc(_file); output_byte_ = (uint8_t)fgetc(file_);
if(feof(_file)) if(feof(file_))
{ {
_output_byte = _check_digit; output_byte_ = check_digit_;
} }
} }
_check_digit ^= _output_byte; check_digit_ ^= output_byte_;
} }
} }
switch(bit_offset) switch(bit_offset)
{ {
case 0: case 0:
_outputToken = WordMarker; output_token_ = WordMarker;
break; break;
default: // i.e. 18 default: // i.e. 18
_outputToken = (_output_byte & (1 << (bit_offset - 1))) ? One : Zero; output_token_ = (output_byte_ & (1 << (bit_offset - 1))) ? One : Zero;
break; break;
case 9: case 9:
{ {
uint8_t parity = _output_byte; uint8_t parity = output_byte_;
parity ^= (parity >> 4); parity ^= (parity >> 4);
parity ^= (parity >> 2); parity ^= (parity >> 2);
parity ^= (parity >> 1); parity ^= (parity >> 1);
_outputToken = (parity&1) ? Zero : One; output_token_ = (parity&1) ? Zero : One;
} }
break; break;
} }

View File

@ -10,7 +10,8 @@
#define Storage_Tape_PRG_hpp #define Storage_Tape_PRG_hpp
#include "../Tape.hpp" #include "../Tape.hpp"
#include <stdint.h> #include "../../FileHolder.hpp"
#include <cstdint>
namespace Storage { namespace Storage {
namespace Tape { namespace Tape {
@ -18,7 +19,7 @@ namespace Tape {
/*! /*!
Provides a @c Tape containing a .PRG, which is a direct local file. 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: public:
/*! /*!
Constructs a @c T64 containing content from the file with name @c file_name, of type @c type. 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. @throws ErrorBadFormat if this file could not be opened and recognised as the specified type.
*/ */
PRG(const char *file_name); PRG(const char *file_name);
~PRG();
enum { enum {
ErrorBadFormat ErrorBadFormat
@ -41,9 +41,8 @@ class PRG: public Tape {
Pulse virtual_get_next_pulse(); Pulse virtual_get_next_pulse();
void virtual_reset(); void virtual_reset();
FILE *_file; uint16_t load_address_;
uint16_t _load_address; uint16_t length_;
uint16_t _length;
enum FilePhase { enum FilePhase {
FilePhaseLeadIn, FilePhaseLeadIn,
@ -51,10 +50,10 @@ class PRG: public Tape {
FilePhaseHeaderDataGap, FilePhaseHeaderDataGap,
FilePhaseData, FilePhaseData,
FilePhaseAtEnd FilePhaseAtEnd
} _filePhase; } file_phase_;
int _phaseOffset; int phase_offset_;
int _bitPhase; int bit_phase_;
enum OutputToken { enum OutputToken {
Leader, Leader,
Zero, Zero,
@ -62,11 +61,11 @@ class PRG: public Tape {
WordMarker, WordMarker,
EndOfBlock, EndOfBlock,
Silence Silence
} _outputToken; } output_token_;
void get_next_output_token(); void get_next_output_token();
uint8_t _output_byte; uint8_t output_byte_;
uint8_t _check_digit; uint8_t check_digit_;
uint8_t _copy_mask; uint8_t copy_mask_;
}; };
} }

View File

@ -74,22 +74,22 @@ static int gzget32(gzFile file)
using namespace Storage::Tape; using namespace Storage::Tape;
UEF::UEF(const char *file_name) : UEF::UEF(const char *file_name) :
_time_base(1200), time_base_(1200),
_is_at_end(false), is_at_end_(false),
_pulse_pointer(0), pulse_pointer_(0),
_is_300_baud(false) is_300_baud_(false)
{ {
_file = gzopen(file_name, "rb"); file_ = gzopen(file_name, "rb");
char identifier[10]; 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!")) if(bytes_read < 10 || strcmp(identifier, "UEF File!"))
{ {
throw ErrorNotUEF; throw ErrorNotUEF;
} }
uint8_t version[2]; uint8_t version[2];
gzread(_file, version, 2); gzread(file_, version, 2);
if(version[1] > 0 || version[0] > 10) if(version[1] > 0 || version[0] > 10)
{ {
@ -101,41 +101,41 @@ UEF::UEF(const char *file_name) :
UEF::~UEF() UEF::~UEF()
{ {
gzclose(_file); gzclose(file_);
} }
#pragma mark - Public methods #pragma mark - Public methods
void UEF::virtual_reset() void UEF::virtual_reset()
{ {
gzseek(_file, 12, SEEK_SET); gzseek(file_, 12, SEEK_SET);
_is_at_end = false; is_at_end_ = false;
parse_next_tape_chunk(); parse_next_tape_chunk();
} }
bool UEF::is_at_end() bool UEF::is_at_end()
{ {
return _is_at_end; return is_at_end_;
} }
Storage::Tape::Tape::Pulse UEF::virtual_get_next_pulse() Storage::Tape::Tape::Pulse UEF::virtual_get_next_pulse()
{ {
Pulse next_pulse; Pulse next_pulse;
if(_is_at_end) if(is_at_end_)
{ {
next_pulse.type = Pulse::Zero; next_pulse.type = Pulse::Zero;
next_pulse.length.length = _time_base * 4; next_pulse.length.length = time_base_ * 4;
next_pulse.length.clock_rate = _time_base * 4; next_pulse.length.clock_rate = time_base_ * 4;
return next_pulse; return next_pulse;
} }
next_pulse = _queued_pulses[_pulse_pointer]; next_pulse = queued_pulses_[pulse_pointer_];
_pulse_pointer++; pulse_pointer_++;
if(_pulse_pointer == _queued_pulses.size()) if(pulse_pointer_ == queued_pulses_.size())
{ {
_queued_pulses.clear(); queued_pulses_.clear();
_pulse_pointer = 0; pulse_pointer_ = 0;
parse_next_tape_chunk(); parse_next_tape_chunk();
} }
return next_pulse; return next_pulse;
@ -145,18 +145,18 @@ Storage::Tape::Tape::Pulse UEF::virtual_get_next_pulse()
void UEF::parse_next_tape_chunk() void UEF::parse_next_tape_chunk()
{ {
while(!_queued_pulses.size()) while(queued_pulses_.empty())
{ {
// read chunk details // read chunk details
uint16_t chunk_id = (uint16_t)gzget16(_file); uint16_t chunk_id = (uint16_t)gzget16(file_);
uint32_t chunk_length = (uint32_t)gzget32(_file); uint32_t chunk_length = (uint32_t)gzget32(file_);
// figure out where the next chunk will start // 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; return;
} }
@ -176,15 +176,15 @@ void UEF::parse_next_tape_chunk()
case 0x0113: // change of base rate case 0x0113: // change of base rate
{ {
// TODO: something smarter than just converting this to an int // TODO: something smarter than just converting this to an int
float new_time_base = gzgetfloat(_file); float new_time_base = gzgetfloat(file_);
_time_base = (unsigned int)roundf(new_time_base); time_base_ = (unsigned int)roundf(new_time_base);
} }
break; break;
case 0x0117: case 0x0117:
{ {
int baud_rate = gzget16(_file); int baud_rate = gzget16(file_);
_is_300_baud = (baud_rate == 300); is_300_baud_ = (baud_rate == 300);
} }
break; break;
@ -193,7 +193,7 @@ void UEF::parse_next_tape_chunk()
break; 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--) while(length--)
{ {
queue_implicit_byte(gzget8(_file)); queue_implicit_byte(gzget8(file_));
} }
} }
void UEF::queue_explicit_bit_pattern(uint32_t length) 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; uint8_t current_byte = 0;
for(size_t bit = 0; bit < length_in_bits; bit++) 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); queue_bit(current_byte&1);
current_byte >>= 1; current_byte >>= 1;
} }
@ -222,30 +222,30 @@ void UEF::queue_explicit_bit_pattern(uint32_t length)
void UEF::queue_integer_gap() void UEF::queue_integer_gap()
{ {
Time duration; Time duration;
duration.length = (unsigned int)gzget16(_file); duration.length = (unsigned int)gzget16(file_);
duration.clock_rate = _time_base; duration.clock_rate = time_base_;
_queued_pulses.emplace_back(Pulse::Zero, duration); queued_pulses_.emplace_back(Pulse::Zero, duration);
} }
void UEF::queue_floating_point_gap() void UEF::queue_floating_point_gap()
{ {
float length = gzgetfloat(_file); float length = gzgetfloat(file_);
Time duration; Time duration;
duration.length = (unsigned int)(length * 4000000); duration.length = (unsigned int)(length * 4000000);
duration.clock_rate = 4000000; duration.clock_rate = 4000000;
_queued_pulses.emplace_back(Pulse::Zero, duration); queued_pulses_.emplace_back(Pulse::Zero, duration);
} }
void UEF::queue_carrier_tone() 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); while(number_of_cycles--) queue_bit(1);
} }
void UEF::queue_carrier_tone_with_dummy() void UEF::queue_carrier_tone_with_dummy()
{ {
unsigned int pre_cycles = (unsigned int)gzget16(_file); unsigned int pre_cycles = (unsigned int)gzget16(file_);
unsigned int post_cycles = (unsigned int)gzget16(_file); unsigned int post_cycles = (unsigned int)gzget16(file_);
while(pre_cycles--) queue_bit(1); while(pre_cycles--) queue_bit(1);
queue_implicit_byte(0xaa); queue_implicit_byte(0xaa);
while(post_cycles--) queue_bit(1); while(post_cycles--) queue_bit(1);
@ -253,33 +253,33 @@ void UEF::queue_carrier_tone_with_dummy()
void UEF::queue_security_cycles() void UEF::queue_security_cycles()
{ {
int number_of_cycles = gzget24(_file); int number_of_cycles = gzget24(file_);
bool first_is_pulse = gzget8(_file) == 'P'; bool first_is_pulse = gzget8(file_) == 'P';
bool last_is_pulse = gzget8(_file) == 'P'; bool last_is_pulse = gzget8(file_) == 'P';
uint8_t current_byte = 0; uint8_t current_byte = 0;
for(int cycle = 0; cycle < number_of_cycles; cycle++) 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); int bit = (current_byte >> 7);
current_byte <<= 1; current_byte <<= 1;
Time duration; Time duration;
duration.length = bit ? 1 : 2; duration.length = bit ? 1 : 2;
duration.clock_rate = _time_base * 4; duration.clock_rate = time_base_ * 4;
if(!cycle && first_is_pulse) 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) 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 else
{ {
_queued_pulses.emplace_back(Pulse::Low, duration); queued_pulses_.emplace_back(Pulse::Low, duration);
_queued_pulses.emplace_back(Pulse::High, duration); queued_pulses_.emplace_back(Pulse::High, duration);
} }
} }
} }
@ -288,9 +288,9 @@ void UEF::queue_defined_data(uint32_t length)
{ {
if(length < 3) return; if(length < 3) return;
int bits_per_packet = gzget8(_file); int bits_per_packet = gzget8(file_);
char parity_type = (char)gzget8(_file); char parity_type = (char)gzget8(file_);
int number_of_stop_bits = gzget8(_file); int number_of_stop_bits = gzget8(file_);
bool has_extra_stop_wave = (number_of_stop_bits < 0); bool has_extra_stop_wave = (number_of_stop_bits < 0);
number_of_stop_bits = abs(number_of_stop_bits); number_of_stop_bits = abs(number_of_stop_bits);
@ -298,7 +298,7 @@ void UEF::queue_defined_data(uint32_t length)
length -= 3; length -= 3;
while(length--) while(length--)
{ {
uint8_t byte = gzget8(_file); uint8_t byte = gzget8(file_);
uint8_t parity_value = byte; uint8_t parity_value = byte;
parity_value ^= (parity_value >> 4); parity_value ^= (parity_value >> 4);
@ -326,9 +326,9 @@ void UEF::queue_defined_data(uint32_t length)
{ {
Time duration; Time duration;
duration.length = 1; duration.length = 1;
duration.clock_rate = _time_base * 4; duration.clock_rate = time_base_ * 4;
_queued_pulses.emplace_back(Pulse::Low, duration); queued_pulses_.emplace_back(Pulse::Low, duration);
_queued_pulses.emplace_back(Pulse::High, duration); queued_pulses_.emplace_back(Pulse::High, duration);
} }
} }
} }
@ -351,7 +351,7 @@ void UEF::queue_bit(int bit)
{ {
int number_of_cycles; int number_of_cycles;
Time duration; Time duration;
duration.clock_rate = _time_base * 4; duration.clock_rate = time_base_ * 4;
if(bit) if(bit)
{ {
@ -366,11 +366,11 @@ void UEF::queue_bit(int bit)
number_of_cycles = 1; number_of_cycles = 1;
} }
if(_is_300_baud) number_of_cycles *= 4; if(is_300_baud_) number_of_cycles *= 4;
while(number_of_cycles--) while(number_of_cycles--)
{ {
_queued_pulses.emplace_back(Pulse::Low, duration); queued_pulses_.emplace_back(Pulse::Low, duration);
_queued_pulses.emplace_back(Pulse::High, duration); queued_pulses_.emplace_back(Pulse::High, duration);
} }
} }

View File

@ -41,13 +41,13 @@ class UEF : public Tape {
void virtual_reset(); void virtual_reset();
Pulse virtual_get_next_pulse(); Pulse virtual_get_next_pulse();
gzFile _file; gzFile file_;
unsigned int _time_base; unsigned int time_base_;
bool _is_at_end; bool is_at_end_;
bool _is_300_baud; bool is_300_baud_;
std::vector<Pulse> _queued_pulses; std::vector<Pulse> queued_pulses_;
size_t _pulse_pointer; size_t pulse_pointer_;
void parse_next_tape_chunk(); void parse_next_tape_chunk();