mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-19 23:29:05 +00:00
Takes a first stab at write support for Macintosh disk images.
Albeit that the Macintosh itself can't yet write.
This commit is contained in:
parent
5a9f3cfc1e
commit
6c2cc206a6
@ -11,7 +11,9 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "../../Track/PCMTrack.hpp"
|
#include "../../Track/PCMTrack.hpp"
|
||||||
|
#include "../../Track/TrackSerialiser.hpp"
|
||||||
#include "../../Encodings/AppleGCR/Encoder.hpp"
|
#include "../../Encodings/AppleGCR/Encoder.hpp"
|
||||||
|
#include "../../Encodings/AppleGCR/SegmentParser.hpp"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
File format specifications as referenced below are largely
|
File format specifications as referenced below are largely
|
||||||
@ -25,7 +27,7 @@ MacintoshIMG::MacintoshIMG(const std::string &file_name) :
|
|||||||
file_(file_name) {
|
file_(file_name) {
|
||||||
|
|
||||||
// Test 1: is this a raw secctor dump? If so it'll start with
|
// Test 1: is this a raw secctor dump? If so it'll start with
|
||||||
// the magic word 0x4C4B6000 (big endian) and be exactly
|
// the magic word 0x4C4B (big endian) and be exactly
|
||||||
// 819,200 bytes long if double sided, or 409,600 bytes if
|
// 819,200 bytes long if double sided, or 409,600 bytes if
|
||||||
// single sided.
|
// single sided.
|
||||||
//
|
//
|
||||||
@ -33,11 +35,12 @@ MacintoshIMG::MacintoshIMG(const std::string &file_name) :
|
|||||||
// DiskCopy 4.2 format, so there's no ambiguity here.
|
// DiskCopy 4.2 format, so there's no ambiguity here.
|
||||||
const auto name_length = file_.get8();
|
const auto name_length = file_.get8();
|
||||||
if(name_length == 0x4c) {
|
if(name_length == 0x4c) {
|
||||||
|
is_diskCopy_file_ = false;
|
||||||
if(file_.stats().st_size != 819200 && file_.stats().st_size != 409600)
|
if(file_.stats().st_size != 819200 && file_.stats().st_size != 409600)
|
||||||
throw Error::InvalidFormat;
|
throw Error::InvalidFormat;
|
||||||
|
|
||||||
uint32_t magic_word = file_.get24be();
|
uint32_t magic_word = file_.get8();
|
||||||
if(magic_word != 0x4b6000)
|
if(magic_word != 0x4b)
|
||||||
throw Error::InvalidFormat;
|
throw Error::InvalidFormat;
|
||||||
|
|
||||||
file_.seek(0, SEEK_SET);
|
file_.seek(0, SEEK_SET);
|
||||||
@ -47,7 +50,7 @@ MacintoshIMG::MacintoshIMG(const std::string &file_name) :
|
|||||||
data_ = file_.read(819200);
|
data_ = file_.read(819200);
|
||||||
} else {
|
} else {
|
||||||
encoding_ = Encoding::GCR400;
|
encoding_ = Encoding::GCR400;
|
||||||
format_ = 0x2;
|
format_ = 0x02;
|
||||||
data_ = file_.read(409600);
|
data_ = file_.read(409600);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -59,6 +62,7 @@ MacintoshIMG::MacintoshIMG(const std::string &file_name) :
|
|||||||
// be one too high.
|
// be one too high.
|
||||||
//
|
//
|
||||||
// Validate the length, then skip the rest of the string.
|
// Validate the length, then skip the rest of the string.
|
||||||
|
is_diskCopy_file_ = true;
|
||||||
if(name_length > 64)
|
if(name_length > 64)
|
||||||
throw Error::InvalidFormat;
|
throw Error::InvalidFormat;
|
||||||
|
|
||||||
@ -132,7 +136,7 @@ int MacintoshIMG::get_head_count() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool MacintoshIMG::get_is_read_only() {
|
bool MacintoshIMG::get_is_read_only() {
|
||||||
return true;
|
return file_.get_is_known_read_only();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<::Storage::Disk::Track> MacintoshIMG::get_track_at_position(::Storage::Disk::Track::Address address) {
|
std::shared_ptr<::Storage::Disk::Track> MacintoshIMG::get_track_at_position(::Storage::Disk::Track::Address address) {
|
||||||
@ -153,6 +157,7 @@ std::shared_ptr<::Storage::Disk::Track> MacintoshIMG::get_track_at_position(::St
|
|||||||
Bit 5 indicates double sided or not.
|
Bit 5 indicates double sided or not.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
std::lock_guard<decltype(buffer_mutex_)> buffer_lock(buffer_mutex_);
|
||||||
if(encoding_ == Encoding::GCR400 || encoding_ == Encoding::GCR800) {
|
if(encoding_ == Encoding::GCR400 || encoding_ == Encoding::GCR800) {
|
||||||
// Perform a GCR encoding.
|
// Perform a GCR encoding.
|
||||||
const auto included_sectors = Storage::Encodings::AppleGCR::Macintosh::sectors_in_track(address.position.as_int());
|
const auto included_sectors = Storage::Encodings::AppleGCR::Macintosh::sectors_in_track(address.position.as_int());
|
||||||
@ -207,3 +212,73 @@ std::shared_ptr<::Storage::Disk::Track> MacintoshIMG::get_track_at_position(::St
|
|||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacintoshIMG::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) {
|
||||||
|
std::map<Track::Address, std::vector<uint8_t>> tracks_by_address;
|
||||||
|
for(const auto &pair: tracks) {
|
||||||
|
// Determine a data rate for the track.
|
||||||
|
const auto included_sectors = Storage::Encodings::AppleGCR::Macintosh::sectors_in_track(pair.first.position.as_int());
|
||||||
|
|
||||||
|
// Rule of thumb here: there are about 6250 bits per sector.
|
||||||
|
const int data_rate = included_sectors.length * 6250;
|
||||||
|
|
||||||
|
// Decode the track.
|
||||||
|
auto sector_map = Storage::Encodings::AppleGCR::sectors_from_segment(
|
||||||
|
Storage::Disk::track_serialisation(*pair.second, Storage::Time(1, data_rate)));
|
||||||
|
|
||||||
|
// Rearrange sectors into ascending order.
|
||||||
|
std::vector<uint8_t> track_contents(static_cast<size_t>(524 * included_sectors.length));
|
||||||
|
for(const auto §or_pair: sector_map) {
|
||||||
|
const size_t target_address = sector_pair.second.address.sector * 524;
|
||||||
|
if(target_address >= track_contents.size()) continue;
|
||||||
|
memcpy(&track_contents[target_address*524], sector_pair.second.data.data(), 524);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store for later.
|
||||||
|
tracks_by_address[pair.first] = std::move(track_contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the buffer mutex and update the in-memory buffer.
|
||||||
|
{
|
||||||
|
std::lock_guard<decltype(buffer_mutex_)> buffer_lock(buffer_mutex_);
|
||||||
|
for(const auto &pair: tracks_by_address) {
|
||||||
|
const auto included_sectors = Storage::Encodings::AppleGCR::Macintosh::sectors_in_track(pair.first.position.as_int());
|
||||||
|
size_t start_sector = size_t(included_sectors.start * get_head_count() + included_sectors.length * pair.first.head);
|
||||||
|
|
||||||
|
for(int c = 0; c < included_sectors.length; ++c) {
|
||||||
|
memcpy(&data_[start_sector * 512], &pair.second[start_sector*524 + 12], 512);
|
||||||
|
|
||||||
|
// TODO: the below strongly implies I should keep tags in memory even if I don't write
|
||||||
|
// them out to the file?
|
||||||
|
if(tags_.size()) {
|
||||||
|
memcpy(&data_[start_sector * 12], pair.second.data(), 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
++start_sector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the file lock and write out the new tracks.
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock_guard(file_.get_file_access_mutex());
|
||||||
|
|
||||||
|
if(!is_diskCopy_file_) {
|
||||||
|
// Just dump out the new sectors. Grossly lazy, possibly worth improving.
|
||||||
|
file_.seek(0, SEEK_SET);
|
||||||
|
file_.write(data_);
|
||||||
|
} else {
|
||||||
|
// Write out the sectors, and possibly the tags, and update checksums.
|
||||||
|
file_.seek(0x54, SEEK_SET);
|
||||||
|
file_.write(data_);
|
||||||
|
file_.write(tags_);
|
||||||
|
|
||||||
|
const auto data_checksum = checksum(data_);
|
||||||
|
const auto tag_checksum = checksum(tags_, 12);
|
||||||
|
|
||||||
|
file_.seek(0x48, SEEK_SET);
|
||||||
|
file_.put_be(data_checksum);
|
||||||
|
file_.put_be(tag_checksum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -37,6 +37,7 @@ class MacintoshIMG: public DiskImage {
|
|||||||
bool get_is_read_only() override;
|
bool get_is_read_only() override;
|
||||||
|
|
||||||
std::shared_ptr<::Storage::Disk::Track> get_track_at_position(::Storage::Disk::Track::Address address) override;
|
std::shared_ptr<::Storage::Disk::Track> get_track_at_position(::Storage::Disk::Track::Address address) override;
|
||||||
|
void set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Storage::FileHolder file_;
|
Storage::FileHolder file_;
|
||||||
@ -51,6 +52,8 @@ class MacintoshIMG: public DiskImage {
|
|||||||
|
|
||||||
std::vector<uint8_t> data_;
|
std::vector<uint8_t> data_;
|
||||||
std::vector<uint8_t> tags_;
|
std::vector<uint8_t> tags_;
|
||||||
|
bool is_diskCopy_file_ = false;
|
||||||
|
std::mutex buffer_mutex_;
|
||||||
|
|
||||||
uint32_t checksum(const std::vector<uint8_t> &, size_t bytes_to_skip = 0);
|
uint32_t checksum(const std::vector<uint8_t> &, size_t bytes_to_skip = 0);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user