mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-13 00:25:26 +00:00
Introduces a new class hierarchy for disk images.
Increasing independence of format-specific stuff and generic caching without mangling them into a common namespace, and allowing in some cases for a decrease in read/write blocking.
This commit is contained in:
@@ -100,6 +100,7 @@
|
|||||||
4B8378E21F336920005CA9E4 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8378E01F336920005CA9E4 /* CharacterMapper.cpp */; };
|
4B8378E21F336920005CA9E4 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8378E01F336920005CA9E4 /* CharacterMapper.cpp */; };
|
||||||
4B8378E51F3378C4005CA9E4 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8378E31F3378C4005CA9E4 /* CharacterMapper.cpp */; };
|
4B8378E51F3378C4005CA9E4 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8378E31F3378C4005CA9E4 /* CharacterMapper.cpp */; };
|
||||||
4B838F1F1F35FDCD0016B5E6 /* CPCDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B838F1D1F35FDCD0016B5E6 /* CPCDSK.cpp */; };
|
4B838F1F1F35FDCD0016B5E6 /* CPCDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B838F1D1F35FDCD0016B5E6 /* CPCDSK.cpp */; };
|
||||||
|
4B84E24A1F7498120054AB7D /* DiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B84E2481F7498120054AB7D /* DiskImage.cpp */; };
|
||||||
4B8805F01DCFC99C003085B1 /* Acorn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805EE1DCFC99C003085B1 /* Acorn.cpp */; };
|
4B8805F01DCFC99C003085B1 /* Acorn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805EE1DCFC99C003085B1 /* Acorn.cpp */; };
|
||||||
4B8805F41DCFD22A003085B1 /* Commodore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805F21DCFD22A003085B1 /* Commodore.cpp */; };
|
4B8805F41DCFD22A003085B1 /* Commodore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805F21DCFD22A003085B1 /* Commodore.cpp */; };
|
||||||
4B8805F71DCFF6C9003085B1 /* Commodore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805F51DCFF6C9003085B1 /* Commodore.cpp */; };
|
4B8805F71DCFF6C9003085B1 /* Commodore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805F51DCFF6C9003085B1 /* Commodore.cpp */; };
|
||||||
@@ -123,7 +124,6 @@
|
|||||||
4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA22B051D8817CE0008C640 /* Disk.cpp */; };
|
4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA22B051D8817CE0008C640 /* Disk.cpp */; };
|
||||||
4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */; };
|
4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */; };
|
||||||
4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA799931D8B656E0045123D /* StaticAnalyser.cpp */; };
|
4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA799931D8B656E0045123D /* StaticAnalyser.cpp */; };
|
||||||
4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */; };
|
|
||||||
4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B31D327F7E00DF5BA0 /* G64.cpp */; };
|
4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B31D327F7E00DF5BA0 /* G64.cpp */; };
|
||||||
4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */; };
|
4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */; };
|
||||||
4BACC5B11F3DFF7C0037C015 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */; };
|
4BACC5B11F3DFF7C0037C015 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */; };
|
||||||
@@ -649,6 +649,8 @@
|
|||||||
4B8378E41F3378C4005CA9E4 /* CharacterMapper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CharacterMapper.hpp; sourceTree = "<group>"; };
|
4B8378E41F3378C4005CA9E4 /* CharacterMapper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CharacterMapper.hpp; sourceTree = "<group>"; };
|
||||||
4B838F1D1F35FDCD0016B5E6 /* CPCDSK.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CPCDSK.cpp; sourceTree = "<group>"; };
|
4B838F1D1F35FDCD0016B5E6 /* CPCDSK.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CPCDSK.cpp; sourceTree = "<group>"; };
|
||||||
4B838F1E1F35FDCD0016B5E6 /* CPCDSK.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CPCDSK.hpp; sourceTree = "<group>"; };
|
4B838F1E1F35FDCD0016B5E6 /* CPCDSK.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CPCDSK.hpp; sourceTree = "<group>"; };
|
||||||
|
4B84E2481F7498120054AB7D /* DiskImage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DiskImage.cpp; sourceTree = "<group>"; };
|
||||||
|
4B84E2491F7498120054AB7D /* DiskImage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskImage.hpp; sourceTree = "<group>"; };
|
||||||
4B8805EE1DCFC99C003085B1 /* Acorn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Acorn.cpp; path = Parsers/Acorn.cpp; sourceTree = "<group>"; };
|
4B8805EE1DCFC99C003085B1 /* Acorn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Acorn.cpp; path = Parsers/Acorn.cpp; sourceTree = "<group>"; };
|
||||||
4B8805EF1DCFC99C003085B1 /* Acorn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Acorn.hpp; path = Parsers/Acorn.hpp; sourceTree = "<group>"; };
|
4B8805EF1DCFC99C003085B1 /* Acorn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Acorn.hpp; path = Parsers/Acorn.hpp; sourceTree = "<group>"; };
|
||||||
4B8805F21DCFD22A003085B1 /* Commodore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Commodore.cpp; path = Parsers/Commodore.cpp; sourceTree = "<group>"; };
|
4B8805F21DCFD22A003085B1 /* Commodore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Commodore.cpp; path = Parsers/Commodore.cpp; sourceTree = "<group>"; };
|
||||||
@@ -686,7 +688,6 @@
|
|||||||
4BA799931D8B656E0045123D /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = ../../StaticAnalyser/Atari/StaticAnalyser.cpp; sourceTree = "<group>"; };
|
4BA799931D8B656E0045123D /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = ../../StaticAnalyser/Atari/StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||||
4BA799941D8B656E0045123D /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/Atari/StaticAnalyser.hpp; sourceTree = "<group>"; };
|
4BA799941D8B656E0045123D /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/Atari/StaticAnalyser.hpp; sourceTree = "<group>"; };
|
||||||
4BA9C3CF1D8164A9002DDB61 /* ConfigurationTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ConfigurationTarget.hpp; sourceTree = "<group>"; };
|
4BA9C3CF1D8164A9002DDB61 /* ConfigurationTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ConfigurationTarget.hpp; sourceTree = "<group>"; };
|
||||||
4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Disk.cpp; sourceTree = "<group>"; };
|
|
||||||
4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Disk.hpp; sourceTree = "<group>"; };
|
4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Disk.hpp; sourceTree = "<group>"; };
|
||||||
4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Storage.hpp; sourceTree = "<group>"; };
|
4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Storage.hpp; sourceTree = "<group>"; };
|
||||||
4BAB62B31D327F7E00DF5BA0 /* G64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = G64.cpp; sourceTree = "<group>"; };
|
4BAB62B31D327F7E00DF5BA0 /* G64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = G64.cpp; sourceTree = "<group>"; };
|
||||||
@@ -1637,8 +1638,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4B0BE4261D3481E700D5256B /* DigitalPhaseLockedLoop.cpp */,
|
4B0BE4261D3481E700D5256B /* DigitalPhaseLockedLoop.cpp */,
|
||||||
4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */,
|
|
||||||
4B6C73BB1D387AE500AFCFCA /* DiskController.cpp */,
|
4B6C73BB1D387AE500AFCFCA /* DiskController.cpp */,
|
||||||
|
4B84E2481F7498120054AB7D /* DiskImage.cpp */,
|
||||||
4B30512B1D989E2200B4FED8 /* Drive.cpp */,
|
4B30512B1D989E2200B4FED8 /* Drive.cpp */,
|
||||||
4BBC95201F36B16C008F4C34 /* MFMDiskController.cpp */,
|
4BBC95201F36B16C008F4C34 /* MFMDiskController.cpp */,
|
||||||
4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */,
|
4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */,
|
||||||
@@ -1649,6 +1650,7 @@
|
|||||||
4B0BE4271D3481E700D5256B /* DigitalPhaseLockedLoop.hpp */,
|
4B0BE4271D3481E700D5256B /* DigitalPhaseLockedLoop.hpp */,
|
||||||
4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */,
|
4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */,
|
||||||
4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */,
|
4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */,
|
||||||
|
4B84E2491F7498120054AB7D /* DiskImage.hpp */,
|
||||||
4B30512C1D989E2200B4FED8 /* Drive.hpp */,
|
4B30512C1D989E2200B4FED8 /* Drive.hpp */,
|
||||||
4BBC95211F36B16C008F4C34 /* MFMDiskController.hpp */,
|
4BBC95211F36B16C008F4C34 /* MFMDiskController.hpp */,
|
||||||
4B3F1B451E0388D200DB26EE /* PCMPatchedTrack.hpp */,
|
4B3F1B451E0388D200DB26EE /* PCMPatchedTrack.hpp */,
|
||||||
@@ -2800,7 +2802,6 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
4B2BFC5F1D613E0200BA3AA9 /* TapePRG.cpp in Sources */,
|
4B2BFC5F1D613E0200BA3AA9 /* TapePRG.cpp in Sources */,
|
||||||
4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */,
|
|
||||||
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
|
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
|
||||||
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */,
|
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */,
|
||||||
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */,
|
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */,
|
||||||
@@ -2820,6 +2821,7 @@
|
|||||||
4B8334951F5E25B60097E338 /* C1540.cpp in Sources */,
|
4B8334951F5E25B60097E338 /* C1540.cpp in Sources */,
|
||||||
4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */,
|
4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */,
|
||||||
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
|
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
|
||||||
|
4B84E24A1F7498120054AB7D /* DiskImage.cpp in Sources */,
|
||||||
4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */,
|
4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */,
|
||||||
4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */,
|
4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */,
|
||||||
4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */,
|
4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */,
|
||||||
|
@@ -77,22 +77,22 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(lowercase_extension) {
|
if(lowercase_extension) {
|
||||||
Format("80", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 80
|
Format("80", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 80
|
||||||
Format("81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 81
|
Format("81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 81
|
||||||
Format("a26", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26
|
Format("a26", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26
|
||||||
Format("adf", result.disks, Disk::AcornADF, TargetPlatform::Acorn) // ADF
|
Format("adf", result.disks, Disk::DiskImageHolder<Storage::Disk::AcornADF>, TargetPlatform::Acorn) // ADF
|
||||||
Format("bin", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN
|
Format("bin", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN
|
||||||
Format("cdt", result.tapes, Tape::TZX, TargetPlatform::AmstradCPC) // CDT
|
Format("cdt", result.tapes, Tape::TZX, TargetPlatform::AmstradCPC) // CDT
|
||||||
Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW
|
Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW
|
||||||
Format("d64", result.disks, Disk::D64, TargetPlatform::Commodore) // D64
|
Format("d64", result.disks, Disk::DiskImageHolder<Storage::Disk::D64>, TargetPlatform::Commodore) // D64
|
||||||
Format("dsd", result.disks, Disk::SSD, TargetPlatform::Acorn) // DSD
|
Format("dsd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // DSD
|
||||||
Format("dsk", result.disks, Disk::CPCDSK, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC)
|
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::CPCDSK>, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC)
|
||||||
Format("dsk", result.disks, Disk::OricMFMDSK, TargetPlatform::Oric) // DSK (Oric)
|
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::OricMFMDSK>, TargetPlatform::Oric) // DSK (Oric)
|
||||||
Format("g64", result.disks, Disk::G64, TargetPlatform::Commodore) // G64
|
Format("g64", result.disks, Disk::DiskImageHolder<Storage::Disk::G64>, TargetPlatform::Commodore) // G64
|
||||||
Format("hfe", result.disks, Disk::HFE, TargetPlatform::AmstradCPC) // HFE (TODO: plus other target platforms)
|
Format("hfe", result.disks, Disk::DiskImageHolder<Storage::Disk::HFE>, TargetPlatform::AmstradCPC) // HFE (TODO: plus other target platforms)
|
||||||
Format("o", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // O
|
Format("o", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // O
|
||||||
Format("p", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P
|
Format("p", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P
|
||||||
Format("p81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P81
|
Format("p81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P81
|
||||||
|
|
||||||
// PRG
|
// PRG
|
||||||
if(!strcmp(lowercase_extension, "prg")) {
|
if(!strcmp(lowercase_extension, "prg")) {
|
||||||
@@ -106,12 +106,12 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Format("rom", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Acorn) // ROM
|
Format("rom", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Acorn) // ROM
|
||||||
Format("ssd", result.disks, Disk::SSD, TargetPlatform::Acorn) // SSD
|
Format("ssd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // SSD
|
||||||
Format("tap", result.tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP (Commodore)
|
Format("tap", result.tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP (Commodore)
|
||||||
Format("tap", result.tapes, Tape::OricTAP, TargetPlatform::Oric) // TAP (Oric)
|
Format("tap", result.tapes, Tape::OricTAP, TargetPlatform::Oric) // TAP (Oric)
|
||||||
Format("tzx", result.tapes, Tape::TZX, TargetPlatform::ZX8081) // TZX
|
Format("tzx", result.tapes, Tape::TZX, TargetPlatform::ZX8081) // TZX
|
||||||
Format("uef", result.tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape)
|
Format("uef", result.tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape)
|
||||||
|
|
||||||
#undef Format
|
#undef Format
|
||||||
#undef Insert
|
#undef Insert
|
||||||
|
@@ -1,49 +0,0 @@
|
|||||||
//
|
|
||||||
// Disk.cpp
|
|
||||||
// Clock Signal
|
|
||||||
//
|
|
||||||
// Created by Thomas Harte on 10/07/2016.
|
|
||||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "Disk.hpp"
|
|
||||||
|
|
||||||
using namespace Storage::Disk;
|
|
||||||
|
|
||||||
int Disk::get_id_for_track_at_position(unsigned int head, unsigned int position) {
|
|
||||||
return (int)(position * get_head_count() + head);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Disk::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {
|
|
||||||
if(get_is_read_only()) return;
|
|
||||||
|
|
||||||
int address = get_id_for_track_at_position(head, position);
|
|
||||||
cached_tracks_[address] = track;
|
|
||||||
|
|
||||||
if(!update_queue_) update_queue_.reset(new Concurrency::AsyncTaskQueue);
|
|
||||||
std::shared_ptr<Track> track_copy(track->clone());
|
|
||||||
update_queue_->enqueue([this, head, position, track_copy] {
|
|
||||||
store_updated_track_at_position(head, position, track_copy, file_access_mutex_);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Track> Disk::get_track_at_position(unsigned int head, unsigned int position) {
|
|
||||||
if(head >= get_head_count()) return nullptr;
|
|
||||||
if(position >= get_head_position_count()) return nullptr;
|
|
||||||
|
|
||||||
int address = get_id_for_track_at_position(head, position);
|
|
||||||
std::map<int, std::shared_ptr<Track>>::iterator cached_track = cached_tracks_.find(address);
|
|
||||||
if(cached_track != cached_tracks_.end()) return cached_track->second;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
|
|
||||||
std::shared_ptr<Track> track = get_uncached_track_at_position(head, position);
|
|
||||||
if(!track) return nullptr;
|
|
||||||
cached_tracks_[address] = track;
|
|
||||||
return track;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Disk::store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex) {}
|
|
||||||
|
|
||||||
void Disk::flush_updates() {
|
|
||||||
if(update_queue_) update_queue_->flush();
|
|
||||||
}
|
|
@@ -60,18 +60,6 @@ class Track {
|
|||||||
virtual Track *clone() = 0;
|
virtual Track *clone() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
|
||||||
Models a disk as a collection of tracks, providing a range of possible track positions and allowing
|
|
||||||
a point sampling of the track beneath any of those positions (if any).
|
|
||||||
|
|
||||||
The intention is not that tracks necessarily be evenly spaced; a head_position_count of 3 wih track
|
|
||||||
A appearing in positions 0 and 1, and track B appearing in position 2 is an appropriate use of this API
|
|
||||||
if it matches the media.
|
|
||||||
|
|
||||||
The track returned is point sampled only; if a particular disk drive has a sufficiently large head to
|
|
||||||
pick up multiple tracks at once then the drive responsible for asking for multiple tracks and for
|
|
||||||
merging the results.
|
|
||||||
*/
|
|
||||||
class Disk {
|
class Disk {
|
||||||
public:
|
public:
|
||||||
virtual ~Disk() {}
|
virtual ~Disk() {}
|
||||||
@@ -87,60 +75,25 @@ class Disk {
|
|||||||
/*!
|
/*!
|
||||||
@returns the number of heads (and, therefore, impliedly surfaces) available on this disk.
|
@returns the number of heads (and, therefore, impliedly surfaces) available on this disk.
|
||||||
*/
|
*/
|
||||||
virtual unsigned int get_head_count() { return 1; }
|
virtual unsigned int get_head_count() = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@returns the @c Track at @c position underneath @c head if there are any detectable events there;
|
@returns the @c Track at @c position underneath @c head if there are any detectable events there;
|
||||||
returns @c nullptr otherwise.
|
returns @c nullptr otherwise.
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
|
virtual std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Replaces the Track at position @c position underneath @c head with @c track. Ignored if this disk is read-only.
|
Replaces the Track at position @c position underneath @c head with @c track. Ignored if this disk is read-only.
|
||||||
Subclasses that are not read-only should use the protected methods @c get_is_modified and, optionally,
|
Subclasses that are not read-only should use the protected methods @c get_is_modified and, optionally,
|
||||||
@c get_modified_track_at_position to query for changes when closing.
|
@c get_modified_track_at_position to query for changes when closing.
|
||||||
*/
|
*/
|
||||||
void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track);
|
virtual void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@returns whether the disk image is read only. Defaults to @c true if not overridden.
|
@returns whether the disk image is read only. Defaults to @c true if not overridden.
|
||||||
*/
|
*/
|
||||||
virtual bool get_is_read_only() { return true; }
|
virtual bool get_is_read_only() = 0;
|
||||||
|
|
||||||
protected:
|
|
||||||
/*!
|
|
||||||
Subclasses should implement this to return the @c Track at @c position underneath @c head. Returned tracks
|
|
||||||
are cached internally so subclasses shouldn't attempt to build their own caches or worry about preparing
|
|
||||||
for track accesses at file load time. Appropriate behaviour is to create them lazily, on demand.
|
|
||||||
*/
|
|
||||||
virtual std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Subclasses that support writing should implement @c store_updated_track_at_position to determine which bytes
|
|
||||||
have to be written from @c track, then obtain @c file_access_mutex and write new data to their file to represent
|
|
||||||
the track underneath @c head at @c position.
|
|
||||||
|
|
||||||
The base class will ensure that calls are made to @c get_uncached_track_at_position only while it holds @c file_access_mutex;
|
|
||||||
that mutex therefore provides serialisation of file access.
|
|
||||||
|
|
||||||
This method will be called asynchronously. Subclasses are responsible for any synchronisation other than that
|
|
||||||
provided automatically via @c file_access_mutex.
|
|
||||||
*/
|
|
||||||
virtual void store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Subclasses that support writing should call @c flush_updates during their destructor if there is anything they
|
|
||||||
do in @c store_updated_track_at_position that would not be valid after their destructor has completed but prior
|
|
||||||
to Disk's constructor running.
|
|
||||||
*/
|
|
||||||
void flush_updates();
|
|
||||||
|
|
||||||
private:
|
|
||||||
int get_id_for_track_at_position(unsigned int head, unsigned int position);
|
|
||||||
std::map<int, std::shared_ptr<Track>> cached_tracks_;
|
|
||||||
|
|
||||||
std::mutex file_access_mutex_;
|
|
||||||
std::unique_ptr<Concurrency::AsyncTaskQueue> update_queue_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
16
Storage/Disk/DiskImage.cpp
Normal file
16
Storage/Disk/DiskImage.cpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// DiskImage.cpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 21/09/2017.
|
||||||
|
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "DiskImage.hpp"
|
||||||
|
|
||||||
|
using namespace Storage::Disk;
|
||||||
|
|
||||||
|
int DiskImageHolderBase::get_id_for_track_at_position(unsigned int head, unsigned int position) {
|
||||||
|
return (int)(position * get_head_count() + head);
|
||||||
|
}
|
||||||
|
|
147
Storage/Disk/DiskImage.hpp
Normal file
147
Storage/Disk/DiskImage.hpp
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
//
|
||||||
|
// DiskImage.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 21/09/2017.
|
||||||
|
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef DiskImage_hpp
|
||||||
|
#define DiskImage_hpp
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include "Disk.hpp"
|
||||||
|
|
||||||
|
namespace Storage {
|
||||||
|
namespace Disk {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Models a disk as a collection of tracks, providing a range of possible track positions and allowing
|
||||||
|
a point sampling of the track beneath any of those positions (if any).
|
||||||
|
|
||||||
|
The intention is not that tracks necessarily be evenly spaced; a head_position_count of 3 wih track
|
||||||
|
A appearing in positions 0 and 1, and track B appearing in position 2 is an appropriate use of this API
|
||||||
|
if it matches the media.
|
||||||
|
|
||||||
|
The track returned is point sampled only; if a particular disk drive has a sufficiently large head to
|
||||||
|
pick up multiple tracks at once then the drive responsible for asking for multiple tracks and for
|
||||||
|
merging the results.
|
||||||
|
*/
|
||||||
|
class DiskImage {
|
||||||
|
public:
|
||||||
|
virtual ~DiskImage() {}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@returns the number of discrete positions that this disk uses to model its complete surface area.
|
||||||
|
|
||||||
|
This is not necessarily a track count. There is no implicit guarantee that every position will
|
||||||
|
return a distinct track, or — e.g. if the media is holeless — will return any track at all.
|
||||||
|
*/
|
||||||
|
virtual unsigned int get_head_position_count() = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@returns the number of heads (and, therefore, impliedly surfaces) available on this disk.
|
||||||
|
*/
|
||||||
|
virtual unsigned int get_head_count() { return 1; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@returns the @c Track at @c position underneath @c head if there are any detectable events there;
|
||||||
|
returns @c nullptr otherwise.
|
||||||
|
*/
|
||||||
|
virtual std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Replaces the Track at position @c position underneath @c head with @c track. Ignored if this disk is read-only.
|
||||||
|
Subclasses that are not read-only should use the protected methods @c get_is_modified and, optionally,
|
||||||
|
@c get_modified_track_at_position to query for changes when closing.
|
||||||
|
*/
|
||||||
|
virtual void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@returns whether the disk image is read only. Defaults to @c true if not overridden.
|
||||||
|
*/
|
||||||
|
virtual bool get_is_read_only() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class PatchingDiskImage {
|
||||||
|
public:
|
||||||
|
struct TrackUpdate {
|
||||||
|
long file_offset;
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class DiskImageHolderBase: public Disk {
|
||||||
|
protected:
|
||||||
|
int get_id_for_track_at_position(unsigned int head, unsigned int position);
|
||||||
|
std::map<int, std::shared_ptr<Track>> cached_tracks_;
|
||||||
|
|
||||||
|
std::unique_ptr<Concurrency::AsyncTaskQueue> update_queue_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class DiskImageHolder: public DiskImageHolderBase {
|
||||||
|
public:
|
||||||
|
template <typename... Ts> DiskImageHolder(Ts&&... args) :
|
||||||
|
disk_image_(args...) {}
|
||||||
|
~DiskImageHolder();
|
||||||
|
|
||||||
|
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);
|
||||||
|
void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track);
|
||||||
|
bool get_is_read_only();
|
||||||
|
|
||||||
|
private:
|
||||||
|
T disk_image_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> unsigned int DiskImageHolder<T>::get_head_position_count() {
|
||||||
|
return disk_image_.get_head_position_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> unsigned int DiskImageHolder<T>::get_head_count() {
|
||||||
|
return disk_image_.get_head_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> bool DiskImageHolder<T>::get_is_read_only() {
|
||||||
|
return disk_image_.get_is_read_only();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void DiskImageHolder<T>::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {
|
||||||
|
if(disk_image_.get_is_read_only()) return;
|
||||||
|
|
||||||
|
int address = get_id_for_track_at_position(head, position);
|
||||||
|
cached_tracks_[address] = track;
|
||||||
|
|
||||||
|
if(!update_queue_) update_queue_.reset(new Concurrency::AsyncTaskQueue);
|
||||||
|
std::shared_ptr<Track> track_copy(track->clone());
|
||||||
|
update_queue_->enqueue([this, head, position, track_copy] {
|
||||||
|
disk_image_.set_track_at_position(head, position, track_copy);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> std::shared_ptr<Track> DiskImageHolder<T>::get_track_at_position(unsigned int head, unsigned int position) {
|
||||||
|
if(head >= get_head_count()) return nullptr;
|
||||||
|
if(position >= get_head_position_count()) return nullptr;
|
||||||
|
|
||||||
|
int address = get_id_for_track_at_position(head, position);
|
||||||
|
std::map<int, std::shared_ptr<Track>>::iterator cached_track = cached_tracks_.find(address);
|
||||||
|
if(cached_track != cached_tracks_.end()) return cached_track->second;
|
||||||
|
|
||||||
|
std::shared_ptr<Track> track = disk_image_.get_track_at_position(head, position);
|
||||||
|
if(!track) return nullptr;
|
||||||
|
cached_tracks_[address] = track;
|
||||||
|
return track;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> DiskImageHolder<T>::~DiskImageHolder() {
|
||||||
|
if(update_queue_) update_queue_->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* DiskImage_hpp */
|
@@ -256,7 +256,7 @@ Parser::Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Disk> &disk) :
|
|||||||
|
|
||||||
Parser::Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Track> &track) :
|
Parser::Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Track> &track) :
|
||||||
Parser(is_mfm) {
|
Parser(is_mfm) {
|
||||||
drive_->set_disk(std::make_shared<Disk::SingleTrackDisk>(track));
|
drive_->set_disk(std::make_shared<Disk::DiskImageHolder<Disk::SingleTrackDiskImage>>(track));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::seek_to_track(uint8_t track) {
|
void Parser::seek_to_track(uint8_t track) {
|
||||||
|
@@ -37,10 +37,6 @@ AcornADF::AcornADF(const char *file_name) :
|
|||||||
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() {
|
|
||||||
flush_updates();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int AcornADF::get_head_position_count() {
|
unsigned int AcornADF::get_head_position_count() {
|
||||||
return 80;
|
return 80;
|
||||||
}
|
}
|
||||||
@@ -57,27 +53,31 @@ long AcornADF::get_file_offset_for_position(unsigned int head, unsigned int posi
|
|||||||
return (position * 1 + head) * bytes_per_sector * sectors_per_track;
|
return (position * 1 + head) * bytes_per_sector * sectors_per_track;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Track> AcornADF::get_uncached_track_at_position(unsigned int head, unsigned int position) {
|
std::shared_ptr<Track> AcornADF::get_track_at_position(unsigned int head, unsigned int position) {
|
||||||
std::shared_ptr<Track> track;
|
std::shared_ptr<Track> track;
|
||||||
|
|
||||||
if(head >= 2) return track;
|
if(head >= 2) return track;
|
||||||
long file_offset = get_file_offset_for_position(head, position);
|
long file_offset = get_file_offset_for_position(head, position);
|
||||||
fseek(file_, file_offset, SEEK_SET);
|
|
||||||
|
|
||||||
std::vector<Storage::Encodings::MFM::Sector> sectors;
|
std::vector<Storage::Encodings::MFM::Sector> sectors;
|
||||||
for(unsigned int sector = 0; sector < sectors_per_track; sector++) {
|
{
|
||||||
Storage::Encodings::MFM::Sector new_sector;
|
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
|
||||||
new_sector.track = (uint8_t)position;
|
fseek(file_, file_offset, SEEK_SET);
|
||||||
new_sector.side = (uint8_t)head;
|
|
||||||
new_sector.sector = (uint8_t)sector;
|
|
||||||
new_sector.size = sector_size;
|
|
||||||
|
|
||||||
new_sector.data.resize(bytes_per_sector);
|
for(unsigned int sector = 0; sector < sectors_per_track; sector++) {
|
||||||
fread(&new_sector.data[0], 1, bytes_per_sector, file_);
|
Storage::Encodings::MFM::Sector new_sector;
|
||||||
if(feof(file_))
|
new_sector.track = (uint8_t)position;
|
||||||
break;
|
new_sector.side = (uint8_t)head;
|
||||||
|
new_sector.sector = (uint8_t)sector;
|
||||||
|
new_sector.size = sector_size;
|
||||||
|
|
||||||
sectors.push_back(std::move(new_sector));
|
new_sector.data.resize(bytes_per_sector);
|
||||||
|
fread(&new_sector.data[0], 1, bytes_per_sector, file_);
|
||||||
|
if(feof(file_))
|
||||||
|
break;
|
||||||
|
|
||||||
|
sectors.push_back(std::move(new_sector));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sectors.size()) return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors);
|
if(sectors.size()) return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors);
|
||||||
@@ -85,7 +85,7 @@ std::shared_ptr<Track> AcornADF::get_uncached_track_at_position(unsigned int hea
|
|||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AcornADF::store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex) {
|
void AcornADF::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {
|
||||||
std::vector<uint8_t> parsed_track;
|
std::vector<uint8_t> parsed_track;
|
||||||
Storage::Encodings::MFM::Parser parser(true, track);
|
Storage::Encodings::MFM::Parser parser(true, track);
|
||||||
for(unsigned int c = 0; c < sectors_per_track; c++) {
|
for(unsigned int c = 0; c < sectors_per_track; c++) {
|
||||||
@@ -99,8 +99,9 @@ void AcornADF::store_updated_track_at_position(unsigned int head, unsigned int p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock_guard(file_access_mutex);
|
|
||||||
long file_offset = get_file_offset_for_position(head, position);
|
long file_offset = get_file_offset_for_position(head, position);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
|
||||||
ensure_file_is_at_least_length(file_offset);
|
ensure_file_is_at_least_length(file_offset);
|
||||||
fseek(file_, file_offset, SEEK_SET);
|
fseek(file_, file_offset, SEEK_SET);
|
||||||
fwrite(parsed_track.data(), 1, parsed_track.size(), file_);
|
fwrite(parsed_track.data(), 1, parsed_track.size(), file_);
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#ifndef AcornADF_hpp
|
#ifndef AcornADF_hpp
|
||||||
#define AcornADF_hpp
|
#define AcornADF_hpp
|
||||||
|
|
||||||
#include "../Disk.hpp"
|
#include "../DiskImage.hpp"
|
||||||
#include "../../FileHolder.hpp"
|
#include "../../FileHolder.hpp"
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
@@ -18,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, public Storage::FileHolder {
|
class AcornADF: public DiskImage, 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.
|
||||||
@@ -27,20 +27,19 @@ class AcornADF: public Disk, public Storage::FileHolder {
|
|||||||
@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 {
|
||||||
ErrorNotAcornADF,
|
ErrorNotAcornADF,
|
||||||
};
|
};
|
||||||
|
|
||||||
// implemented to satisfy @c Disk
|
|
||||||
unsigned int get_head_position_count();
|
unsigned int get_head_position_count();
|
||||||
unsigned int get_head_count();
|
unsigned int get_head_count();
|
||||||
bool get_is_read_only();
|
bool get_is_read_only();
|
||||||
|
void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track);
|
||||||
|
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex);
|
std::mutex file_access_mutex_;
|
||||||
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
|
|
||||||
long get_file_offset_for_position(unsigned int head, unsigned int position);
|
long get_file_offset_for_position(unsigned int head, unsigned int position);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -50,7 +50,7 @@ bool CPCDSK::get_is_read_only() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Track> CPCDSK::get_uncached_track_at_position(unsigned int head, unsigned int position) {
|
std::shared_ptr<Track> CPCDSK::get_track_at_position(unsigned int head, unsigned int position) {
|
||||||
// Given that thesea are interleaved images, determine which track, chronologically, is being requested.
|
// Given that thesea are interleaved images, determine which track, chronologically, is being requested.
|
||||||
unsigned int chronological_track = (position * head_count_) + head;
|
unsigned int chronological_track = (position * head_count_) + head;
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#ifndef CPCDSK_hpp
|
#ifndef CPCDSK_hpp
|
||||||
#define CPCDSK_hpp
|
#define CPCDSK_hpp
|
||||||
|
|
||||||
#include "../Disk.hpp"
|
#include "../DiskImage.hpp"
|
||||||
#include "../../FileHolder.hpp"
|
#include "../../FileHolder.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -20,7 +20,7 @@ namespace Disk {
|
|||||||
/*!
|
/*!
|
||||||
Provies a @c Disk containing an Amstrad CPC-stype disk image — some arrangement of sectors with status bits.
|
Provies a @c Disk containing an Amstrad CPC-stype disk image — some arrangement of sectors with status bits.
|
||||||
*/
|
*/
|
||||||
class CPCDSK: public Disk, public Storage::FileHolder {
|
class CPCDSK: public DiskImage, 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.
|
||||||
@@ -38,9 +38,9 @@ class CPCDSK: public Disk, public Storage::FileHolder {
|
|||||||
unsigned int get_head_position_count();
|
unsigned int get_head_position_count();
|
||||||
unsigned int get_head_count();
|
unsigned int get_head_count();
|
||||||
bool get_is_read_only();
|
bool get_is_read_only();
|
||||||
|
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
|
|
||||||
|
|
||||||
unsigned int head_count_;
|
unsigned int head_count_;
|
||||||
unsigned int head_position_count_;
|
unsigned int head_position_count_;
|
||||||
|
@@ -38,7 +38,7 @@ unsigned int D64::get_head_position_count() {
|
|||||||
return number_of_tracks_*2;
|
return number_of_tracks_*2;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Track> D64::get_uncached_track_at_position(unsigned int head, unsigned int position) {
|
std::shared_ptr<Track> D64::get_track_at_position(unsigned int head, unsigned int position) {
|
||||||
// every other track is missing, as is any head above 0
|
// every other track is missing, as is any head above 0
|
||||||
if(position&1 || head)
|
if(position&1 || head)
|
||||||
return std::shared_ptr<Track>();
|
return std::shared_ptr<Track>();
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#ifndef D64_hpp
|
#ifndef D64_hpp
|
||||||
#define D64_hpp
|
#define D64_hpp
|
||||||
|
|
||||||
#include "../Disk.hpp"
|
#include "../DiskImage.hpp"
|
||||||
#include "../../FileHolder.hpp"
|
#include "../../FileHolder.hpp"
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
@@ -18,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, public Storage::FileHolder {
|
class D64: public DiskImage, 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.
|
||||||
@@ -34,9 +34,9 @@ class D64: public Disk, public Storage::FileHolder {
|
|||||||
|
|
||||||
// implemented to satisfy @c Disk
|
// implemented to satisfy @c Disk
|
||||||
unsigned int get_head_position_count();
|
unsigned int get_head_position_count();
|
||||||
|
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
|
|
||||||
unsigned int number_of_tracks_;
|
unsigned int number_of_tracks_;
|
||||||
uint16_t disk_id_;
|
uint16_t disk_id_;
|
||||||
};
|
};
|
||||||
|
@@ -34,7 +34,7 @@ unsigned int G64::get_head_position_count() {
|
|||||||
return number_of_tracks_ > 84 ? number_of_tracks_ : 84;
|
return number_of_tracks_ > 84 ? number_of_tracks_ : 84;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Track> G64::get_uncached_track_at_position(unsigned int head, unsigned int position) {
|
std::shared_ptr<Track> G64::get_track_at_position(unsigned int head, unsigned int position) {
|
||||||
std::shared_ptr<Track> resulting_track;
|
std::shared_ptr<Track> resulting_track;
|
||||||
|
|
||||||
// if there's definitely no track here, return the empty track
|
// if there's definitely no track here, return the empty track
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#ifndef G64_hpp
|
#ifndef G64_hpp
|
||||||
#define G64_hpp
|
#define G64_hpp
|
||||||
|
|
||||||
#include "../Disk.hpp"
|
#include "../DiskImage.hpp"
|
||||||
#include "../../FileHolder.hpp"
|
#include "../../FileHolder.hpp"
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
@@ -18,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, public Storage::FileHolder {
|
class G64: public DiskImage, 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.
|
||||||
@@ -37,9 +37,9 @@ class G64: public Disk, public Storage::FileHolder {
|
|||||||
|
|
||||||
// implemented to satisfy @c Disk
|
// implemented to satisfy @c Disk
|
||||||
unsigned int get_head_position_count();
|
unsigned int get_head_position_count();
|
||||||
|
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
|
|
||||||
uint8_t number_of_tracks_;
|
uint8_t number_of_tracks_;
|
||||||
uint16_t maximum_track_size_;
|
uint16_t maximum_track_size_;
|
||||||
};
|
};
|
||||||
|
@@ -35,11 +35,7 @@ unsigned int HFE::get_head_count() {
|
|||||||
return head_count_;
|
return head_count_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HFE::get_is_read_only() {
|
std::shared_ptr<Track> HFE::get_track_at_position(unsigned int head, unsigned int position) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Track> HFE::get_uncached_track_at_position(unsigned int head, unsigned int position) {
|
|
||||||
// Get track position and length from the lookup table; data is then always interleaved
|
// Get track position and length from the lookup table; data is then always interleaved
|
||||||
// based on an assumption of two heads.
|
// based on an assumption of two heads.
|
||||||
fseek(file_, track_list_offset_ + position * 4, SEEK_SET);
|
fseek(file_, track_list_offset_ + position * 4, SEEK_SET);
|
||||||
@@ -84,3 +80,4 @@ std::shared_ptr<Track> HFE::get_uncached_track_at_position(unsigned int head, un
|
|||||||
std::shared_ptr<Track> track(new PCMTrack(segment));
|
std::shared_ptr<Track> track(new PCMTrack(segment));
|
||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#ifndef HFE_hpp
|
#ifndef HFE_hpp
|
||||||
#define HFE_hpp
|
#define HFE_hpp
|
||||||
|
|
||||||
#include "../Disk.hpp"
|
#include "../DiskImage.hpp"
|
||||||
#include "../../FileHolder.hpp"
|
#include "../../FileHolder.hpp"
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
@@ -18,7 +18,7 @@ namespace Disk {
|
|||||||
/*!
|
/*!
|
||||||
Provies a @c Disk containing an HFE disk image — a bit stream representation of a floppy.
|
Provies a @c Disk containing an HFE disk image — a bit stream representation of a floppy.
|
||||||
*/
|
*/
|
||||||
class HFE: public Disk, public Storage::FileHolder {
|
class HFE: public DiskImage, 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.
|
||||||
@@ -36,10 +36,9 @@ class HFE: public Disk, public Storage::FileHolder {
|
|||||||
// implemented to satisfy @c Disk
|
// implemented to satisfy @c Disk
|
||||||
unsigned int get_head_position_count();
|
unsigned int get_head_position_count();
|
||||||
unsigned int get_head_count();
|
unsigned int get_head_count();
|
||||||
bool get_is_read_only();
|
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
|
|
||||||
|
|
||||||
unsigned int head_count_;
|
unsigned int head_count_;
|
||||||
unsigned int track_count_;
|
unsigned int track_count_;
|
||||||
|
@@ -25,10 +25,6 @@ OricMFMDSK::OricMFMDSK(const char *file_name) :
|
|||||||
throw ErrorNotOricMFMDSK;
|
throw ErrorNotOricMFMDSK;
|
||||||
}
|
}
|
||||||
|
|
||||||
OricMFMDSK::~OricMFMDSK() {
|
|
||||||
flush_updates();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int OricMFMDSK::get_head_position_count() {
|
unsigned int OricMFMDSK::get_head_position_count() {
|
||||||
return track_count_;
|
return track_count_;
|
||||||
}
|
}
|
||||||
@@ -54,59 +50,61 @@ long OricMFMDSK::get_file_offset_for_position(unsigned int head, unsigned int po
|
|||||||
return (seek_offset * 6400) + 256;
|
return (seek_offset * 6400) + 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Track> OricMFMDSK::get_uncached_track_at_position(unsigned int head, unsigned int position) {
|
std::shared_ptr<Track> OricMFMDSK::get_track_at_position(unsigned int head, unsigned int position) {
|
||||||
fseek(file_, get_file_offset_for_position(head, position), SEEK_SET);
|
|
||||||
|
|
||||||
PCMSegment segment;
|
PCMSegment segment;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
|
||||||
|
fseek(file_, get_file_offset_for_position(head, position), SEEK_SET);
|
||||||
|
|
||||||
// The file format omits clock bits. So it's not a genuine MFM capture.
|
// The file format omits clock bits. So it's not a genuine MFM capture.
|
||||||
// A consumer must contextually guess when an FB, FC, etc is meant to be a control mark.
|
// A consumer must contextually guess when an FB, FC, etc is meant to be a control mark.
|
||||||
size_t track_offset = 0;
|
size_t track_offset = 0;
|
||||||
uint8_t last_header[6] = {0, 0, 0, 0, 0, 0};
|
uint8_t last_header[6] = {0, 0, 0, 0, 0, 0};
|
||||||
std::unique_ptr<Encodings::MFM::Encoder> encoder = Encodings::MFM::GetMFMEncoder(segment.data);
|
std::unique_ptr<Encodings::MFM::Encoder> encoder = Encodings::MFM::GetMFMEncoder(segment.data);
|
||||||
bool did_sync = false;
|
bool did_sync = false;
|
||||||
while(track_offset < 6250) {
|
while(track_offset < 6250) {
|
||||||
uint8_t next_byte = (uint8_t)fgetc(file_);
|
uint8_t next_byte = (uint8_t)fgetc(file_);
|
||||||
track_offset++;
|
track_offset++;
|
||||||
|
|
||||||
switch(next_byte) {
|
switch(next_byte) {
|
||||||
default: {
|
default: {
|
||||||
encoder->add_byte(next_byte);
|
encoder->add_byte(next_byte);
|
||||||
if(did_sync) {
|
if(did_sync) {
|
||||||
switch(next_byte) {
|
switch(next_byte) {
|
||||||
default: break;
|
default: break;
|
||||||
|
|
||||||
case 0xfe:
|
case 0xfe:
|
||||||
for(int byte = 0; byte < 6; byte++) {
|
for(int byte = 0; byte < 6; byte++) {
|
||||||
last_header[byte] = (uint8_t)fgetc(file_);
|
last_header[byte] = (uint8_t)fgetc(file_);
|
||||||
encoder->add_byte(last_header[byte]);
|
encoder->add_byte(last_header[byte]);
|
||||||
track_offset++;
|
track_offset++;
|
||||||
if(track_offset == 6250) break;
|
if(track_offset == 6250) break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xfb:
|
case 0xfb:
|
||||||
for(int byte = 0; byte < (128 << last_header[3]) + 2; byte++) {
|
for(int byte = 0; byte < (128 << last_header[3]) + 2; byte++) {
|
||||||
encoder->add_byte((uint8_t)fgetc(file_));
|
encoder->add_byte((uint8_t)fgetc(file_));
|
||||||
track_offset++;
|
track_offset++;
|
||||||
if(track_offset == 6250) break;
|
if(track_offset == 6250) break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
did_sync = false;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
did_sync = false;
|
case 0xa1: // a synchronisation mark that implies a sector or header coming
|
||||||
|
encoder->output_short(Storage::Encodings::MFM::MFMSync);
|
||||||
|
did_sync = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xc2: // an 'ordinary' synchronisation mark
|
||||||
|
encoder->output_short(Storage::Encodings::MFM::MFMIndexSync);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xa1: // a synchronisation mark that implies a sector or header coming
|
|
||||||
encoder->output_short(Storage::Encodings::MFM::MFMSync);
|
|
||||||
did_sync = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xc2: // an 'ordinary' synchronisation mark
|
|
||||||
encoder->output_short(Storage::Encodings::MFM::MFMIndexSync);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,13 +114,13 @@ std::shared_ptr<Track> OricMFMDSK::get_uncached_track_at_position(unsigned int h
|
|||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OricMFMDSK::store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex) {
|
void OricMFMDSK::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {
|
||||||
Storage::Encodings::MFM::Parser parser(true, track);
|
Storage::Encodings::MFM::Parser parser(true, track);
|
||||||
std::vector<uint8_t> parsed_track = parser.get_track(0);
|
std::vector<uint8_t> parsed_track = parser.get_track(0);
|
||||||
long file_offset = get_file_offset_for_position(head, position);
|
long file_offset = get_file_offset_for_position(head, position);
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock_guard(file_access_mutex);
|
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
|
||||||
fseek(file_, file_offset, SEEK_SET);
|
fseek(file_, file_offset, SEEK_SET);
|
||||||
size_t track_size = std::min((size_t)6400, parsed_track.size());
|
size_t track_size = std::min((size_t)6400, parsed_track.size());
|
||||||
fwrite(parsed_track.data(), 1, track_size, file_);
|
fwrite(parsed_track.data(), 1, track_size, file_);
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#ifndef OricMFMDSK_hpp
|
#ifndef OricMFMDSK_hpp
|
||||||
#define OricMFMDSK_hpp
|
#define OricMFMDSK_hpp
|
||||||
|
|
||||||
#include "../Disk.hpp"
|
#include "../DiskImage.hpp"
|
||||||
#include "../../FileHolder.hpp"
|
#include "../../FileHolder.hpp"
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
@@ -18,16 +18,14 @@ namespace Disk {
|
|||||||
/*!
|
/*!
|
||||||
Provies a @c Disk containing an Oric MFM-stype disk image — a stream of the MFM data bits with clocks omitted.
|
Provies a @c Disk containing an Oric MFM-stype disk image — a stream of the MFM data bits with clocks omitted.
|
||||||
*/
|
*/
|
||||||
class OricMFMDSK: public Disk, public Storage::FileHolder {
|
class OricMFMDSK: public DiskImage, public Storage::FileHolder {
|
||||||
public:
|
public:
|
||||||
/*!
|
/*!
|
||||||
Construct an @c AcornADF containing content from the file with name @c file_name.
|
Construct an @c OricMFMDSK containing content from the file with name @c file_name.
|
||||||
|
|
||||||
@throws ErrorCantOpen if this file can't be opened.
|
@throws ErrorNotOricMFMDSK if the file doesn't appear to contain an Oric MFM format image.
|
||||||
@throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image.
|
|
||||||
*/
|
*/
|
||||||
OricMFMDSK(const char *file_name);
|
OricMFMDSK(const char *file_name);
|
||||||
~OricMFMDSK();
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
ErrorNotOricMFMDSK,
|
ErrorNotOricMFMDSK,
|
||||||
@@ -37,10 +35,11 @@ class OricMFMDSK: public Disk, public Storage::FileHolder {
|
|||||||
unsigned int get_head_position_count();
|
unsigned int get_head_position_count();
|
||||||
unsigned int get_head_count();
|
unsigned int get_head_count();
|
||||||
bool get_is_read_only();
|
bool get_is_read_only();
|
||||||
|
void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track);
|
||||||
|
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex);
|
std::mutex file_access_mutex_;
|
||||||
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
|
|
||||||
long get_file_offset_for_position(unsigned int head, unsigned int position);
|
long get_file_offset_for_position(unsigned int head, unsigned int position);
|
||||||
|
|
||||||
uint32_t head_count_;
|
uint32_t head_count_;
|
||||||
|
@@ -28,10 +28,6 @@ SSD::SSD(const char *file_name) :
|
|||||||
else if(track_count_ < 80) track_count_ = 80;
|
else if(track_count_ < 80) track_count_ = 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
SSD::~SSD() {
|
|
||||||
flush_updates();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int SSD::get_head_position_count() {
|
unsigned int SSD::get_head_position_count() {
|
||||||
return track_count_;
|
return track_count_;
|
||||||
}
|
}
|
||||||
@@ -48,29 +44,33 @@ long SSD::get_file_offset_for_position(unsigned int head, unsigned int position)
|
|||||||
return (position * head_count_ + head) * 256 * 10;
|
return (position * head_count_ + head) * 256 * 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Track> SSD::get_uncached_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;
|
||||||
fseek(file_, get_file_offset_for_position(head, position), SEEK_SET);
|
|
||||||
|
|
||||||
std::vector<Storage::Encodings::MFM::Sector> sectors;
|
std::vector<Storage::Encodings::MFM::Sector> sectors;
|
||||||
for(int sector = 0; sector < 10; sector++) {
|
{
|
||||||
Storage::Encodings::MFM::Sector new_sector;
|
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
|
||||||
new_sector.track = (uint8_t)position;
|
fseek(file_, get_file_offset_for_position(head, position), SEEK_SET);
|
||||||
new_sector.side = 0;
|
|
||||||
new_sector.sector = (uint8_t)sector;
|
|
||||||
new_sector.size = 1;
|
|
||||||
|
|
||||||
new_sector.data.resize(256);
|
for(int sector = 0; sector < 10; sector++) {
|
||||||
fread(new_sector.data.data(), 1, 256, file_);
|
Storage::Encodings::MFM::Sector new_sector;
|
||||||
|
new_sector.track = (uint8_t)position;
|
||||||
|
new_sector.side = 0;
|
||||||
|
new_sector.sector = (uint8_t)sector;
|
||||||
|
new_sector.size = 1;
|
||||||
|
|
||||||
// zero out if this wasn't present in the disk image; it's still appropriate to put a sector
|
new_sector.data.resize(256);
|
||||||
// on disk because one will have been placed during formatting, but there's no reason to leak
|
fread(new_sector.data.data(), 1, 256, file_);
|
||||||
// information from outside the emulated machine's world
|
|
||||||
if(feof(file_)) memset(new_sector.data.data(), 0, 256);
|
|
||||||
|
|
||||||
sectors.push_back(std::move(new_sector));
|
// zero out if this wasn't present in the disk image; it's still appropriate to put a sector
|
||||||
|
// on disk because one will have been placed during formatting, but there's no reason to leak
|
||||||
|
// information from outside the emulated machine's world
|
||||||
|
if(feof(file_)) memset(new_sector.data.data(), 0, 256);
|
||||||
|
|
||||||
|
sectors.push_back(std::move(new_sector));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sectors.size()) return Storage::Encodings::MFM::GetFMTrackWithSectors(sectors);
|
if(sectors.size()) return Storage::Encodings::MFM::GetFMTrackWithSectors(sectors);
|
||||||
@@ -78,23 +78,24 @@ std::shared_ptr<Track> SSD::get_uncached_track_at_position(unsigned int head, un
|
|||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSD::store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex) {
|
void SSD::set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track) {
|
||||||
std::vector<uint8_t> parsed_track;
|
std::vector<uint8_t> data;
|
||||||
Storage::Encodings::MFM::Parser parser(false, track);
|
Storage::Encodings::MFM::Parser parser(false, track);
|
||||||
for(unsigned int c = 0; c < 10; c++) {
|
for(unsigned int c = 0; c < 10; c++) {
|
||||||
std::shared_ptr<Storage::Encodings::MFM::Sector> sector = parser.get_sector(0, (uint8_t)position, (uint8_t)c);
|
std::shared_ptr<Storage::Encodings::MFM::Sector> sector = parser.get_sector(0, (uint8_t)position, (uint8_t)c);
|
||||||
if(sector) {
|
if(sector) {
|
||||||
parsed_track.insert(parsed_track.end(), sector->data.begin(), sector->data.end());
|
data.insert(data.end(), sector->data.begin(), sector->data.end());
|
||||||
} else {
|
} else {
|
||||||
// TODO: what's correct here? Warn the user that whatever has been written to the disk,
|
// TODO: what's correct here? Warn the user that whatever has been written to the disk,
|
||||||
// it can no longer be stored as an SSD? If so, warn them by what route?
|
// it can no longer be stored as an SSD? If so, warn them by what route?
|
||||||
parsed_track.resize(parsed_track.size() + 256);
|
data.resize(data.size() + 256);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock_guard(file_access_mutex);
|
|
||||||
long file_offset = get_file_offset_for_position(head, position);
|
long file_offset = get_file_offset_for_position(head, position);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock_guard(file_access_mutex_);
|
||||||
ensure_file_is_at_least_length(file_offset);
|
ensure_file_is_at_least_length(file_offset);
|
||||||
fseek(file_, file_offset, SEEK_SET);
|
fseek(file_, file_offset, SEEK_SET);
|
||||||
fwrite(parsed_track.data(), 1, parsed_track.size(), file_);
|
fwrite(data.data(), 1, data.size(), file_);
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#ifndef SSD_hpp
|
#ifndef SSD_hpp
|
||||||
#define SSD_hpp
|
#define SSD_hpp
|
||||||
|
|
||||||
#include "../Disk.hpp"
|
#include "../DiskImage.hpp"
|
||||||
#include "../../FileHolder.hpp"
|
#include "../../FileHolder.hpp"
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
@@ -18,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, public Storage::FileHolder {
|
class SSD: public DiskImage, 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.
|
||||||
@@ -27,7 +27,6 @@ class SSD: public Disk, public Storage::FileHolder {
|
|||||||
@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 {
|
||||||
ErrorNotSSD,
|
ErrorNotSSD,
|
||||||
@@ -37,10 +36,11 @@ class SSD: public Disk, public Storage::FileHolder {
|
|||||||
unsigned int get_head_position_count();
|
unsigned int get_head_position_count();
|
||||||
unsigned int get_head_count();
|
unsigned int get_head_count();
|
||||||
bool get_is_read_only();
|
bool get_is_read_only();
|
||||||
|
void set_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track);
|
||||||
|
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex);
|
std::mutex file_access_mutex_;
|
||||||
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
|
|
||||||
long get_file_offset_for_position(unsigned int head, unsigned int position);
|
long get_file_offset_for_position(unsigned int head, unsigned int position);
|
||||||
|
|
||||||
unsigned int head_count_;
|
unsigned int head_count_;
|
||||||
|
@@ -10,13 +10,13 @@
|
|||||||
|
|
||||||
using namespace Storage::Disk;
|
using namespace Storage::Disk;
|
||||||
|
|
||||||
SingleTrackDisk::SingleTrackDisk(const std::shared_ptr<Track> &track) :
|
SingleTrackDiskImage::SingleTrackDiskImage(const std::shared_ptr<Track> &track) :
|
||||||
track_(track) {}
|
track_(track) {}
|
||||||
|
|
||||||
unsigned int SingleTrackDisk::get_head_position_count() {
|
unsigned int SingleTrackDiskImage::get_head_position_count() {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Track> SingleTrackDisk::get_uncached_track_at_position(unsigned int head, unsigned int position) {
|
std::shared_ptr<Track> SingleTrackDiskImage::get_track_at_position(unsigned int head, unsigned int position) {
|
||||||
return track_;
|
return track_;
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#ifndef SingleTrackDisk_hpp
|
#ifndef SingleTrackDisk_hpp
|
||||||
#define SingleTrackDisk_hpp
|
#define SingleTrackDisk_hpp
|
||||||
|
|
||||||
#include "Disk.hpp"
|
#include "DiskImage.hpp"
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
namespace Disk {
|
namespace Disk {
|
||||||
@@ -17,16 +17,15 @@ namespace Disk {
|
|||||||
/*!
|
/*!
|
||||||
Provides a disk that has houses a single track.
|
Provides a disk that has houses a single track.
|
||||||
*/
|
*/
|
||||||
class SingleTrackDisk: public Disk {
|
class SingleTrackDiskImage: public DiskImage {
|
||||||
public:
|
public:
|
||||||
/// Constructs a single-track disk with the track @c track.
|
/// Constructs a single-track disk with the track @c track.
|
||||||
SingleTrackDisk(const std::shared_ptr<Track> &track);
|
SingleTrackDiskImage(const std::shared_ptr<Track> &track);
|
||||||
|
unsigned int get_head_position_count();
|
||||||
|
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Track> track_;
|
std::shared_ptr<Track> track_;
|
||||||
|
|
||||||
unsigned int get_head_position_count();
|
|
||||||
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
@@ -124,6 +125,7 @@ class FileHolder {
|
|||||||
FILE *file_;
|
FILE *file_;
|
||||||
struct stat file_stats_;
|
struct stat file_stats_;
|
||||||
bool is_read_only_;
|
bool is_read_only_;
|
||||||
|
std::mutex file_access_mutex_;
|
||||||
|
|
||||||
const std::string name_;
|
const std::string name_;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user