1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-16 22:28:57 +00:00
CLK/Storage/Disk/DiskImage/Formats/WOZ.cpp
2018-05-24 18:45:00 -04:00

122 lines
3.3 KiB
C++

//
// 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"
#include "../../../../NumberTheory/CRC.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<char>(0xff), 0x0a, 0x0d, 0x0a
};
if(!file_.check_signature(signature, 8)) throw Error::InvalidFormat;
// Check the file's CRC32, instead of skipping it.
const uint32_t crc = file_.get32le();
CRC::CRC32 crc_generator;
while(true) {
uint8_t next = file_.get8();
if(file_.eof()) break;
crc_generator.add(next);
}
if(crc != crc_generator.get_value()) {
throw Error::InvalidFormat;
}
// Retreat to the first byte after the CRC.
file_.seek(12, SEEK_SET);
// Parse all chunks up front.
bool has_tmap = false;
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<long>(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);
has_tmap = true;
} break;
case CK("TRKS"): {
tracks_offset_ = file_.tell();
} break;
// TODO: parse META chunks.
default:
break;
}
#undef CK
file_.seek(end_of_chunk, SEEK_SET);
}
if(tracks_offset_ == -1 || !has_tmap) throw Error::InvalidFormat;
}
HeadPosition WOZ::get_maximum_head_position() {
return is_3_5_disk_ ? HeadPosition(80) : HeadPosition(160, 4);
}
int WOZ::get_head_count() {
return is_3_5_disk_ ? 2 : 1;
}
long WOZ::file_offset(Track::Address address) {
// Calculate table position; if this track is defined to be unformatted, return no track.
const int table_position = address.head * (is_3_5_disk_ ? 80 : 160) + (is_3_5_disk_ ? address.position.as_int() : address.position.as_quarter());
if(track_map_[table_position] == 0xff) return NoSuchTrack;
// Seek to the real track.
return tracks_offset_ + track_map_[table_position] * 6656;
}
std::shared_ptr<Track> WOZ::get_track_at_position(Track::Address address) {
long offset = file_offset(address);
if(offset == NoSuchTrack) return nullptr;
// Seek to the real track.
PCMSegment track_contents;
{
std::lock_guard<std::mutex> lock_guard(file_.get_file_access_mutex());
file_.seek(offset, SEEK_SET);
// In WOZ a track is up to 6646 bytes of data, followed by a two-byte record of the
// number of bytes that actually had data in them, then a two-byte count of the number
// of bits that were used. Other information follows but is not intended for emulation.
track_contents.data = file_.read(6646);
track_contents.data.resize(file_.get16le());
track_contents.number_of_bits = file_.get16le();
}
return std::shared_ptr<PCMTrack>(new PCMTrack(track_contents));
}