diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index de6b2ed0d..a5d8e6ec1 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -281,6 +281,8 @@ 4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */; }; 4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA799931D8B656E0045123D /* StaticAnalyser.cpp */; }; 4BAD13441FF709C700FD114A /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E61051FF34737002A9DBD /* MSX.cpp */; }; + 4BAF2B4E2004580C00480230 /* DMK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAF2B4C2004580C00480230 /* DMK.cpp */; }; + 4BAF2B4F2004580C00480230 /* DMK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAF2B4C2004580C00480230 /* DMK.cpp */; }; 4BB17D4E1ED7909F00ABD1E1 /* tests.expected.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */; }; 4BB17D4F1ED7909F00ABD1E1 /* tests.in.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BB17D4D1ED7909F00ABD1E1 /* tests.in.json */; }; 4BB298F11B587D8400A49093 /* start in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E51B587D8300A49093 /* start */; }; @@ -938,6 +940,8 @@ 4BA9C3CF1D8164A9002DDB61 /* ConfigurationTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ConfigurationTarget.hpp; sourceTree = ""; }; 4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Disk.hpp; sourceTree = ""; }; 4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Storage.hpp; sourceTree = ""; }; + 4BAF2B4C2004580C00480230 /* DMK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DMK.cpp; sourceTree = ""; }; + 4BAF2B4D2004580C00480230 /* DMK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DMK.hpp; sourceTree = ""; }; 4BB06B211F316A3F00600C7A /* ForceInline.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ForceInline.hpp; sourceTree = ""; }; 4BB146C61F49D7D700253439 /* Sleeper.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sleeper.hpp; sourceTree = ""; }; 4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.expected.json; path = FUSE/tests.expected.json; sourceTree = ""; }; @@ -1786,6 +1790,7 @@ 4B45188D1F75FD1B00926311 /* AcornADF.cpp */, 4B45188F1F75FD1B00926311 /* CPCDSK.cpp */, 4B4518911F75FD1B00926311 /* D64.cpp */, + 4BAF2B4C2004580C00480230 /* DMK.cpp */, 4B4518931F75FD1B00926311 /* G64.cpp */, 4B4518951F75FD1B00926311 /* HFE.cpp */, 4B58601C1F806AB200AEE2E3 /* MFMSectorDump.cpp */, @@ -1795,6 +1800,7 @@ 4B45188E1F75FD1B00926311 /* AcornADF.hpp */, 4B4518901F75FD1B00926311 /* CPCDSK.hpp */, 4B4518921F75FD1B00926311 /* D64.hpp */, + 4BAF2B4D2004580C00480230 /* DMK.hpp */, 4B4518941F75FD1B00926311 /* G64.hpp */, 4B4518961F75FD1B00926311 /* HFE.hpp */, 4B58601D1F806AB200AEE2E3 /* MFMSectorDump.hpp */, @@ -3406,6 +3412,7 @@ 4B055ADF1FAE9B4C0060FFFF /* IRQDelegatePortHandler.cpp in Sources */, 4B055AB51FAE860F0060FFFF /* TapePRG.cpp in Sources */, 4B055AE01FAE9B660060FFFF /* CRT.cpp in Sources */, + 4BAF2B4F2004580C00480230 /* DMK.cpp in Sources */, 4B0E04F21FC9EAA800F43484 /* StaticAnalyser.cpp in Sources */, 4B055AD01FAE9B030060FFFF /* Tape.cpp in Sources */, 4B055A961FAE85BB0060FFFF /* Commodore.cpp in Sources */, @@ -3439,6 +3446,7 @@ 4B2BFC5F1D613E0200BA3AA9 /* TapePRG.cpp in Sources */, 4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */, 4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */, + 4BAF2B4E2004580C00480230 /* DMK.cpp in Sources */, 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */, 4B7136861F78724F008B8ED9 /* Encoder.cpp in Sources */, 4B0E04EA1FC9E5DA00F43484 /* CAS.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index de015839a..a5f0c8a78 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -265,11 +265,6 @@ $(PRODUCT_MODULE_NAME).MachineDocument - LSItemContentTypes - - org.akop.cocoamsx.filetype.cassette - com.clocksignal.cas - CFBundleTypeExtensions cas @@ -281,11 +276,30 @@ MSX Tape Image CFBundleTypeRole Viewer + LSItemContentTypes + + org.akop.cocoamsx.filetype.cassette + com.clocksignal.cas + LSTypeIsPackage 0 NSDocumentClass $(PRODUCT_MODULE_NAME).MachineDocument + + CFBundleTypeExtensions + + dmk + + CFBundleTypeIconFile + floppy35 + CFBundleTypeName + Disk Image + CFBundleTypeRole + Viewer + LSTypeIsPackage + 0 + CFBundleExecutable $(EXECUTABLE_NAME) diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 2d71973a6..ce9214b9d 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -29,6 +29,7 @@ #include "../Storage/Disk/DiskImage/Formats/CPCDSK.hpp" #include "../Storage/Disk/DiskImage/Formats/D64.hpp" #include "../Storage/Disk/DiskImage/Formats/G64.hpp" +#include "../Storage/Disk/DiskImage/Formats/DMK.hpp" #include "../Storage/Disk/DiskImage/Formats/HFE.hpp" #include "../Storage/Disk/DiskImage/Formats/MSXDSK.hpp" #include "../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp" @@ -90,6 +91,7 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType Format("cdt", result.tapes, Tape::TZX, TargetPlatform::AmstradCPC) // CDT Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW Format("d64", result.disks, Disk::DiskImageHolder, TargetPlatform::Commodore) // D64 + Format("dmk", result.disks, Disk::DiskImageHolder, TargetPlatform::MSX) // DMK Format("dsd", result.disks, Disk::DiskImageHolder, TargetPlatform::Acorn) // DSD Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC) Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::MSX) // DSK (MSX) diff --git a/Storage/Disk/DiskImage/Formats/DMK.cpp b/Storage/Disk/DiskImage/Formats/DMK.cpp new file mode 100644 index 000000000..8ea7db34e --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/DMK.cpp @@ -0,0 +1,75 @@ +// +// DMK.cpp +// Clock Signal +// +// Created by Thomas Harte on 08/01/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "DMK.hpp" + +using namespace Storage::Disk; + +DMK::DMK(const char *file_name) : + file_(file_name) { + // Determine whether this DMK represents a read-only disk (whether intentionally, + // or by virtue of placement). + uint8_t read_only_byte = file_.get8(); + if(read_only_byte != 0x00 && read_only_byte != 0xff) throw ErrorNotDMK; + is_read_only_ = (read_only_byte == 0xff) || file_.get_is_known_read_only(); + + // Read track count and size. + head_position_count_ = static_cast(file_.get8()); + track_length_ = static_cast(file_.get16le()); + + // Track length must be at least 0x80, as that's the size of the IDAM + // table before track contents. + if(track_length_ < 0x80) throw ErrorNotDMK; + + // Read the file flags and apply them. + uint8_t flags = file_.get8(); + head_count_ = 2 - ((flags & 0x10) >> 4); + head_position_count_ /= head_count_; + is_purely_single_density_ = !!(flags & 0x40); + + // Skip to the end of the header and check that this is + // "in the emulator's native format". + file_.seek(0xc, SEEK_SET); + uint32_t format = file_.get32le(); + if(format) throw ErrorNotDMK; +} + +int DMK::get_head_position_count() { + return head_position_count_; +} + +int DMK::get_head_count() { + return head_count_; +} + +bool DMK::get_is_read_only() { + return is_read_only_; +} + +long DMK::get_file_offset_for_position(Track::Address address) { + return (address.head*head_count_ + address.position) * track_length_ + 16; +} + +std::shared_ptr<::Storage::Disk::Track> DMK::get_track_at_position(::Storage::Disk::Track::Address address) { + file_.seek(get_file_offset_for_position(address), SEEK_SET); + + uint16_t idam_locations[64]; + std::size_t index = 0; + for(std::size_t c = 0; c < sizeof(idam_locations); ++c) { + idam_locations[index] = file_.get16le(); + if((idam_locations[index] & 0x7fff) >= 128) { + index++; + } + } + + // `index` is now the final (sensical) entry in the IDAM location table. + // TODO: parse track contents. + printf("number of IDAMs: %d", index); + + return nullptr; +} diff --git a/Storage/Disk/DiskImage/Formats/DMK.hpp b/Storage/Disk/DiskImage/Formats/DMK.hpp new file mode 100644 index 000000000..ce16ae7ee --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/DMK.hpp @@ -0,0 +1,57 @@ +// +// DMK.hpp +// Clock Signal +// +// Created by Thomas Harte on 08/01/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef DMK_hpp +#define DMK_hpp + +#include "../DiskImage.hpp" +#include "../../../FileHolder.hpp" + +namespace Storage { +namespace Disk { + +/*! + Provides a @c Disk containing a DMK disk image — mostly a decoded byte stream, but with + a record of IDAM locations. +*/ +class DMK: public DiskImage { + public: + /*! + Construct a @c DMK containing content from the file with name @c file_name. + + @throws ErrorNotDMK if this file doesn't appear to be a DMK. + */ + DMK(const char *file_name); + + enum { + ErrorNotDMK + }; + + // implemented to satisfy @c Disk + int get_head_position_count() override; + int get_head_count() override; + bool get_is_read_only() override; + + std::shared_ptr<::Storage::Disk::Track> get_track_at_position(::Storage::Disk::Track::Address address) override; + + private: + FileHolder file_; + long get_file_offset_for_position(Track::Address address); + + bool is_read_only_; + int head_position_count_; + int head_count_; + + long track_length_; + bool is_purely_single_density_; +}; + +} +} + +#endif /* DMK_hpp */