diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index 2ca13cac9..c929125c0 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -39,6 +39,7 @@ #include "../../Storage/Disk/DiskImage/Formats/NIB.hpp" #include "../../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp" #include "../../Storage/Disk/DiskImage/Formats/SSD.hpp" +#include "../../Storage/Disk/DiskImage/Formats/WOZ.hpp" // Tapes #include "../../Storage/Tape/Formats/CAS.hpp" @@ -129,6 +130,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: Format("tsx", result.tapes, Tape::TZX, TargetPlatform::MSX) // TSX Format("tzx", result.tapes, Tape::TZX, TargetPlatform::ZX8081) // TZX Format("uef", result.tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape) + Format("woz", result.disks, Disk::DiskImageHolder, TargetPlatform::AppleII) // WOZ #undef Format #undef Insert diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 2905d456e..bafaf0bc9 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -222,6 +222,8 @@ 4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */; }; 4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; }; 4B6A4C991F58F09E00E3F787 /* 6502Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6A4C951F58F09E00E3F787 /* 6502Base.cpp */; }; + 4B6ED2F0208E2F8A0047B343 /* WOZ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6ED2EE208E2F8A0047B343 /* WOZ.cpp */; }; + 4B6ED2F1208E2F8A0047B343 /* WOZ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6ED2EE208E2F8A0047B343 /* WOZ.cpp */; }; 4B7136861F78724F008B8ED9 /* Encoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7136841F78724F008B8ED9 /* Encoder.cpp */; }; 4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7136871F78725F008B8ED9 /* Shifter.cpp */; }; 4B71368E1F788112008B8ED9 /* Parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B71368C1F788112008B8ED9 /* Parser.cpp */; }; @@ -889,6 +891,8 @@ 4B6A4C911F58F09E00E3F787 /* 6502AllRAM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6502AllRAM.cpp; sourceTree = ""; }; 4B6A4C921F58F09E00E3F787 /* 6502AllRAM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502AllRAM.hpp; sourceTree = ""; }; 4B6A4C951F58F09E00E3F787 /* 6502Base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6502Base.cpp; sourceTree = ""; }; + 4B6ED2EE208E2F8A0047B343 /* WOZ.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WOZ.cpp; sourceTree = ""; }; + 4B6ED2EF208E2F8A0047B343 /* WOZ.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = WOZ.hpp; sourceTree = ""; }; 4B7041271F92C26900735E45 /* JoystickMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = JoystickMachine.hpp; sourceTree = ""; }; 4B70412A1F92C2A700735E45 /* Joystick.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Joystick.hpp; sourceTree = ""; }; 4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ROMSlotHandler.hpp; path = MSX/ROMSlotHandler.hpp; sourceTree = ""; }; @@ -1879,6 +1883,8 @@ 4B4518981F75FD1B00926311 /* OricMFMDSK.hpp */, 4B45189A1F75FD1B00926311 /* SSD.hpp */, 4BFDD7891F7F2DB4008579B9 /* Utility */, + 4B6ED2EE208E2F8A0047B343 /* WOZ.cpp */, + 4B6ED2EF208E2F8A0047B343 /* WOZ.hpp */, ); path = Formats; sourceTree = ""; @@ -3536,6 +3542,7 @@ 4BB0A65E204500A900FB3688 /* StaticAnalyser.cpp in Sources */, 4B055AC11FAE98DC0060FFFF /* MachineForTarget.cpp in Sources */, 4BBB70A9202014E2002FE009 /* MultiCRTMachine.cpp in Sources */, + 4B6ED2F1208E2F8A0047B343 /* WOZ.cpp in Sources */, 4B055AD81FAE9B180060FFFF /* Video.cpp in Sources */, 4B89452F201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B894531201967B4007DE474 /* StaticAnalyser.cpp in Sources */, @@ -3808,6 +3815,7 @@ 4B894526201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */, 4B8805FB1DCFF807003085B1 /* Oric.cpp in Sources */, + 4B6ED2F0208E2F8A0047B343 /* WOZ.cpp in Sources */, 4BFE7B871FC39BF100160B38 /* StandardOptions.cpp in Sources */, 4B15A9FC208249BB005E6C8D /* StaticAnalyser.cpp in Sources */, 4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 782b17c09..f9049689f 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -68,7 +68,7 @@ CFBundleTypeExtensions nib + woz CFBundleTypeIconFile floppy525 diff --git a/Storage/Disk/DiskImage/Formats/WOZ.cpp b/Storage/Disk/DiskImage/Formats/WOZ.cpp new file mode 100644 index 000000000..1d76b5f6c --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/WOZ.cpp @@ -0,0 +1,100 @@ +// +// WOZ.cpp +// Clock Signal +// +// Created by Thomas Harte on 23/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "WOZ.hpp" + +#include "../../Track/PCMTrack.hpp" + +using namespace Storage::Disk; + +WOZ::WOZ(const std::string &file_name) : + file_(file_name) { + + const char signature[8] = { + 'W', 'O', 'Z', '1', + static_cast(0xff), 0x0a, 0x0d, 0x0a + }; + if(!file_.check_signature(signature, 8)) throw ErrorNotWOZ; + + // TODO: check CRC32, instead of skipping it. + file_.seek(4, SEEK_CUR); + + // Parse all chunks up front. + while(true) { + const uint32_t chunk_id = file_.get32le(); + const uint32_t chunk_size = file_.get32le(); + if(file_.eof()) break; + + long end_of_chunk = file_.tell() + static_cast(chunk_size); + + #define CK(str) (str[0] | (str[1] << 8) | (str[2] << 16) | (str[3] << 24)) + switch(chunk_id) { + case CK("INFO"): { + const uint8_t version = file_.get8(); + if(version != 1) break; + is_3_5_disk_ = file_.get8() == 2; + is_read_only_ = file_.get8() == 1; + /* Ignored: + 1 byte: Synchronized; 1 = Cross track sync was used during imaging. + 1 byte: Cleaned; 1 = MC3470 fake bits have been removed. + 32 bytes: Cretor; a UTF-8 string. + */ + } break; + + case CK("TMAP"): { + file_.read(track_map_, 160); + } break; + + case CK("TRKS"): { + tracks_offset_ = file_.tell(); + } break; + + // TODO: parse META chunks. + + default: + break; + } + #undef CK + + file_.seek(end_of_chunk, SEEK_SET); + } +} + +int WOZ::get_head_position_count() { + // TODO: deal with the elephant in the room of non-integral track coordinates. + return is_3_5_disk_ ? 80 : 160; +} + +int WOZ::get_head_count() { + return is_3_5_disk_ ? 2 : 1; +} + +std::shared_ptr WOZ::get_track_at_position(Track::Address address) { + // Out-of-bounds => no track. + if(address.head >= get_head_count()) return nullptr; + if(address.position >= get_head_position_count()) return nullptr; + + // Calculate table position; if this track is defined to be unformatted, return no track. + const int table_position = address.head * get_head_position_count() + address.position; + if(track_map_[table_position] == 0xff) return nullptr; + + // Seek to the real track. + file_.seek(tracks_offset_ + track_map_[table_position] * 6656, SEEK_SET); + + PCMSegment track_contents; + track_contents.data = file_.read(6646); + track_contents.data.resize(file_.get16le()); + track_contents.number_of_bits = file_.get16le(); + + const uint16_t splice_point = file_.get16le(); + if(splice_point != 0xffff) { + // TODO: expand track from splice_point? + } + + return std::shared_ptr(new PCMTrack(track_contents)); +} diff --git a/Storage/Disk/DiskImage/Formats/WOZ.hpp b/Storage/Disk/DiskImage/Formats/WOZ.hpp new file mode 100644 index 000000000..16abba35c --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/WOZ.hpp @@ -0,0 +1,46 @@ +// +// WOZ.hpp +// Clock Signal +// +// Created by Thomas Harte on 23/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef WOZ_hpp +#define WOZ_hpp + +#include "../DiskImage.hpp" +#include "../../../FileHolder.hpp" + +#include + +namespace Storage { +namespace Disk { + +/*! + Provides a @c DiskImage containing a WOZ — a bit stream representation of a floppy. +*/ +class WOZ: public DiskImage { + public: + WOZ(const std::string &file_name); + + enum { + ErrorNotWOZ + }; + + int get_head_position_count() override; + int get_head_count() override; + std::shared_ptr get_track_at_position(Track::Address address) override; + + private: + Storage::FileHolder file_; + bool is_read_only_ = false; + bool is_3_5_disk_ = false; + uint8_t track_map_[160]; + long tracks_offset_ = 0; +}; + +} +} + +#endif /* WOZ_hpp */