diff options
Diffstat (limited to 'src/lib/formats')
155 files changed, 5109 insertions, 2583 deletions
diff --git a/src/lib/formats/86f_dsk.cpp b/src/lib/formats/86f_dsk.cpp new file mode 100644 index 00000000000..654e847d8db --- /dev/null +++ b/src/lib/formats/86f_dsk.cpp @@ -0,0 +1,272 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert, Carl + +#include "86f_dsk.h" + +#include "ioprocs.h" + +#include <cstring> +#include <tuple> + + +namespace { + +#define _86F_FORMAT_HEADER "86BF" + +#pragma pack(1) + +struct _86FIMG +{ + uint8_t headername[4]; + uint8_t minor_version; + uint8_t major_version; + uint16_t flags; + uint32_t firsttrackoffs; +}; + +#pragma pack() + +enum +{ + SURFACE_DESC = 1, + TYPE_MASK = 6, + TYPE_DD = 0, + TYPE_HD = 2, + TYPE_ED = 4, + TYPE_ED2000 = 6, + TWO_SIDES = 8, + WRITE_PROTECT = 0x10, + RPM_MASK = 0x60, + RPM_0 = 0, + RPM_1 = 0x20, + RPM_15 = 0x40, + RPM_2 = 0x60, + EXTRA_BC = 0x80, + ZONED_RPM = 0x100, + ZONE_PREA2_1 = 0, + ZONE_PREA2_2 = 0x200, + ZONE_A2 = 0x400, + ZONE_C64 = 0x600, + ENDIAN_BIG = 0x800, + RPM_FAST = 0x1000, + TOTAL_BC = 0x1000 +}; + +} // anonymous namespace + + +_86f_format::_86f_format() : floppy_image_format_t() +{ +} + +const char *_86f_format::name() const noexcept +{ + return "86f"; +} + +const char *_86f_format::description() const noexcept +{ + return "86f floppy image"; +} + +const char *_86f_format::extensions() const noexcept +{ + return "86f"; +} + +bool _86f_format::supports_save() const noexcept +{ + return false; +} + +int _86f_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const +{ + uint8_t header[4]; + auto const [err, actual] = read_at(io, 0, &header, sizeof(header)); + if (err) { + return 0; + } + if (!memcmp(header, _86F_FORMAT_HEADER, 4)) { + return FIFID_SIGN; + } + return 0; +} + +void _86f_format::generate_track_from_bitstream_with_weak(int track, int head, const uint8_t *trackbuf, const uint8_t *weak, int index_cell, int track_size, floppy_image &image) const +{ + int j = 0; + std::vector<uint32_t> &dest = image.get_buffer(track, head); + dest.clear(); + + for(int i=index_cell; i != track_size; i++, j++) { + int databit = trackbuf[i >> 3] & (0x80 >> (i & 7)); + int weakbit = weak ? weak[i >> 3] & (0x80 >> (i & 7)) : 0; + if(weakbit && databit) + dest.push_back(floppy_image::MG_D | (j*2+1)); + else if(weakbit && !databit) + dest.push_back(floppy_image::MG_N | (j*2+1)); + else if(databit) + dest.push_back(floppy_image::MG_F | (j*2+1)); + } + for(int i=0; i != index_cell; i++, j++) { + int databit = trackbuf[i >> 3] & (0x80 >> (i & 7)); + int weakbit = weak ? weak[i >> 3] & (0x80 >> (i & 7)) : 0; + if(weakbit && databit) + dest.push_back(floppy_image::MG_D | (j*2+1)); + else if(weakbit && !databit) + dest.push_back(floppy_image::MG_N | (j*2+1)); + else if(databit) + dest.push_back(floppy_image::MG_F | (j*2+1)); + } + + normalize_times(dest, track_size*2); + image.set_write_splice_position(track, head, 0, 0); +} + +bool _86f_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const +{ + std::error_condition err; + size_t actual; + _86FIMG header; + + // read header + std::tie(err, actual) = read_at(io, 0, &header, sizeof(header)); + if(err || (actual != sizeof(header))) + return false; + + int drivecyl, driveheads; + image.get_maximal_geometry(drivecyl, driveheads); + bool skip_odd = drivecyl < 50; + int imagesides = header.flags & TWO_SIDES ? 2 : 1; + int sides = (imagesides == 2) && (driveheads == 2) ? 2 : 1; + + std::vector<uint32_t> tracklist; + int tracklistsize = header.firsttrackoffs - 8; + tracklist.resize(tracklistsize / 4); + std::tie(err, actual) = read_at(io, 8, &tracklist[0], tracklistsize); + if(err || (actual != tracklistsize)) + return false; + + uint32_t tracklen = 0; + if((header.flags & (TOTAL_BC | EXTRA_BC | RPM_MASK)) != (TOTAL_BC | EXTRA_BC)) { + switch(header.flags & (RPM_MASK | RPM_FAST | TYPE_MASK)) { + case TYPE_DD | RPM_2: + case TYPE_HD | RPM_2: + tracklen = 12750; + break; + case TYPE_DD | RPM_15: + case TYPE_HD | RPM_15: + tracklen = 12687; + break; + case TYPE_DD | RPM_1: + case TYPE_HD | RPM_1: + tracklen = 12625; + break; + case TYPE_DD | RPM_0: + case TYPE_HD | RPM_0: + tracklen = 12500; + break; + case TYPE_DD | RPM_1 | RPM_FAST: + case TYPE_HD | RPM_1 | RPM_FAST: + tracklen = 12376; + break; + case TYPE_DD | RPM_15 | RPM_FAST: + case TYPE_HD | RPM_15 | RPM_FAST: + tracklen = 12315; + break; + case TYPE_DD | RPM_2 | RPM_FAST: + case TYPE_HD | RPM_2 | RPM_FAST: + tracklen = 12254; + break; + + case TYPE_ED | RPM_2: + tracklen = 25500; + break; + case TYPE_ED | RPM_15: + tracklen = 25375; + break; + case TYPE_ED | RPM_1: + tracklen = 25250; + break; + case TYPE_ED | RPM_0: + tracklen = 25000; + break; + case TYPE_ED | RPM_1 | RPM_FAST: + tracklen = 25752; + break; + case TYPE_ED | RPM_15 | RPM_FAST: + tracklen = 24630; + break; + case TYPE_ED | RPM_2 | RPM_FAST: + tracklen = 24509; + break; + } + } + uint32_t trackoff; + int trackinfolen = header.flags & EXTRA_BC ? 10 : 6; + if(skip_odd) + drivecyl *= 2; + std::vector<uint8_t> trackbuf; + std::vector<uint8_t> weakbuf; + int track; + for(track=0; (track < (tracklistsize / 4)) && (track < drivecyl); track++) { + for(int side=0; side < sides; side++) { + trackoff = tracklist[(track * imagesides) + side]; + if(!trackoff) break; + if(!skip_odd || track%2 == 0) { + char trackinfo[10]; + std::tie(err, actual) = read_at(io, trackoff, &trackinfo, trackinfolen); // FIXME: check for errors and premature EOF + if(err || (actual != trackinfolen)) + return false; + uint32_t bitcells = tracklen << 4; + uint32_t index_cell; + if(header.flags & EXTRA_BC) + { + uint32_t extra = *(uint32_t *)(trackinfo + 2); + index_cell = *(uint32_t *)(trackinfo + 6); + if((header.flags & TOTAL_BC) && !tracklen) + bitcells = extra; + else + bitcells += (int32_t)extra; + } + else + index_cell = *(uint32_t *)(trackinfo + 2); + uint32_t fulltracklen = (bitcells >> 3) + (bitcells & 7 ? 1 : 0); + uint8_t *weak = nullptr; + trackbuf.resize(fulltracklen); + std::tie(err, actual) = read_at(io, trackoff + trackinfolen, &trackbuf[0], fulltracklen); + if(err || (actual != fulltracklen)) + return false; + if(header.flags & SURFACE_DESC) + { + weakbuf.resize(fulltracklen); + std::tie(err, actual) = read_at(io, trackoff + trackinfolen + fulltracklen, &weakbuf[0], fulltracklen); + if(err || (actual != fulltracklen)) + return false; + weak = &weakbuf[0]; + } + if(skip_odd) { + generate_track_from_bitstream_with_weak(track/2, side, &trackbuf[0], weak, index_cell, bitcells, image); + } + else { + generate_track_from_bitstream_with_weak(track, side, &trackbuf[0], weak, index_cell, bitcells, image); + } + } + } + if(!trackoff) break; + } + + if(imagesides == 2) + image.set_variant(track > 50 ? floppy_image::DSHD : floppy_image::DSDD); + else + image.set_variant(floppy_image::SSDD); + return true; +} + +/* +bool _86f_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const +{ + return true; +} +*/ +const _86f_format FLOPPY_86F_FORMAT; diff --git a/src/lib/formats/86f_dsk.h b/src/lib/formats/86f_dsk.h new file mode 100644 index 00000000000..0afbe4d65ee --- /dev/null +++ b/src/lib/formats/86f_dsk.h @@ -0,0 +1,37 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert, Carl +/********************************************************************* + + formats/86f_dsk.h + + 86f disk images + +*********************************************************************/ +#ifndef MAME_FORMATS_86F_DSK_H +#define MAME_FORMATS_86F_DSK_H + +#pragma once + +#include "flopimg.h" + +class _86f_format : public floppy_image_format_t +{ +public: + _86f_format(); + + virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override; + virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override; + //virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override; + + virtual const char *name() const noexcept override; + virtual const char *description() const noexcept override; + virtual const char *extensions() const noexcept override; + virtual bool supports_save() const noexcept override; + +private: + void generate_track_from_bitstream_with_weak(int track, int head, const uint8_t *trackbuf, const uint8_t *weak, int index_cell, int track_size, floppy_image &image) const; +}; + +extern const _86f_format FLOPPY_86F_FORMAT; + +#endif // MAME_FORMATS_86F_DSK_H diff --git a/src/lib/formats/a26_cas.cpp b/src/lib/formats/a26_cas.cpp index 46522bd50de..c609f922348 100644 --- a/src/lib/formats/a26_cas.cpp +++ b/src/lib/formats/a26_cas.cpp @@ -120,7 +120,7 @@ static int a26_cas_do_work( int16_t **buffer, const uint8_t *bytes ) { return size; } -static int a26_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) { +static int a26_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { int16_t *p = buffer; return a26_cas_do_work( &p, (const uint8_t *)bytes ); diff --git a/src/lib/formats/abc800i_dsk.cpp b/src/lib/formats/abc800i_dsk.cpp index 1961dd9a9de..9a5a798c886 100644 --- a/src/lib/formats/abc800i_dsk.cpp +++ b/src/lib/formats/abc800i_dsk.cpp @@ -89,11 +89,10 @@ const abc800i_format FLOPPY_ABC800I_FORMAT; int abc800i_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t h[1]; - size_t actual; - io.read_at(0x810, h, 1, actual); + auto const [err, actual] = read_at(io, 0x810, h, 1); // start of directory - if (h[0] == 0x03) + if (!err && (actual == 1) && (h[0] == 0x03)) return FIFID_SIGN; return 0; diff --git a/src/lib/formats/ace_tap.cpp b/src/lib/formats/ace_tap.cpp index a2fa9fe8b80..9613450e59e 100644 --- a/src/lib/formats/ace_tap.cpp +++ b/src/lib/formats/ace_tap.cpp @@ -135,7 +135,7 @@ static int ace_handle_tap(int16_t *buffer, const uint8_t *casdata) /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int ace_tap_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int ace_tap_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { return ace_handle_tap( buffer, bytes ); } diff --git a/src/lib/formats/acorn_dsk.cpp b/src/lib/formats/acorn_dsk.cpp index 42a3aa61bdf..682b6424556 100644 --- a/src/lib/formats/acorn_dsk.cpp +++ b/src/lib/formats/acorn_dsk.cpp @@ -16,6 +16,7 @@ #include "multibyte.h" #include <cstring> +#include <tuple> acorn_ssd_format::acorn_ssd_format() : wd177x_format(formats) @@ -45,25 +46,24 @@ int acorn_ssd_format::find_size(util::random_read &io, uint32_t form_factor, con if (io.length(size)) return -1; - for (int i=0; formats[i].form_factor; i++) + for (int i = 0; formats[i].form_factor; i++) { - size_t actual; const format &f = formats[i]; if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor) continue; // test for Torch CPN - test pattern at sector &0018 - io.read_at(0x32800, cat, 8, actual); + read_at(io, 0x32800, cat, 8); // FIXME: check for errors and premature EOF if (memcmp(cat, "\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd", 8) == 0 && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count) return i; // test for HADFS - test pattern at sector 70 - io.read_at(0x04610, cat, 8, actual); + read_at(io, 0x04610, cat, 8); // FIXME: check for errors and premature EOF if (memcmp(cat, "\x00\x28\x43\x29\x4a\x47\x48\x00", 8) == 0 && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count) return i; // test for Kenda SD - offset &0962 = 0 SD/1 DD, offset &0963 = disk size blocks / 4 (block size = 1K, ie. 0x400 bytes), reserved tracks = 3, ie. 0x1e00 bytes, soft stagger = 2 sectors, ie. 0x200 bytes - io.read_at(0x0960, cat, 8, actual); + read_at(io, 0x0960, cat, 8); // FIXME: check for errors and premature EOF if (cat[2] == 0 && ((uint64_t)cat[3] * 4 * 0x400 + 0x2000) == size && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count) { // valid blocks for single sided @@ -75,7 +75,7 @@ int acorn_ssd_format::find_size(util::random_read &io, uint32_t form_factor, con } // read sector count from side 0 catalogue - io.read_at(0x100, cat, 8, actual); + read_at(io, 0x100, cat, 8); // FIXME: check for errors and premature EOF sectors0 = get_u16be(&cat[6]) & 0x3ff; LOG_FORMATS("ssd: sector count 0: %d %s\n", sectors0, sectors0 % 10 != 0 ? "invalid" : ""); @@ -84,11 +84,11 @@ int acorn_ssd_format::find_size(util::random_read &io, uint32_t form_factor, con if (f.head_count == 2) { // read sector count from side 2 catalogue - io.read_at((uint64_t)compute_track_size(f) * f.track_count + 0x100, cat, 8, actual); // sequential + read_at(io, (uint64_t)compute_track_size(f) * f.track_count + 0x100, cat, 8); // sequential sectors2 = get_u16be(&cat[6]) & 0x3ff; // exception case for Acorn CP/M System Disc 1 - io.read_at(0x367ec, cat, 8, actual); + read_at(io, 0x367ec, cat, 8); // FIXME: check for errors and premature EOF if (memcmp(cat, "/M ", 4) == 0) sectors2 = get_u16be(&cat[6]) & 0x3ff; @@ -99,7 +99,7 @@ int acorn_ssd_format::find_size(util::random_read &io, uint32_t form_factor, con sectors2 = sectors0; } - if (sectors0 > 0 && sectors2 > 0 && size <= (sectors0 + sectors2) * 256) + if (sectors0 > 0 && sectors2 > 0 && sectors0 % 10 == 0 && sectors2 % 10 == 0 && size <= (sectors0 + sectors2) * 256) return i; } } @@ -192,39 +192,38 @@ int acorn_dsd_format::find_size(util::random_read &io, uint32_t form_factor, con for (int i = 0; formats[i].form_factor; i++) { - size_t actual; const format &f = formats[i]; if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor) continue; // test for Torch CPN - test pattern at sector &0018 - io.read_at(0x1200, cat, 8, actual); + read_at(io, 0x1200, cat, 8); // FIXME: check for errors and premature EOF if (memcmp(cat, "\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd", 8) == 0 && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count) return i; // test for HADFS - test pattern at sector 70 - io.read_at(0x08c10, cat, 8, actual); + read_at(io, 0x08c10, cat, 8); // FIXME: check for errors and premature EOF if (memcmp(cat, "\x00\x28\x43\x29\x4a\x47\x48\x00", 8) == 0 && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count) return i; // test for FLEX - from System Information Record - io.read_at(0x0226, cat, 2, actual); + read_at(io, 0x0226, cat, 2); // FIXME: check for errors and premature EOF if ((memcmp(cat, "\x4f\x14", 2) == 0 || memcmp(cat, "\x4f\x0a", 2) == 0) && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count) return i; // read sector count from side 0 catalogue - io.read_at(0x100, cat, 8, actual); + read_at(io, 0x100, cat, 8); // FIXME: check for errors and premature EOF sectors0 = get_u16be(&cat[6]) & 0x3ff; LOG_FORMATS("dsd: sector count 0: %d %s\n", sectors0, sectors0 % 10 != 0 ? "invalid" : ""); if ((size <= (uint64_t)compute_track_size(f) * f.track_count * f.head_count) && (sectors0 <= f.track_count * f.sector_count)) { // read sector count from side 2 catalogue - io.read_at(0xb00, cat, 8, actual); // interleaved + read_at(io, 0xb00, cat, 8); // interleaved sectors2 = get_u16be(&cat[6]) & 0x3ff; // exception case for Acorn CP/M System Disc 1 - io.read_at(0x97ec, cat, 8, actual); + read_at(io, 0x97ec, cat, 8); // FIXME: check for errors and premature EOF if (memcmp(cat, "/M ", 4) == 0) sectors2 = get_u16be(&cat[6]) & 0x3ff; @@ -295,12 +294,11 @@ const char *opus_ddos_format::extensions() const noexcept int opus_ddos_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { - size_t actual; uint8_t cat[8]; uint32_t sectors0, sectors2; // read sector count from side 0 catalogue - io.read_at(0x1000, cat, 8, actual); + read_at(io, 0x1000, cat, 8); // FIXME: check for errors and premature EOF sectors0 = get_u16be(&cat[1]); LOG_FORMATS("ddos: sector count 0: %d %s\n", sectors0, sectors0 % 18 != 0 ? "invalid" : ""); @@ -319,7 +317,7 @@ int opus_ddos_format::find_size(util::random_read &io, uint32_t form_factor, con if (f.head_count == 2) { // read sector count from side 2 catalogue - io.read_at((uint64_t)compute_track_size(f) * f.track_count + 0x1000, cat, 8, actual); // sequential + read_at(io, (uint64_t)compute_track_size(f) * f.track_count + 0x1000, cat, 8); // sequential sectors2 = get_u16be(&cat[1]); LOG_FORMATS("ddos: sector count 2: %d %s\n", sectors2, sectors2 % 18 != 0 ? "invalid" : ""); } @@ -393,25 +391,30 @@ const char *acorn_adfs_old_format::extensions() const noexcept int acorn_adfs_old_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { + std::error_condition err; size_t actual; uint8_t map[3]; uint32_t sectors; uint8_t oldmap[4]; // read sector count from free space map - io.read_at(0xfc, map, 3, actual); + std::tie(err, actual) = read_at(io, 0xfc, map, 3); + if (err || (3 != actual)) + return -1; sectors = get_u24le(map); LOG_FORMATS("adfs_o: sector count %d %s\n", sectors, sectors % 16 != 0 ? "invalid" : ""); // read map identifier - io.read_at(0x201, oldmap, 4, actual); + std::tie(err, actual) = read_at(io, 0x201, oldmap, 4); + if (err || (4 != actual)) + return -1; LOG_FORMATS("adfs_o: map identifier %s %s\n", oldmap, memcmp(oldmap, "Hugo", 4) != 0 ? "invalid" : ""); uint64_t size; if (io.length(size)) return -1; - for (int i=0; formats[i].form_factor; i++) + for (int i = 0; formats[i].form_factor; i++) { const format &f = formats[i]; if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor) @@ -429,7 +432,7 @@ int acorn_adfs_old_format::identify(util::random_read &io, uint32_t form_factor, { int type = find_size(io, form_factor, variants); - if(type != -1) + if (type != -1) return FIFID_STRUCT | FIFID_SIZE; return 0; @@ -491,14 +494,19 @@ const char *acorn_adfs_new_format::extensions() const noexcept int acorn_adfs_new_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { + std::error_condition err; size_t actual; uint8_t dform[4]; uint8_t eform[4]; // read map identifiers for D and E formats - io.read_at(0x401, dform, 4, actual); + std::tie(err, actual) = read_at(io, 0x401, dform, 4); + if (err || (4 != actual)) + return -1; LOG_FORMATS("adfs_n: map identifier (D format) %s %s\n", dform, (memcmp(dform, "Hugo", 4) != 0 && memcmp(dform, "Nick", 4) != 0) ? "invalid" : ""); - io.read_at(0x801, eform, 4, actual); + std::tie(err, actual) = read_at(io, 0x801, eform, 4); + if (err || (4 != actual)) + return -1; LOG_FORMATS("adfs_n: map identifier (E format) %s %s\n", eform, memcmp(eform, "Nick", 4) != 0 ? "invalid" : ""); uint64_t size; @@ -585,12 +593,14 @@ int acorn_dos_format::find_size(util::random_read &io, uint32_t form_factor, con if (size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count) { // read media type ID from FAT - Acorn DOS = 0xfd - size_t actual; uint8_t type; - io.read_at(0, &type, 1, actual); - LOG_FORMATS("dos: 800k media type id %02X %s\n", type, type != 0xfd ? "invalid" : ""); - if (type == 0xfd) - return i; + auto const [err, actual] = read_at(io, 0, &type, 1); + if (!err && (1 == actual)) + { + LOG_FORMATS("dos: 800k media type id %02X %s\n", type, type != 0xfd ? "invalid" : ""); + if (type == 0xfd) + return i; + } } } return -1; @@ -647,14 +657,14 @@ bool opus_ddcpm_format::supports_save() const noexcept int opus_ddcpm_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { - size_t actual; uint8_t h[8]; - - io.read_at(0, h, 8, actual); + auto const [err, actual] = read_at(io, 0, h, 8); + if (err || (8 != actual)) + return 0; uint64_t size; if (io.length(size)) - return -1; + return 0; if (size == 819200 && memcmp(h, "Slogger ", 8) == 0) return FIFID_SIGN | FIFID_SIZE; @@ -686,8 +696,7 @@ bool opus_ddcpm_format::load(util::random_read &io, uint32_t form_factor, const desc_pc_sector sects[10]; uint8_t sectdata[10*512]; - size_t actual; - io.read_at(head * 80 * spt * 512 + track * spt * 512, sectdata, spt * 512, actual); + /*auto const [err, actual] =*/ read_at(io, head * 80 * spt * 512 + track * spt * 512, sectdata, spt * 512); // FIXME: check for errors and premature EOF for (int i = 0; i < spt; i++) { @@ -742,7 +751,7 @@ int cumana_dfs_format::find_size(util::random_read &io, uint32_t form_factor, co if (io.length(size)) return -1; - for (int i=0; formats[i].form_factor; i++) + for (int i = 0; formats[i].form_factor; i++) { const format &f = formats[i]; if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor) @@ -750,9 +759,10 @@ int cumana_dfs_format::find_size(util::random_read &io, uint32_t form_factor, co if (size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count) { - size_t actual; uint8_t info; - io.read_at(14, &info, 1, actual); + auto const [err, actual] = read_at(io, 14, &info, 1); + if (err || (actual != 1)) + return -1; if (f.head_count == (util::BIT(info, 6) ? 2 : 1) && f.track_count == (util::BIT(info, 7) ? 80 : 40)) return i; } diff --git a/src/lib/formats/aim_dsk.cpp b/src/lib/formats/aim_dsk.cpp index 88399204f37..c33cab8fb12 100644 --- a/src/lib/formats/aim_dsk.cpp +++ b/src/lib/formats/aim_dsk.cpp @@ -72,8 +72,8 @@ bool aim_format::load(util::random_read &io, uint32_t form_factor, const std::ve bool header = false; // Read track - size_t actual; - io.read_at((heads * track + head) * track_size, &track_data[0], track_size, actual); + /*auto const [err, actual] =*/ read_at(io, (heads * track + head) * track_size, &track_data[0], track_size); + // FIXME: check for error and premature EOF // Find first sector header or index mark for (int offset = 0; offset < track_size; offset += 2) diff --git a/src/lib/formats/all.cpp b/src/lib/formats/all.cpp index 02f5b636cf5..bab86919cb7 100644 --- a/src/lib/formats/all.cpp +++ b/src/lib/formats/all.cpp @@ -320,6 +320,10 @@ #include "guab_dsk.h" #endif +#ifdef HAS_FORMATS_H17D_DSK +#include "h17disk.h" +#endif + #ifdef HAS_FORMATS_H8_CAS #include "h8_cas.h" #endif @@ -576,6 +580,10 @@ #include "rx50_dsk.h" #endif +#ifdef HAS_FORMATS_SAP_DSK +#include "sap_dsk.h" +#endif + #ifdef HAS_FORMATS_SC3000_BIT #include "sc3000_bit.h" #endif @@ -785,6 +793,7 @@ void mame_formats_full_list(mame_formats_enumerator &en) en.add(fs::PRODOS); #endif #ifdef HAS_FORMATS_AP2_DSK + en.add(FLOPPY_A213S_FORMAT); // ap2_dsk.h en.add(FLOPPY_A216S_DOS_FORMAT); // ap2_dsk.h en.add(FLOPPY_A216S_PRODOS_FORMAT); // ap2_dsk.h en.add(FLOPPY_RWTS18_FORMAT); // ap2_dsk.h @@ -1339,6 +1348,9 @@ void mame_formats_full_list(mame_formats_enumerator &en) en.add(FLOPPY_THOMSON_525_FORMAT); // thom_dsk.h en.add(FLOPPY_THOMSON_35_FORMAT); // thom_dsk.h #endif +#ifdef HAS_FORMATS_SAP_DSK + en.add(FLOPPY_SAP_FORMAT); +#endif en.category("Texas Instruments"); #ifdef HAS_FORMATS_TI99_DSK @@ -1510,7 +1522,7 @@ void mame_formats_full_list(mame_formats_enumerator &en) en.add(vtech1_cassette_formats); // vt_cas.h en.add(vtech2_cassette_formats); // vt_cas.h #endif -#ifdef HAS_FORMATS_VT_DSJ +#ifdef HAS_FORMATS_VT_DSK en.add(FLOPPY_VTECH_BIN_FORMAT); // vt_dsk.h en.add(FLOPPY_VTECH_DSK_FORMAT); // vt_dsk.h #endif @@ -1522,4 +1534,9 @@ void mame_formats_full_list(mame_formats_enumerator &en) #ifdef HAS_FORMATS_X07_CAS en.add(x07_cassette_formats); // x07_cas.h #endif + + en.category("Heath"); +#ifdef HAS_FORMATS_H17D_DSK + en.add(FLOPPY_H17D_FORMAT); // h17disk.h +#endif } diff --git a/src/lib/formats/ami_dsk.cpp b/src/lib/formats/ami_dsk.cpp index 6ad9145c7d8..d4c567050c8 100644 --- a/src/lib/formats/ami_dsk.cpp +++ b/src/lib/formats/ami_dsk.cpp @@ -81,8 +81,9 @@ bool adf_format::load(util::random_read &io, uint32_t form_factor, const std::ve image.set_variant(floppy_image::DSDD); for (int track=0; track < tracks; track++) { for (int side=0; side < 2; side++) { - size_t actual; - io.read_at((track*2 + side)*512*11, sectdata, 512*11, actual); + auto const [err, actual] = read_at(io, (track*2 + side)*512*11, sectdata, 512*11); + if (err || (512*11 != actual)) + return false; generate_track(amiga_11, track, side, sectors, 11, 100000, image); } } @@ -90,8 +91,9 @@ bool adf_format::load(util::random_read &io, uint32_t form_factor, const std::ve image.set_variant(floppy_image::DSHD); for (int track=0; track < tracks; track++) { for (int side=0; side < 2; side++) { - size_t actual; - io.read_at((track*2 + side)*512*22, sectdata, 512*22, actual); + auto const [err, actual] = read_at(io, (track*2 + side)*512*22, sectdata, 512*22); + if (err || (512*22 != actual)) + return false; generate_track(amiga_22, track, side, sectors, 22, 200000, image); } } @@ -151,8 +153,8 @@ bool adf_format::save(util::random_read_write &io, const std::vector<uint32_t> & *dest++ = val; } - size_t actual; - io.write_at((track*2 + side) * data_track_size, sectdata, data_track_size, actual); + // FIXME: check for errors + /*auto const [err, actual] =*/ write_at(io, (track*2 + side) * data_track_size, sectdata, data_track_size); } } } diff --git a/src/lib/formats/ap2_dsk.cpp b/src/lib/formats/ap2_dsk.cpp index 557800dcec2..1880ad7bd23 100644 --- a/src/lib/formats/ap2_dsk.cpp +++ b/src/lib/formats/ap2_dsk.cpp @@ -18,6 +18,128 @@ #include <cstdlib> #include <cstring> +static const uint8_t translate5[] = { + 0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba, + 0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb, + 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef, + 0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, 0xfe, 0xff, +}; + +a2_13sect_format::a2_13sect_format() : floppy_image_format_t() +{ +} + +int a2_13sect_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const +{ + uint64_t size; + if (io.length(size)) + return 0; + + if (size != APPLE2_STD_TRACK_COUNT * 13 * APPLE2_SECTOR_SIZE) + return 0; + + return FIFID_SIZE; +} + +bool a2_13sect_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const +{ + uint64_t size; + if (io.length(size)) + return false; + + image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD); + + int tracks = size / 13 / APPLE2_SECTOR_SIZE; + + for(int track = 0; track < tracks; track++) { + std::vector<uint32_t> track_data; + uint8_t sector_data[APPLE2_SECTOR_SIZE * 13]; + + auto const [err, actual] = read_at( + io, track * sizeof sector_data, sector_data, sizeof sector_data); + if (err || actual != sizeof sector_data) + return false; + + for(int i=0; i<13; i++) { + int sector = (i * 10) % 13; + + // write inter-sector padding + for(int j=0; j<40; j++) + raw_w(track_data, 9, 0x1fe); + raw_w(track_data, 8, 0xff); + + // write sector address + raw_w(track_data, 24, 0xd5aab5); + raw_w(track_data, 16, gcr4_encode(0xfe)); + raw_w(track_data, 16, gcr4_encode(track)); + raw_w(track_data, 16, gcr4_encode(sector)); + raw_w(track_data, 16, gcr4_encode(0xfe ^ track ^ sector)); + raw_w(track_data, 24, 0xdeaaeb); + + // write intra-sector padding + for(int j=0; j<11; j++) + raw_w(track_data, 9, 0x1fe); + + // write sector data + raw_w(track_data, 24, 0xd5aaad); + + uint8_t pval = 0x00; + auto write_data_byte = [&track_data, &pval](uint8_t nval) { + raw_w(track_data, 8, translate5[nval ^ pval]); + pval = nval; + }; + + const uint8_t *sdata = sector_data + APPLE2_SECTOR_SIZE * sector; + + // write 154 bytes encoding bits 2-0 + write_data_byte(sdata[255] & 7); + for (int k=2; k>-1; k--) + for (int j=0; j<51; j++) + write_data_byte( + (sdata[j*5+k] & 7) << 2 + | ((sdata[j*5+3] >> (2-k)) & 1) << 1 + | ((sdata[j*5+4] >> (2-k)) & 1)); + + // write 256 bytes encoding bits 7-3 + for (int k=0; k<5; k++) + for (int j=50; j>-1; j--) + write_data_byte(sdata[j*5+k] >> 3); + write_data_byte(sdata[255] >> 3); + + raw_w(track_data, 8, translate5[pval]); + raw_w(track_data, 24, 0xdeaaeb); + raw_w(track_data, 8, 0xff); + } + + generate_track_from_levels(track, 0, track_data, 0, image); + } + + return true; +} + + +const char *a2_13sect_format::name() const noexcept +{ + return "a2_13sect"; +} + +const char *a2_13sect_format::description() const noexcept +{ + return "Apple II 13-sector d13 image"; +} + +const char *a2_13sect_format::extensions() const noexcept +{ + return "d13"; +} + +bool a2_13sect_format::supports_save() const noexcept +{ + return false; +} + +const a2_13sect_format FLOPPY_A213S_FORMAT; + static const uint8_t translate6[0x40] = { 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, @@ -96,16 +218,17 @@ int a2_16sect_format::identify(util::random_read &io, uint32_t form_factor, cons if (io.length(size)) return 0; - //uint32_t expected_size = 35 * 16 * 256; - uint32_t expected_size = APPLE2_TRACK_COUNT * 16 * 256; - // check standard size plus some oddball sizes in our softlist - if ((size != expected_size) && (size != 35 * 16 * 256) && (size != 143403) && (size != 143363) && (size != 143358)) + if ( + size != APPLE2_TRACK_COUNT * 16 * APPLE2_SECTOR_SIZE + && size != APPLE2_STD_TRACK_COUNT * 16 * APPLE2_SECTOR_SIZE + && size != 143403 && size != 143363 && size != 143358 && size != 143195 + ) { return 0; } - uint8_t sector_data[256*2]; + uint8_t sector_data[APPLE2_SECTOR_SIZE*2]; static const unsigned char pascal_block1[4] = { 0x08, 0xa5, 0x0f, 0x29 }; static const unsigned char pascal2_block1[4] = { 0xff, 0xa2, 0x00, 0x8e }; static const unsigned char dos33_block1[4] = { 0xa2, 0x02, 0x8e, 0x52 }; @@ -114,8 +237,9 @@ int a2_16sect_format::identify(util::random_read &io, uint32_t form_factor, cons static const unsigned char cpm22_block1[8] = { 0xa2, 0x55, 0xa9, 0x00, 0x9d, 0x00, 0x0d, 0xca }; static const unsigned char subnod_block1[8] = { 0x63, 0xaa, 0xf0, 0x76, 0x8d, 0x63, 0xaa, 0x8e }; - size_t actual; - io.read_at(0, sector_data, 256*2, actual); + auto const [err, actual] = read_at(io, 0, sector_data, sizeof sector_data); + if (err || actual != sizeof sector_data) + return 0; bool prodos_order = false; // check ProDOS boot block @@ -181,17 +305,20 @@ bool a2_16sect_format::load(util::random_read &io, uint32_t form_factor, const s image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD); - int tracks = (size == (40 * 16 * 256)) ? 40 : 35; + int tracks = (size == (APPLE2_TRACK_COUNT * 16 * APPLE2_SECTOR_SIZE)) ? APPLE2_TRACK_COUNT : APPLE2_STD_TRACK_COUNT; int fpos = 0; for(int track=0; track < tracks; track++) { std::vector<uint32_t> track_data; - uint8_t sector_data[256*16]; + uint8_t sector_data[APPLE2_SECTOR_SIZE*16]; - size_t actual; - io.read_at(fpos, sector_data, 256*16, actual); + auto const [err, actual] = read_at(io, fpos, sector_data, sizeof sector_data); + // Some supported images have oddball sizes, where the last track is incomplete. + // Skip the `actual` check to avoid rejecting them. + if (err /* || actual != sizeof sector_data */) + return false; - fpos += 256*16; + fpos += APPLE2_SECTOR_SIZE*16; for(int i=0; i<49; i++) raw_w(track_data, 10, 0x3fc); for(int i=0; i<16; i++) { @@ -206,7 +333,7 @@ bool a2_16sect_format::load(util::random_read &io, uint32_t form_factor, const s sector = dos_skewing[i]; } - const uint8_t *sdata = sector_data + 256 * sector; + const uint8_t *sdata = sector_data + APPLE2_SECTOR_SIZE * sector; for(int j=0; j<20; j++) raw_w(track_data, 10, 0x3fc); raw_w(track_data, 8, 0xff); @@ -269,212 +396,215 @@ uint8_t a2_16sect_format::gb(const std::vector<bool> &buf, int &pos, int &wrap) return v; } -//#define VERBOSE_SAVE - bool a2_16sect_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const { - int g_tracks, g_heads; - int visualgrid[16][APPLE2_TRACK_COUNT]; // visualizer grid, cleared/initialized below - -// lenient addr check: if unset, only accept an addr mark if the checksum was good -// if set, accept an addr mark if the track and sector values are both sane -#undef LENIENT_ADDR_CHECK -// if set, use the old, not as robust logic for choosing which copy of a decoded sector to write -// to the resulting image if the sector has a bad checksum and/or postamble -#undef USE_OLD_BEST_SECTOR_PRIORITY -// nothing found -#define NOTFOUND 0 -// address mark was found -#define ADDRFOUND 1 -// address checksum is good -#define ADDRGOOD 2 -// data mark was found (requires addrfound and sane values) -#define DATAFOUND 4 -// data checksum is good -#define DATAGOOD 8 -// data postamble is good -#define DATAPOST 16 - for (auto & elem : visualgrid) { - for (int j = 0; j < APPLE2_TRACK_COUNT; j++) { - elem[j] = 0; - } + int g_tracks, g_heads; + int visualgrid[16][APPLE2_TRACK_COUNT]; // visualizer grid, cleared/initialized below + + constexpr bool VERBOSE_SAVE = false; + + // if false, only accept an addr mark if the checksum was good + // if true, accept an addr mark if the track and sector values are both sane + constexpr bool LENIENT_ADDR_CHECK = false; + + // if true, use the old, not as robust logic for choosing which copy of a decoded sector to write + // to the resulting image if the sector has a bad checksum and/or postamble + constexpr bool USE_OLD_BEST_SECTOR_PRIORITY = false; + + // nothing found + constexpr int NOTFOUND = 0; + // address mark was found + constexpr int ADDRFOUND = 1; + // address checksum is good + constexpr int ADDRGOOD = 2; + // data mark was found (requires addrfound and sane values) + constexpr int DATAFOUND = 4; + // data checksum is good + constexpr int DATAGOOD = 8; + // data postamble is good + constexpr int DATAPOST = 16; + + for (auto & elem : visualgrid) { + for (int j = 0; j < APPLE2_TRACK_COUNT; j++) { + elem[j] = NOTFOUND; } - image.get_actual_geometry(g_tracks, g_heads); + } + image.get_actual_geometry(g_tracks, g_heads); - int head = 0; + int head = 0; - int pos_data = 0; + int pos_data = 0; - for(int track=0; track < g_tracks; track++) { - uint8_t sectdata[(256)*16]; - memset(sectdata, 0, sizeof(sectdata)); - int nsect = 16; - #ifdef VERBOSE_SAVE - fprintf(stderr,"DEBUG: a2_16sect_format::save() about to generate bitstream from track %d...", track); - #endif - auto buf = generate_bitstream_from_track(track, head, 3915, image); - #ifdef VERBOSE_SAVE - fprintf(stderr,"done.\n"); - #endif - int pos = 0; - int wrap = 0; - int hb = 0; - int dosver = 0; // apple dos version; 0 = >=3.3, 1 = <3.3 - for(;;) { - uint8_t v = gb(buf, pos, wrap); - if(v == 0xff) { + for(int track=0; track < g_tracks; track++) { + uint8_t sectdata[APPLE2_SECTOR_SIZE*16]; + memset(sectdata, 0, sizeof(sectdata)); + int nsect = 16; + if(VERBOSE_SAVE) { + fprintf(stderr,"DEBUG: a2_16sect_format::save() about to generate bitstream from track %d...", track); + } + auto buf = generate_bitstream_from_track(track, head, 3915, image); + if(VERBOSE_SAVE) { + fprintf(stderr,"done.\n"); + } + int pos = 0; + int wrap = 0; + int hb = 0; + int dosver = 0; // apple dos version; 0 = >=3.3, 1 = <3.3 + for(;;) { + uint8_t v = gb(buf, pos, wrap); + if(v == 0xff) { + hb = 1; + } + else if(hb == 1 && v == 0xd5){ + hb = 2; + } + else if(hb == 2 && v == 0xaa) { + hb = 3; + } + else if(hb == 3 && ((v == 0x96) || (v == 0xab))) { // 0x96 = dos 3.3/16sec, 0xab = dos 3.21 and below/13sec + hb = 4; + if (v == 0xab) dosver = 1; + } + else + hb = 0; + + if(hb == 4) { + uint8_t h[11]; + for(auto & elem : h) + elem = gb(buf, pos, wrap); + //uint8_t v2 = gcr6bw_tb[h[2]]; + uint8_t vl = gcr4_decode(h[0],h[1]); + uint8_t tr = gcr4_decode(h[2],h[3]); + uint8_t se = gcr4_decode(h[4],h[5]); + uint8_t chk = gcr4_decode(h[6],h[7]); + if(VERBOSE_SAVE) { + uint32_t post = get_u24be(&h[8]); + printf("Address Mark:\tVolume %d, Track %d, Sector %2d, Checksum %02X: %s, Postamble %03X: %s\n", + vl, tr, se, chk, (chk ^ vl ^ tr ^ se)==0?"OK":"BAD", post, (post&0xFFFF00)==0xDEAA00?"OK":"BAD"); + } + // sanity check + if (tr == track && se < nsect) { + visualgrid[se][track] |= ADDRFOUND; + visualgrid[se][track] |= ((chk ^ vl ^ tr ^ se)==0)?ADDRGOOD:0; + + if (visualgrid[se][track] & (LENIENT_ADDR_CHECK ? ADDRFOUND : ADDRGOOD)) { + int opos = pos; + int owrap = wrap; + hb = 0; + for(int i=0; i<20 && hb != 4; i++) { + v = gb(buf, pos, wrap); + if(v == 0xff) hb = 1; - } - else if(hb == 1 && v == 0xd5){ + else if(hb == 1 && v == 0xd5) hb = 2; - } - else if(hb == 2 && v == 0xaa) { + else if(hb == 2 && v == 0xaa) hb = 3; - } - else if(hb == 3 && ((v == 0x96) || (v == 0xab))) { // 0x96 = dos 3.3/16sec, 0xab = dos 3.21 and below/13sec + else if(hb == 3 && v == 0xad) hb = 4; - if (v == 0xab) dosver = 1; - } else hb = 0; + } + if((hb == 4)&&(dosver == 0)) { + visualgrid[se][track] |= DATAFOUND; + uint8_t *dest; + uint8_t data[0x157]; + uint32_t dpost = 0; + uint8_t c = 0; + + if (m_prodos_order) + { + dest = sectdata+APPLE2_SECTOR_SIZE*prodos_skewing[se]; + } + else + { + dest = sectdata+APPLE2_SECTOR_SIZE*dos_skewing[se]; + } - if(hb == 4) { - uint8_t h[11]; - for(auto & elem : h) - elem = gb(buf, pos, wrap); - //uint8_t v2 = gcr6bw_tb[h[2]]; - uint8_t vl = gcr4_decode(h[0],h[1]); - uint8_t tr = gcr4_decode(h[2],h[3]); - uint8_t se = gcr4_decode(h[4],h[5]); - uint8_t chk = gcr4_decode(h[6],h[7]); - #ifdef VERBOSE_SAVE - uint32_t post = get_u24be(&h[8]); - printf("Address Mark:\tVolume %d, Track %d, Sector %2d, Checksum %02X: %s, Postamble %03X: %s\n", vl, tr, se, chk, (chk ^ vl ^ tr ^ se)==0?"OK":"BAD", post, (post&0xFFFF00)==0xDEAA00?"OK":"BAD"); - #endif - // sanity check - if (tr == track && se < nsect) { - visualgrid[se][track] |= ADDRFOUND; - visualgrid[se][track] |= ((chk ^ vl ^ tr ^ se)==0)?ADDRGOOD:0; -#ifdef LENIENT_ADDR_CHECK - if ((visualgrid[se][track] & ADDRFOUND) == ADDRFOUND) { -#else - if ((visualgrid[se][track] & ADDRGOOD) == ADDRGOOD) { -#endif - int opos = pos; - int owrap = wrap; - hb = 0; - for(int i=0; i<20 && hb != 4; i++) { - v = gb(buf, pos, wrap); - if(v == 0xff) - hb = 1; - else if(hb == 1 && v == 0xd5) - hb = 2; - else if(hb == 2 && v == 0xaa) - hb = 3; - else if(hb == 3 && v == 0xad) - hb = 4; - else - hb = 0; - } - if((hb == 4)&&(dosver == 0)) { - visualgrid[se][track] |= DATAFOUND; - uint8_t *dest; - uint8_t data[0x157]; - uint32_t dpost = 0; - uint8_t c = 0; - - if (m_prodos_order) - { - dest = sectdata+(256)*prodos_skewing[se]; - } - else - { - dest = sectdata+(256)*dos_skewing[se]; - } - - // first read in sector and decode to 6bit form - for(int i=0; i<0x156; i++) { - data[i] = gcr6bw_tb[gb(buf, pos, wrap)] ^ c; - c = data[i]; - // printf("%02x ", c); - // if (((i&0xf)+1)==0x10) printf("\n"); - } - // read the checksum byte - data[0x156] = gcr6bw_tb[gb(buf,pos,wrap)]; - // now read the postamble bytes - for(int i=0; i<3; i++) { - dpost <<= 8; - dpost |= gb(buf, pos, wrap); - } - // next combine in the upper 2 bits of each byte - uint8_t bit_swap[4] = { 0, 2, 1, 3 }; - for(int i=0; i<0x56; i++) - data[i+0x056] = data[i+0x056]<<2 | bit_swap[data[i]&3]; - for(int i=0; i<0x56; i++) - data[i+0x0ac] = data[i+0x0ac]<<2 | bit_swap[(data[i]>>2)&3]; - for(int i=0; i<0x54; i++) - data[i+0x102] = data[i+0x102]<<2 | bit_swap[(data[i]>>4)&3]; - // now decode it into 256 bytes - // but only write it if the bitfield of the track shows datagood is NOT set. - // if it is set we don't want to overwrite a guaranteed good read with a bad one - // if past read had a bad checksum or bad postamble... -#ifndef USE_OLD_BEST_SECTOR_PRIORITY - if (((visualgrid[se][track]&DATAGOOD)==0)||((visualgrid[se][track]&DATAPOST)==0)) { - // if the current read is good, and postamble is good, write it in, no matter what. - // if the current read is good and the current postamble is bad, write it in unless the postamble was good before - // if the current read is bad and the current postamble is good and the previous read had neither good, write it in - // if the current read isn't good and neither is the postamble but nothing better - // has been written before, write it anyway. - if ( ((data[0x156] == c) && (dpost&0xFFFF00)==0xDEAA00) || - (((data[0x156] == c) && (dpost&0xFFFF00)!=0xDEAA00) && ((visualgrid[se][track]&DATAPOST)==0)) || - (((data[0x156] != c) && (dpost&0xFFFF00)==0xDEAA00) && (((visualgrid[se][track]&DATAGOOD)==0)&&(visualgrid[se][track]&DATAPOST)==0)) || - (((data[0x156] != c) && (dpost&0xFFFF00)!=0xDEAA00) && (((visualgrid[se][track]&DATAGOOD)==0)&&(visualgrid[se][track]&DATAPOST)==0)) - ) { - for(int i=0x56; i<0x156; i++) { - uint8_t dv = data[i]; - *dest++ = dv; - } - } - } -#else - if ((visualgrid[se][track]&DATAGOOD)==0) { - for(int i=0x56; i<0x156; i++) { - uint8_t dv = data[i]; - *dest++ = dv; - } - } -#endif - // do some checking - #ifdef VERBOSE_SAVE - if ((data[0x156] != c) || (dpost&0xFFFF00)!=0xDEAA00) - fprintf(stderr,"Data Mark:\tChecksum xpctd %d found %d: %s, Postamble %03X: %s\n", data[0x156], c, (data[0x156]==c)?"OK":"BAD", dpost, (dpost&0xFFFF00)==0xDEAA00?"OK":"BAD"); - #endif - if (data[0x156] == c) visualgrid[se][track] |= DATAGOOD; - if ((dpost&0xFFFF00)==0xDEAA00) visualgrid[se][track] |= DATAPOST; - } else if ((hb == 4)&&(dosver == 1)) { - fprintf(stderr,"ERROR: We don't handle dos sectors below 3.3 yet!\n"); - } else { - pos = opos; - wrap = owrap; + // first read in sector and decode to 6bit form + for(int i=0; i<0x156; i++) { + data[i] = gcr6bw_tb[gb(buf, pos, wrap)] ^ c; + c = data[i]; + // printf("%02x ", c); + // if (((i&0xf)+1)==0x10) printf("\n"); + } + // read the checksum byte + data[0x156] = gcr6bw_tb[gb(buf,pos,wrap)]; + // now read the postamble bytes + for(int i=0; i<3; i++) { + dpost <<= 8; + dpost |= gb(buf, pos, wrap); + } + // next combine in the upper 2 bits of each byte + uint8_t bit_swap[4] = { 0, 2, 1, 3 }; + for(int i=0; i<0x56; i++) + data[i+0x056] = data[i+0x056]<<2 | bit_swap[data[i]&3]; + for(int i=0; i<0x56; i++) + data[i+0x0ac] = data[i+0x0ac]<<2 | bit_swap[(data[i]>>2)&3]; + for(int i=0; i<0x54; i++) + data[i+0x102] = data[i+0x102]<<2 | bit_swap[(data[i]>>4)&3]; + // now decode it into 256 bytes + // but only write it if the bitfield of the track shows datagood is NOT set. + // if it is set we don't want to overwrite a guaranteed good read with a bad one + // if past read had a bad checksum or bad postamble... + if(USE_OLD_BEST_SECTOR_PRIORITY) { + if ((visualgrid[se][track]&DATAGOOD)==0) { + for(int i=0x56; i<0x156; i++) { + uint8_t dv = data[i]; + *dest++ = dv; + } + } + } else { + if (((visualgrid[se][track]&DATAGOOD)==0)||((visualgrid[se][track]&DATAPOST)==0)) { + // if the current read is good, and postamble is good, write it in, no matter what. + // if the current read is good and the current postamble is bad, write it in unless the postamble was good before + // if the current read is bad and the current postamble is good and the previous read had neither good, write it in + // if the current read isn't good and neither is the postamble but nothing better + // has been written before, write it anyway. + if ( ((data[0x156] == c) && (dpost&0xFFFF00)==0xDEAA00) || + (((data[0x156] == c) && (dpost&0xFFFF00)!=0xDEAA00) && ((visualgrid[se][track]&DATAPOST)==0)) || + (((data[0x156] != c) && (dpost&0xFFFF00)==0xDEAA00) && (((visualgrid[se][track]&DATAGOOD)==0)&&(visualgrid[se][track]&DATAPOST)==0)) || + (((data[0x156] != c) && (dpost&0xFFFF00)!=0xDEAA00) && (((visualgrid[se][track]&DATAGOOD)==0)&&(visualgrid[se][track]&DATAPOST)==0)) + ) { + for(int i=0x56; i<0x156; i++) { + uint8_t dv = data[i]; + *dest++ = dv; } } } - hb = 0; + } + // do some checking + if(VERBOSE_SAVE) { + if ((data[0x156] != c) || (dpost&0xFFFF00)!=0xDEAA00) + fprintf(stderr,"Data Mark:\tChecksum xpctd %d found %d: %s, Postamble %03X: %s\n", + data[0x156], c, (data[0x156]==c)?"OK":"BAD", dpost, (dpost&0xFFFF00)==0xDEAA00?"OK":"BAD"); + } + if (data[0x156] == c) visualgrid[se][track] |= DATAGOOD; + if ((dpost&0xFFFF00)==0xDEAA00) visualgrid[se][track] |= DATAPOST; + } else if ((hb == 4)&&(dosver == 1)) { + fprintf(stderr,"ERROR: We don't handle dos sectors below 3.3 yet!\n"); + } else { + pos = opos; + wrap = owrap; } - if(wrap) - break; + } } - for(int i=0; i<nsect; i++) { - //if(nsect>0) printf("t%d,", track); - uint8_t const *const data = sectdata + (256)*i; - size_t actual; - io.write_at(pos_data, data, 256, actual); - pos_data += 256; - } - //printf("\n"); + hb = 0; + } + if(wrap) + break; } - // display a little table of which sectors decoded ok - #ifdef VERBOSE_SAVE + for(int i=0; i<nsect; i++) { + //if(nsect>0) printf("t%d,", track); + uint8_t const *const data = sectdata + APPLE2_SECTOR_SIZE*i; + auto const [err, actual] = write_at(io, pos_data, data, APPLE2_SECTOR_SIZE); + if (err || actual != APPLE2_SECTOR_SIZE) + return false; + pos_data += APPLE2_SECTOR_SIZE; + } + //printf("\n"); + } + // display a little table of which sectors decoded ok + if(VERBOSE_SAVE) { int total_good = 0; for (int j = 0; j < APPLE2_TRACK_COUNT; j++) { printf("T%2d: ",j); @@ -491,9 +621,9 @@ bool a2_16sect_format::save(util::random_read_write &io, const std::vector<uint3 printf("\n"); } printf("Total Good Sectors: %d\n", total_good); - #endif + } - return true; + return true; } const a2_16sect_dos_format FLOPPY_A216S_DOS_FORMAT; @@ -796,8 +926,7 @@ bool a2_rwts18_format::save(util::random_read_write &io, const std::vector<uint3 for(int i=0; i<nsect; i++) { //if(nsect>0) printf("t%d,", track); uint8_t const *const data = sectdata + (256)*i; - size_t actual; - io.write_at(pos_data, data, 256, actual); + /*auto const [err, actual] =*/ write_at(io, pos_data, data, 256); // FIXME: check for errors pos_data += 256; } @@ -978,8 +1107,7 @@ bool a2_rwts18_format::save(util::random_read_write &io, const std::vector<uint3 for(int i=0; i<nsect; i++) { //if(nsect>0) printf("t%d,", track); uint8_t const *const data = sectdata + (256)*i; - size_t actual; - io.write_at(pos_data, data, 256, actual); + /*auto const [err, actual] =*/ write_at(io, pos_data, data, 256); // FIXME: check for errors pos_data += 256; } //printf("\n"); @@ -1048,14 +1176,12 @@ bool a2_edd_format::load(util::random_read &io, uint32_t form_factor, const std: { uint8_t nibble[16384], stream[16384]; int npos[16384]; + static const size_t img_size = 2'244'608; - std::unique_ptr<uint8_t []> img(new (std::nothrow) uint8_t[2244608]); - if (!img) + auto [err, img, actual] = read_at(io, 0, img_size); + if(err || actual != img_size) return false; - size_t actual; - io.read_at(0, img.get(), 2244608, actual); - for(int i=0; i<137; i++) { uint8_t const *const trk = &img[16384*i]; int pos = 0; @@ -1286,16 +1412,18 @@ bool a2_nib_format::load(util::random_read &io, uint32_t form_factor, const std: if (size != expected_size_35t && size != expected_size_40t) return false; - const auto nr_tracks = size == expected_size_35t? 35 : 40; + const auto nr_tracks = size / nibbles_per_track; std::vector<uint8_t> nibbles(nibbles_per_track); for (unsigned track = 0; track < nr_tracks; ++track) { - size_t actual; - io.read_at(track * nibbles_per_track, &nibbles[0], nibbles_per_track, actual); + auto const [err, actual] = read_at(io, track * nibbles_per_track, &nibbles[0], nibbles_per_track); + if (err || actual != nibbles_per_track) + return false; + auto levels = generate_levels_from_nibbles(nibbles); - generate_track_from_levels(track, 0, - levels, - 0, image); + if (!levels.empty()) { + generate_track_from_levels(track, 0, levels, 0, image); + } } image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD); diff --git a/src/lib/formats/ap2_dsk.h b/src/lib/formats/ap2_dsk.h index cbeea73f2d3..33117ecbd84 100644 --- a/src/lib/formats/ap2_dsk.h +++ b/src/lib/formats/ap2_dsk.h @@ -21,14 +21,30 @@ ***************************************************************************/ -#define APPLE2_NIBBLE_SIZE 416 -#define APPLE2_SMALL_NIBBLE_SIZE 374 -#define APPLE2_STD_TRACK_COUNT 35 -#define APPLE2_TRACK_COUNT 40 -#define APPLE2_SECTOR_COUNT 16 -#define APPLE2_SECTOR_SIZE 256 +constexpr int APPLE2_STD_TRACK_COUNT = 35; +constexpr int APPLE2_TRACK_COUNT = 40; +constexpr int APPLE2_SECTOR_SIZE = 256; /**************************************************************************/ +class a2_13sect_format : public floppy_image_format_t +{ +public: + a2_13sect_format(); + + virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override; + virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override; + + virtual const char *name() const noexcept override; + virtual const char *description() const noexcept override; + virtual const char *extensions() const noexcept override; + virtual bool supports_save() const noexcept override; + +private: + static uint8_t gb(const std::vector<bool> &buf, int &pos, int &wrap); +}; + +extern const a2_13sect_format FLOPPY_A213S_FORMAT; + class a2_16sect_format : public floppy_image_format_t { public: @@ -126,8 +142,8 @@ public: private: static constexpr size_t nibbles_per_track = 0x1a00; static constexpr size_t min_sync_bytes = 4; - static constexpr auto expected_size_35t = 35 * nibbles_per_track; - static constexpr auto expected_size_40t = 40 * nibbles_per_track; + static constexpr auto expected_size_35t = APPLE2_STD_TRACK_COUNT * nibbles_per_track; + static constexpr auto expected_size_40t = APPLE2_TRACK_COUNT * nibbles_per_track; static std::vector<uint32_t> generate_levels_from_nibbles(const std::vector<uint8_t>& nibbles); }; diff --git a/src/lib/formats/ap_dsk35.cpp b/src/lib/formats/ap_dsk35.cpp index b4a82e5ff56..4096215c35f 100644 --- a/src/lib/formats/ap_dsk35.cpp +++ b/src/lib/formats/ap_dsk35.cpp @@ -142,27 +142,26 @@ int dc42_format::identify(util::random_read &io, uint32_t form_factor, const std return 0; uint8_t h[0x54]; - size_t actual; - io.read_at(0, h, 0x54, actual); - uint32_t dsize = get_u32be(&h[0x40]); - uint32_t tsize = get_u32be(&h[0x44]); + auto const [err, actual] = read_at(io, 0, h, 0x54); + if (err || (0x54 != actual)) + return 0; - uint8_t encoding = h[0x50]; - uint8_t format = h[0x51]; + uint32_t const dsize = get_u32be(&h[0x40]); + uint32_t const tsize = get_u32be(&h[0x44]); + + uint8_t const encoding = h[0x50]; + uint8_t const format = h[0x51]; // if it's a 1.44MB DC42 image, reject it so the generic PC MFM handler picks it up if ((encoding == 0x03) && (format == 0x02)) - { return 0; - } return (size == 0x54+tsize+dsize && h[0] < 64 && h[0x52] == 1 && h[0x53] == 0) ? FIFID_STRUCT : 0; } bool dc42_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { - size_t actual; uint8_t h[0x54]; - io.read_at(0, h, 0x54, actual); + read_at(io, 0, h, 0x54); // FIXME: check for errors and premature EOF int dsize = get_u32be(&h[0x40]); int tsize = get_u32be(&h[0x44]); @@ -218,14 +217,14 @@ bool dc42_format::load(util::random_read &io, uint32_t form_factor, const std::v sectors[si].sector = i; sectors[si].info = format; if(tsize) { - io.read_at(pos_tag, data, 12, actual); + read_at(io, pos_tag, data, 12); // FIXME: check for errors and premature EOF sectors[si].tag = data; pos_tag += 12; } else { sectors[si].tag = nullptr; } sectors[si].data = data+12; - io.read_at(pos_data, data+12, 512, actual); + read_at(io, pos_data, data+12, 512); // FIXME: check for errors and premature EOF pos_data += 512; si = (si + 2) % ns; if(si == 0) @@ -279,9 +278,8 @@ bool dc42_format::save(util::random_read_write &io, const std::vector<uint32_t> for(unsigned int i=0; i < sectors.size(); i++) { auto &sdata = sectors[i]; sdata.resize(512+12); - size_t actual; - io.write_at(pos_tag, &sdata[0], 12, actual); - io.write_at(pos_data, &sdata[12], 512, actual); + write_at(io, pos_tag, &sdata[0], 12); // FIXME: check for errors + write_at(io, pos_data, &sdata[12], 512); // FIXME: check for errors pos_tag += 12; pos_data += 512; if(track || head || i) @@ -294,8 +292,7 @@ bool dc42_format::save(util::random_read_write &io, const std::vector<uint32_t> put_u32be(&h[0x48], dchk); put_u32be(&h[0x4c], tchk); - size_t actual; - io.write_at(0, h, 0x54, actual); + write_at(io, 0, h, 0x54); // FIXME: check for errors return true; } @@ -340,14 +337,13 @@ int apple_gcr_format::identify(util::random_read &io, uint32_t form_factor, cons bool apple_gcr_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { - size_t actual; desc_gcr_sector sectors[12]; uint8_t sdata[512*12]; int pos_data = 0; uint8_t header[64]; - io.read_at(0, header, 64, actual); + read_at(io, 0, header, 64); // FIXME: check for errors and premature EOF uint64_t size; if(io.length(size)) @@ -361,7 +357,7 @@ bool apple_gcr_format::load(util::random_read &io, uint32_t form_factor, const s for(int track=0; track < 80; track++) { for(int head=0; head < head_count; head++) { int ns = 12 - (track/16); - io.read_at(pos_data, sdata, 512*ns, actual); + read_at(io, pos_data, sdata, 512*ns); // FIXME: check for errors and premature EOF pos_data += 512*ns; int si = 0; @@ -398,8 +394,7 @@ bool apple_gcr_format::save(util::random_read_write &io, const std::vector<uint3 for(unsigned int i=0; i < sectors.size(); i++) { auto &sdata = sectors[i]; sdata.resize(512+12); - size_t actual; - io.write_at(pos_data, &sdata[12], 512, actual); + /*auto const [err, actual] =*/ write_at(io, pos_data, &sdata[12], 512); // FIXME: check for errors pos_data += 512; } } @@ -438,15 +433,19 @@ bool apple_2mg_format::supports_save() const noexcept int apple_2mg_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t signature[4]; - size_t actual; - io.read_at(0, signature, 4, actual); - if (!strncmp(reinterpret_cast<char *>(signature), "2IMG", 4)) + auto const [err, actual] = read_at(io, 0, signature, 4); + if (err || (4 != actual)) + { + return 0; + } + + if (!memcmp(signature, "2IMG", 4)) { return FIFID_SIGN; } // Bernie ][ The Rescue wrote 2MGs with the signature byte-flipped, other fields are valid - if (!strncmp(reinterpret_cast<char *>(signature), "GMI2", 4)) + if (!memcmp(signature, "GMI2", 4)) { return FIFID_SIGN; } @@ -456,10 +455,9 @@ int apple_2mg_format::identify(util::random_read &io, uint32_t form_factor, cons bool apple_2mg_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { - size_t actual; desc_gcr_sector sectors[12]; uint8_t sdata[512*12], header[64]; - io.read_at(0, header, 64, actual); + read_at(io, 0, header, 64); // FIXME: check for errors and premature EOF uint32_t blocks = get_u32le(&header[0x14]); uint32_t pos_data = get_u32le(&header[0x18]); @@ -471,7 +469,7 @@ bool apple_2mg_format::load(util::random_read &io, uint32_t form_factor, const s for(int track=0; track < 80; track++) { for(int head=0; head < 2; head++) { int ns = 12 - (track/16); - io.read_at(pos_data, sdata, 512*ns, actual); + read_at(io, pos_data, sdata, 512*ns); // FIXME: check for errors and premature EOF pos_data += 512*ns; int si = 0; @@ -494,8 +492,6 @@ bool apple_2mg_format::load(util::random_read &io, uint32_t form_factor, const s bool apple_2mg_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const { - size_t actual; - uint8_t header[0x40]; int pos_data = 0x40; @@ -516,7 +512,7 @@ bool apple_2mg_format::save(util::random_read_write &io, const std::vector<uint3 header[0x18] = 0x40; // bytes of disk data header[0x1c] = 0x00; header[0x1d] = 0x80; header[0x1e] = 0x0c; // 0xC8000 (819200) - io.write_at(0, header, 0x40, actual); + write_at(io, 0, header, 0x40); // FIXME: check for errors for(int track=0; track < 80; track++) { for(int head=0; head < 2; head++) { @@ -524,7 +520,7 @@ bool apple_2mg_format::save(util::random_read_write &io, const std::vector<uint3 for(unsigned int i=0; i < sectors.size(); i++) { auto &sdata = sectors[i]; sdata.resize(512+12); - io.write_at(pos_data, &sdata[12], 512, actual); + write_at(io, pos_data, &sdata[12], 512); // FIXME: check for errors pos_data += 512; } } diff --git a/src/lib/formats/apd_dsk.cpp b/src/lib/formats/apd_dsk.cpp index c1319e43cf4..55d722bdf38 100644 --- a/src/lib/formats/apd_dsk.cpp +++ b/src/lib/formats/apd_dsk.cpp @@ -101,8 +101,9 @@ int apd_format::identify(util::random_read &io, uint32_t form_factor, const std: return 0; std::vector<uint8_t> img(size); - size_t actual; - io.read_at(0, &img[0], size, actual); + auto const [ioerr, actual] = read_at(io, 0, &img[0], size); + if (ioerr || (actual != size)) + return 0; int err; std::vector<uint8_t> gz_ptr(8); @@ -126,7 +127,7 @@ int apd_format::identify(util::random_read &io, uint32_t form_factor, const std: err = inflateEnd(&d_stream); if (err != Z_OK) return 0; - img = gz_ptr; + img = std::move(gz_ptr); } if (!memcmp(&img[0], APD_HEADER, sizeof(APD_HEADER))) { @@ -143,8 +144,9 @@ bool apd_format::load(util::random_read &io, uint32_t form_factor, const std::ve return false; std::vector<uint8_t> img(size); - size_t actual; - io.read_at(0, &img[0], size, actual); + auto const [ioerr, actual] = read_at(io, 0, &img[0], size); + if (ioerr || (actual != size)) + return false; int err; std::vector<uint8_t> gz_ptr; diff --git a/src/lib/formats/apf_apt.cpp b/src/lib/formats/apf_apt.cpp index dd1d119b010..9930d501652 100644 --- a/src/lib/formats/apf_apt.cpp +++ b/src/lib/formats/apf_apt.cpp @@ -152,12 +152,12 @@ static int apf_cpf_handle_cassette(int16_t *buffer, const uint8_t *bytes) Generate samples for the tape image ********************************************************************/ -static int apf_apt_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int apf_apt_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { return apf_apt_handle_cassette(buffer, bytes); } -static int apf_cpf_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int apf_cpf_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { return apf_cpf_handle_cassette(buffer, bytes); } diff --git a/src/lib/formats/apridisk.cpp b/src/lib/formats/apridisk.cpp index fafc12d9f3c..36ad6018336 100644 --- a/src/lib/formats/apridisk.cpp +++ b/src/lib/formats/apridisk.cpp @@ -38,8 +38,9 @@ const char *apridisk_format::extensions() const noexcept int apridisk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t header[APR_HEADER_SIZE]; - size_t actual; - io.read_at(0, header, APR_HEADER_SIZE, actual); + auto const [err, actual] = read_at(io, 0, header, APR_HEADER_SIZE); + if (err || (APR_HEADER_SIZE != actual)) + return 0; const char magic[] = "ACT Apricot disk image\x1a\x04"; @@ -64,11 +65,9 @@ bool apridisk_format::load(util::random_read &io, uint32_t form_factor, const st while (file_offset < file_size) { - size_t actual; - // read sector header uint8_t sector_header[16]; - io.read_at(file_offset, sector_header, 16, actual); + read_at(io, file_offset, sector_header, 16); // FIXME: check for errors and premature EOF uint32_t type = get_u32le(§or_header[0]); uint16_t compression = get_u16le(§or_header[4]); @@ -103,7 +102,7 @@ bool apridisk_format::load(util::random_read &io, uint32_t form_factor, const st case APR_COMPRESSED: { uint8_t comp[3]; - io.read_at(file_offset, comp, 3, actual); + read_at(io, file_offset, comp, 3); // FIXME: check for errors and premature EOF uint16_t length = get_u16le(&comp[0]); if (length != SECTOR_SIZE) @@ -117,7 +116,7 @@ bool apridisk_format::load(util::random_read &io, uint32_t form_factor, const st break; case APR_UNCOMPRESSED: - io.read_at(file_offset, data_ptr, SECTOR_SIZE, actual); + read_at(io, file_offset, data_ptr, SECTOR_SIZE); // FIXME: check for errors and premature EOF break; default: diff --git a/src/lib/formats/as_dsk.cpp b/src/lib/formats/as_dsk.cpp index 5c8a0545870..0fad7217585 100644 --- a/src/lib/formats/as_dsk.cpp +++ b/src/lib/formats/as_dsk.cpp @@ -4,55 +4,17 @@ // Applesauce solved output formats #include "as_dsk.h" + #include "ioprocs.h" +#include "multibyte.h" #include <string.h> -as_format::as_format() : floppy_image_format_t() -{ -} -uint32_t as_format::find_tag(const std::vector<uint8_t> &data, uint32_t tag) -{ - uint32_t offset = 12; - do { - if(r32(data, offset) == tag) - return offset + 8; - offset += r32(data, offset+4) + 8; - } while(offset < data.size() - 8); - return 0; -} +namespace { -uint32_t as_format::r32(const std::vector<uint8_t> &data, uint32_t offset) -{ - return data[offset] | (data[offset+1] << 8) | (data[offset+2] << 16) | (data[offset+3] << 24); -} - -uint16_t as_format::r16(const std::vector<uint8_t> &data, uint32_t offset) -{ - return data[offset] | (data[offset+1] << 8); -} - -void as_format::w32(std::vector<uint8_t> &data, int offset, uint32_t value) -{ - data[offset] = value; - data[offset+1] = value >> 8; - data[offset+2] = value >> 16; - data[offset+3] = value >> 24; -} - -void as_format::w16(std::vector<uint8_t> &data, int offset, uint16_t value) -{ - data[offset] = value; - data[offset+1] = value >> 8; -} - -uint8_t as_format::r8(const std::vector<uint8_t> &data, uint32_t offset) -{ - return data[offset]; -} - -uint32_t as_format::crc32r(const uint8_t *data, uint32_t size) +template <typename T> +uint32_t crc32r(T &&data, uint32_t size) { // Reversed crc32 uint32_t crc = 0xffffffff; @@ -67,15 +29,35 @@ uint32_t as_format::crc32r(const uint8_t *data, uint32_t size) return ~crc; } -bool as_format::load_bitstream_track(const std::vector<uint8_t> &img, floppy_image &image, int head, int track, int subtrack, uint8_t idx, uint32_t off_trks, bool may_be_short, bool set_variant) +template <typename T> +uint32_t find_tag(T &&data, size_t size, uint32_t tag) +{ + uint32_t offset = 12; + do { + if(get_u32le(&data[offset]) == tag) + return offset + 8; + offset += get_u32le(&data[offset+4]) + 8; + } while(offset < (size - 8)); + return 0; +} + +} // anonymous namespace + + +as_format::as_format() : floppy_image_format_t() +{ +} + + +bool as_format::load_bitstream_track(const uint8_t *img, floppy_image &image, int head, int track, int subtrack, uint8_t idx, uint32_t off_trks, bool may_be_short, bool set_variant) { uint32_t trks_off = off_trks + (idx * 8); - uint32_t track_size = r32(img, trks_off + 4); + uint32_t track_size = get_u32le(&img[trks_off + 4]); if (track_size == 0) return false; - uint32_t boff = (uint32_t)r16(img, trks_off + 0) * 512; + uint32_t boff = (uint32_t)get_u16le(&img[trks_off + 0]) * 512; // With 5.25 floppies the end-of-track may be missing // if unformatted. Accept track length down to 95% of @@ -92,15 +74,15 @@ bool as_format::load_bitstream_track(const std::vector<uint8_t> &img, floppy_ima generate_track_from_bitstream(track, head, &img[boff], track_size, image, subtrack, 0xffff); if(set_variant) - image.set_variant(r32(img, trks_off + 4) >= 90000 ? floppy_image::DSHD : floppy_image::DSDD); + image.set_variant(get_u32le(&img[trks_off + 4]) >= 90000 ? floppy_image::DSHD : floppy_image::DSDD); return true; } -void as_format::load_flux_track(const std::vector<uint8_t> &img, floppy_image &image, int head, int track, int subtrack, uint8_t fidx, uint32_t off_trks) +void as_format::load_flux_track(const uint8_t *img, floppy_image &image, int head, int track, int subtrack, uint8_t fidx, uint32_t off_trks) { uint32_t trks_off = off_trks + (fidx * 8); - uint32_t boff = (uint32_t)r16(img, trks_off + 0) * 512; - uint32_t track_size = r32(img, trks_off + 4); + uint32_t boff = (uint32_t)get_u16le(&img[trks_off + 0]) * 512; + uint32_t track_size = get_u32le(&img[trks_off + 4]); uint32_t total_ticks = 0; for(uint32_t i=0; i != track_size; i++) @@ -239,13 +221,13 @@ bool as_format::test_flux(const std::vector<tdata> &tracks) void as_format::save_tracks(std::vector<uint8_t> &data, const std::vector<tdata> &tracks, uint32_t total_blocks, bool has_flux) { - w32(data, 80, 0x50414D54); // TMAP - w32(data, 84, 160); // size + put_u32le(&data[80], 0x50414d54); // TMAP + put_u32le(&data[84], 160); // size uint32_t fstart = 1536 + total_blocks*512; if(has_flux) { - w32(data, fstart, 0x58554c46); - w32(data, fstart+4, 160); + put_u32le(&data[fstart], 0x58554c46); + put_u32le(&data[fstart+4], 160); fstart += 8; } @@ -264,8 +246,8 @@ void as_format::save_tracks(std::vector<uint8_t> &data, const std::vector<tdata> } } - w32(data, 248, 0x534B5254); // TRKS - w32(data, 252, 1280 + total_blocks*512); // size + put_u32le(&data[248], 0x534b5254); // TRKS + put_u32le(&data[252], 1280 + total_blocks*512); // size uint8_t tid = 0; uint16_t tb = 3; @@ -274,14 +256,14 @@ void as_format::save_tracks(std::vector<uint8_t> &data, const std::vector<tdata> int size = tracks[i].data.size(); int blocks = (size + 511) / 512; memcpy(data.data() + tb*512, tracks[i].data.data(), size); - w16(data, 256 + tid*8, tb); - w16(data, 256 + tid*8 + 2, blocks); - w32(data, 256 + tid*8 + 4, tracks[i].track_size); + put_u16le(&data[256 + tid*8], tb); + put_u16le(&data[256 + tid*8 + 2], blocks); + put_u32le(&data[256 + tid*8 + 4], tracks[i].track_size); tb += blocks; tid ++; } - w32(data, 8, crc32r(&data[12], data.size() - 12)); + put_u32le(&data[8], crc32r(&data[12], data.size() - 12)); } @@ -316,10 +298,10 @@ const uint8_t woz_format::signature2[8] = { 0x57, 0x4f, 0x5a, 0x32, 0xff, 0x0a, int woz_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t header[8]; - size_t actual; - io.read_at(0, header, 8, actual); - if (!memcmp(header, signature, 8)) return FIFID_SIGN; - if (!memcmp(header, signature2, 8)) return FIFID_SIGN; + auto const [err, actual] = read_at(io, 0, header, 8); + if(err || (8 != actual)) return 0; + if(!memcmp(header, signature, 8)) return FIFID_SIGN; + if(!memcmp(header, signature2, 8)) return FIFID_SIGN; return 0; } @@ -328,9 +310,9 @@ bool woz_format::load(util::random_read &io, uint32_t form_factor, const std::ve uint64_t image_size; if(io.length(image_size)) return false; - std::vector<uint8_t> img(image_size); - size_t actual; - io.read_at(0, &img[0], img.size(), actual); + auto const [err, img, actual] = read_at(io, 0, image_size); + if(err || (actual != image_size)) + return false; // Check signature if((memcmp(&img[0], signature, 8)) && (memcmp(&img[0], signature2, 8))) @@ -340,29 +322,29 @@ bool woz_format::load(util::random_read &io, uint32_t form_factor, const std::ve if(!memcmp(&img[0], signature2, 8)) woz_vers = 2; // Check integrity - uint32_t crc = crc32r(&img[12], img.size() - 12); - if(crc != r32(img, 8)) + uint32_t crc = crc32r(&img[12], image_size - 12); + if(crc != get_u32le(&img[8])) return false; - uint32_t off_info = find_tag(img, 0x4f464e49); - uint32_t off_tmap = find_tag(img, 0x50414d54); - uint32_t off_trks = find_tag(img, 0x534b5254); -// uint32_t off_writ = find_tag(img, 0x54495257); + uint32_t off_info = find_tag(img, image_size, 0x4f464e49); + uint32_t off_tmap = find_tag(img, image_size, 0x50414d54); + uint32_t off_trks = find_tag(img, image_size, 0x534b5254); +// uint32_t off_writ = find_tag(img, image_size, 0x54495257); if(!off_info || !off_tmap || !off_trks) return false; - uint32_t info_vers = r8(img, off_info + 0); + uint32_t info_vers = img[off_info + 0]; if(info_vers < 1 || info_vers > 3) return false; - uint16_t off_flux = info_vers < 3 ? 0 : r16(img, off_info + 46); - uint16_t flux_size = info_vers < 3 ? 0 : r16(img, off_info + 48); + uint16_t off_flux = (info_vers < 3) ? 0 : get_u16le(&img[off_info + 46]); + uint16_t flux_size = (info_vers < 3) ? 0 : get_u16le(&img[off_info + 48]); if(!flux_size) off_flux = 0; - bool is_35 = r8(img, off_info + 1) == 2; + bool is_35 = img[off_info + 1] == 2; if((form_factor == floppy_image::FF_35 && !is_35) || (form_factor == floppy_image::FF_525 && is_35)) return false; @@ -374,36 +356,36 @@ bool woz_format::load(util::random_read &io, uint32_t form_factor, const std::ve else image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD); - if (woz_vers == 1) { + if(woz_vers == 1) { for (unsigned int trkid = 0; trkid != limit; trkid++) { int head = is_35 && trkid >= 80 ? 1 : 0; int track = is_35 ? trkid % 80 : trkid / 4; int subtrack = is_35 ? 0 : trkid & 3; - uint8_t idx = r8(img, off_tmap + trkid); + uint8_t idx = img[off_tmap + trkid]; if(idx != 0xff) { uint32_t boff = off_trks + 6656*idx; - if (r16(img, boff + 6648) == 0) + if (get_u16le(&img[boff + 6648]) == 0) return false; - generate_track_from_bitstream(track, head, &img[boff], r16(img, boff + 6648), image, subtrack, r16(img, boff + 6650)); + generate_track_from_bitstream(track, head, &img[boff], get_u16le(&img[boff + 6648]), image, subtrack, get_u16le(&img[boff + 6650])); if(is_35 && !track && head) image.set_variant(floppy_image::DSDD); } } - } else if (woz_vers == 2) { + } else if(woz_vers == 2) { for (unsigned int trkid = 0; trkid != limit; trkid++) { int head = is_35 && trkid & 1 ? 1 : 0; int track = is_35 ? trkid >> 1 : trkid / 4; int subtrack = is_35 ? 0 : trkid & 3; - uint8_t idx = r8(img, off_tmap + trkid); - uint8_t fidx = off_flux ? r8(img, off_flux*512 + 8 + trkid) : 0xff; + uint8_t idx = img[off_tmap + trkid]; + uint8_t fidx = off_flux ? img[off_flux*512 + 8 + trkid] : 0xff; - if(fidx != 0xff) - load_flux_track(img, image, head, track, subtrack, fidx, off_trks); + if(fidx != 0xff) { + load_flux_track(&img[0], image, head, track, subtrack, fidx, off_trks); - else if(idx != 0xff) { - if(!load_bitstream_track(img, image, head, track, subtrack, idx, off_trks, !is_35, is_35 && !track && head)) + } else if(idx != 0xff) { + if(!load_bitstream_track(&img[0], image, head, track, subtrack, idx, off_trks, !is_35, is_35 && !track && head)) return false; } } @@ -445,31 +427,30 @@ bool woz_format::save(util::random_read_write &io, const std::vector<uint32_t> & memcpy(&data[0], signature2, 8); - w32(data, 12, 0x4F464E49); // INFO - w32(data, 16, 60); // size - data[20] = 3; // chunk version + put_u32le(&data[12], 0x4f464e49); // INFO + put_u32le(&data[16], 60); // size + data[20] = 3; // chunk version data[21] = image.get_form_factor() == floppy_image::FF_525 ? 1 : 2; - data[22] = 0; // not write protected - data[23] = 1; // synchronized, since our internal format is - data[24] = 1; // weak bits are generated, not stored + data[22] = 0; // not write protected + data[23] = 1; // synchronized, since our internal format is + data[24] = 1; // weak bits are generated, not stored data[25] = 'M'; data[26] = 'A'; data[27] = 'M'; data[28] = 'E'; memset(&data[29], ' ', 32-4); data[57] = twosided ? 2 : 1; - data[58] = 0; // boot sector unknown + data[58] = 0; // boot sector unknown data[59] = image.get_form_factor() == floppy_image::FF_525 ? 32 : image.get_variant() == floppy_image::DSHD ? 8 : 16; - w16(data, 60, 0); // compatibility unknown - w16(data, 62, 0); // needed ram unknown - w16(data, 64, max_blocks); - w16(data, 66, has_flux ? total_blocks+3 : 0); - w16(data, 68, max_blocks); + put_u16le(&data[60], 0); // compatibility unknown + put_u16le(&data[62], 0); // needed RAM unknown + put_u16le(&data[64], max_blocks); + put_u16le(&data[66], has_flux ? total_blocks+3 : 0); + put_u16le(&data[68], max_blocks); save_tracks(data, tracks, total_blocks, has_flux); - size_t actual; - io.write_at(0, data.data(), data.size(), actual); + /*auto const [err, actual] =*/ write_at(io, 0, data.data(), data.size()); // FIXME: check for errors return true; } @@ -505,9 +486,9 @@ const uint8_t moof_format::signature[8] = { 0x4d, 0x4f, 0x4f, 0x46, 0xff, 0x0a, int moof_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t header[8]; - size_t actual; - io.read_at(0, header, 8, actual); - if (!memcmp(header, signature, 8)) return FIFID_SIGN; + auto const [err, actual] = read_at(io, 0, header, 8); + if(err || (8 != actual)) return 0; + if(!memcmp(header, signature, 8)) return FIFID_SIGN; return 0; } @@ -516,37 +497,37 @@ bool moof_format::load(util::random_read &io, uint32_t form_factor, const std::v uint64_t image_size; if(io.length(image_size)) return false; - std::vector<uint8_t> img(image_size); - size_t actual; - io.read_at(0, &img[0], img.size(), actual); + auto const [err, img, actual] = read_at(io, 0, image_size); + if(err || (actual != image_size)) + return false; // Check signature if(memcmp(&img[0], signature, 8)) return false; // Check integrity - uint32_t crc = crc32r(&img[12], img.size() - 12); - if(crc != r32(img, 8)) + uint32_t crc = crc32r(&img[12], image_size - 12); + if(crc != get_u32le(&img[8])) return false; - uint32_t off_info = find_tag(img, 0x4f464e49); - uint32_t off_tmap = find_tag(img, 0x50414d54); - uint32_t off_trks = find_tag(img, 0x534b5254); + uint32_t off_info = find_tag(img, image_size, 0x4f464e49); + uint32_t off_tmap = find_tag(img, image_size, 0x50414d54); + uint32_t off_trks = find_tag(img, image_size, 0x534b5254); if(!off_info || !off_tmap || !off_trks) return false; - uint32_t info_vers = r8(img, off_info + 0); + uint32_t info_vers = img[off_info + 0]; if(info_vers != 1) return false; - uint16_t off_flux = r16(img, off_info + 40); - uint16_t flux_size = r16(img, off_info + 42); + uint16_t off_flux = get_u16le(&img[off_info + 40]); + uint16_t flux_size = get_u16le(&img[off_info + 42]); if(!flux_size) off_flux = 0; - switch(r8(img, off_info + 1)) { + switch(img[off_info + 1]) { case 1: image.set_form_variant(floppy_image::FF_35, floppy_image::SSDD); break; @@ -564,14 +545,14 @@ bool moof_format::load(util::random_read &io, uint32_t form_factor, const std::v int head = trkid & 1; int track = trkid >> 1; - uint8_t idx = r8(img, off_tmap + trkid); - uint8_t fidx = off_flux ? r8(img, off_flux*512 + 8 + trkid) : 0xff; + uint8_t idx = img[off_tmap + trkid]; + uint8_t fidx = off_flux ? img[off_flux*512 + 8 + trkid] : 0xff; - if(fidx != 0xff) - load_flux_track(img, image, head, track, 0, fidx, off_trks); + if(fidx != 0xff) { + load_flux_track(&img[0], image, head, track, 0, fidx, off_trks); - else if(idx != 0xff) { - if(!load_bitstream_track(img, image, head, track, 0, idx, off_trks, false, false)) + } else if(idx != 0xff) { + if(!load_bitstream_track(&img[0], image, head, track, 0, idx, off_trks, false, false)) return false; } } @@ -606,27 +587,26 @@ bool moof_format::save(util::random_read_write &io, const std::vector<uint32_t> memcpy(&data[0], signature, 8); - w32(data, 12, 0x4F464E49); // INFO - w32(data, 16, 60); // size - data[20] = 1; // chunk version + put_u32le(&data[12], 0x4f464e49); // INFO + put_u32le(&data[16], 60); // size + data[20] = 1; // chunk version data[21] = is_hd ? 3 : twosided ? 2 : 1; // variant - data[22] = 0; // not write protected - data[23] = 1; // synchronized, since our internal format is - data[24] = is_hd ? 8 : 16; // optimal timing + data[22] = 0; // not write protected + data[23] = 1; // synchronized, since our internal format is + data[24] = is_hd ? 8 : 16; // optimal timing data[25] = 'M'; data[26] = 'A'; data[27] = 'M'; data[28] = 'E'; memset(&data[29], ' ', 32-4); - data[57] = 0; // pad - w16(data, 58, max_blocks); - w16(data, 60, has_flux ? total_blocks+3 : 0); - w16(data, 62, max_blocks); + data[57] = 0; // pad + put_u16le(&data[58], max_blocks); + put_u16le(&data[60], has_flux ? total_blocks+3 : 0); + put_u16le(&data[62], max_blocks); save_tracks(data, tracks, total_blocks, has_flux); - size_t actual; - io.write_at(0, data.data(), data.size(), actual); + /*auto const [err, actual] =*/ write_at(io, 0, data.data(), data.size()); // FIXME: check for errors return true; } diff --git a/src/lib/formats/as_dsk.h b/src/lib/formats/as_dsk.h index ec0a785d151..8871bc1ed11 100644 --- a/src/lib/formats/as_dsk.h +++ b/src/lib/formats/as_dsk.h @@ -23,16 +23,8 @@ protected: bool flux = false; }; - static uint32_t r32(const std::vector<uint8_t> &data, uint32_t offset); - static uint16_t r16(const std::vector<uint8_t> &data, uint32_t offset); - static uint8_t r8(const std::vector<uint8_t> &data, uint32_t offset); - static void w32(std::vector<uint8_t> &data, int offset, uint32_t value); - static void w16(std::vector<uint8_t> &data, int offset, uint16_t value); - static uint32_t crc32r(const uint8_t *data, uint32_t size); - static uint32_t find_tag(const std::vector<uint8_t> &data, uint32_t tag); - - static bool load_bitstream_track(const std::vector<uint8_t> &img, floppy_image &image, int head, int track, int subtrack, uint8_t idx, uint32_t off_trks, bool may_be_short, bool set_variant); - static void load_flux_track(const std::vector<uint8_t> &img, floppy_image &image, int head, int track, int subtrack, uint8_t fidx, uint32_t off_trks); + static bool load_bitstream_track(const uint8_t *img, floppy_image &image, int head, int track, int subtrack, uint8_t idx, uint32_t off_trks, bool may_be_short, bool set_variant); + static void load_flux_track(const uint8_t *img, floppy_image &image, int head, int track, int subtrack, uint8_t fidx, uint32_t off_trks); static tdata analyze_for_save(const floppy_image &image, int head, int track, int subtrack, int speed_zone); static std::pair<int, int> count_blocks(const std::vector<tdata> &tracks); diff --git a/src/lib/formats/bk0010_dsk.cpp b/src/lib/formats/bk0010_dsk.cpp new file mode 100644 index 00000000000..8922b144fcd --- /dev/null +++ b/src/lib/formats/bk0010_dsk.cpp @@ -0,0 +1,39 @@ +// license:BSD-3-Clause +// copyright-holders:Sergey Svishchev +/********************************************************************* + + formats/bk0010_dsk.cpp + + Floppies used by BK (BY: device), DVK (MY: device) and UKNC + (MZ: device) + +*********************************************************************/ + +#include "formats/bk0010_dsk.h" + +bk0010_format::bk0010_format() : wd177x_format(formats) +{ +} + +// gap sizes taken from BKBTL emulator +const bk0010_format::format bk0010_format::formats[] = { + { + floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM, + 2000, // 2us, 300rpm + 10, 80, 1, + 512, {}, + 1, {}, + 42, 22, 36 + }, + { + floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM, + 2000, // 2us, 300rpm + 10, 80, 2, + 512, {}, + 1, {}, + 42, 22, 36 + }, + {} +}; + +const bk0010_format FLOPPY_BK0010_FORMAT; diff --git a/src/lib/formats/bk0010_dsk.h b/src/lib/formats/bk0010_dsk.h new file mode 100644 index 00000000000..96b00ebede7 --- /dev/null +++ b/src/lib/formats/bk0010_dsk.h @@ -0,0 +1,30 @@ +// license:BSD-3-Clause +// copyright-holders:Sergey Svishchev +/********************************************************************* + + formats/bk0010_dsk.h + +*********************************************************************/ +#ifndef MAME_FORMATS_BK0010_DSK_H +#define MAME_FORMATS_BK0010_DSK_H + +#pragma once + +#include "wd177x_dsk.h" + +class bk0010_format : public wd177x_format +{ +public: + bk0010_format(); + + virtual const char *name() const noexcept override { return "bk0010"; } + virtual const char *description() const noexcept override { return "BK-0010 disk image"; } + virtual const char *extensions() const noexcept override { return "img,bkd"; } + +private: + static const format formats[]; +}; + +extern const bk0010_format FLOPPY_BK0010_FORMAT; + +#endif // MAME_FORMATS_BK0010_DSK_H diff --git a/src/lib/formats/camplynx_cas.cpp b/src/lib/formats/camplynx_cas.cpp index a6151462911..545814b954f 100644 --- a/src/lib/formats/camplynx_cas.cpp +++ b/src/lib/formats/camplynx_cas.cpp @@ -179,7 +179,7 @@ static int camplynx_handle_cassette(int16_t *buffer, const uint8_t *bytes) Generate samples for the tape image ********************************************************************/ -static int camplynx_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int camplynx_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { return camplynx_handle_cassette(buffer, bytes); } diff --git a/src/lib/formats/cassimg.cpp b/src/lib/formats/cassimg.cpp index 38be38db9b1..cea484f2735 100644 --- a/src/lib/formats/cassimg.cpp +++ b/src/lib/formats/cassimg.cpp @@ -366,8 +366,7 @@ void cassette_image::change(util::random_read_write::ptr &&io, const Format *for void cassette_image::image_read(void *buffer, uint64_t offset, size_t length) { - size_t actual; - m_io->read_at(offset, buffer, length, actual); + /*auto const [err, actual] =*/ read_at(*m_io, offset, buffer, length); // FIXME: check for errors and premature EOF } @@ -383,8 +382,7 @@ uint8_t cassette_image::image_read_byte(uint64_t offset) void cassette_image::image_write(const void *buffer, uint64_t offset, size_t length) { - size_t actual; - m_io->write_at(offset, buffer, length, actual); + /*auto const [err, actual] =*/ write_at(*m_io, offset, buffer, length); // FIXME: check for errors } @@ -793,8 +791,8 @@ cassette_image::error cassette_image::legacy_construct(const LegacyWaveFiller *l error err; int length; int sample_count; - std::vector<uint8_t> bytes; - std::vector<int16_t> samples; + std::unique_ptr<int16_t []> samples; + std::unique_ptr<uint8_t []> chunk; int pos = 0; uint64_t offset = 0; @@ -803,7 +801,7 @@ cassette_image::error cassette_image::legacy_construct(const LegacyWaveFiller *l assert(legacy_args->trailer_samples >= 0); assert(legacy_args->fill_wave); - uint64_t size = image_size(); + const uint64_t size = image_size(); /* normalize the args */ LegacyWaveFiller args = *legacy_args; @@ -814,21 +812,24 @@ cassette_image::error cassette_image::legacy_construct(const LegacyWaveFiller *l if (args.sample_frequency == 0) args.sample_frequency = 11025; - /* allocate a buffer for the binary data */ - std::vector<uint8_t> chunk(args.chunk_size); - /* determine number of samples */ if (args.chunk_sample_calc != nullptr) { - if (size > 0x7FFFFFFF) + if (size > 0x7fffffff) + { + err = error::OUT_OF_MEMORY; + goto done; + } + + std::unique_ptr<uint8_t []> bytes(new (std::nothrow) uint8_t [size]); + if (!bytes) { err = error::OUT_OF_MEMORY; goto done; } - bytes.resize(size); - image_read(&bytes[0], 0, size); - sample_count = args.chunk_sample_calc(&bytes[0], (int)size); + image_read(bytes.get(), 0, size); + sample_count = args.chunk_sample_calc(bytes.get(), (int)size); // chunk_sample_calc functions report errors by returning negative numbers if (sample_count < 0) @@ -848,12 +849,17 @@ cassette_image::error cassette_image::legacy_construct(const LegacyWaveFiller *l sample_count += args.header_samples + args.trailer_samples; /* allocate a buffer for the completed samples */ - samples.resize(sample_count); + samples.reset(new (std::nothrow) int16_t [sample_count]); + if (!samples) + { + err = error::OUT_OF_MEMORY; + goto done; + } /* if there has to be a header */ if (args.header_samples > 0) { - length = args.fill_wave(&samples[pos], sample_count - pos, CODE_HEADER); + length = args.fill_wave(&samples[pos], sample_count - pos, CODE_HEADER, -1); if (length < 0) { err = error::INVALID_IMAGE; @@ -863,12 +869,19 @@ cassette_image::error cassette_image::legacy_construct(const LegacyWaveFiller *l } /* convert the file data to samples */ + chunk.reset(new (std::nothrow) uint8_t [args.chunk_size]); + if (!chunk) + { + err = error::OUT_OF_MEMORY; + goto done; + } while ((pos < sample_count) && (offset < size)) { - image_read(&chunk[0], offset, args.chunk_size); - offset += args.chunk_size; + const int slice = std::min<int>(args.chunk_size, size - offset); + image_read(chunk.get(), offset, slice); + offset += slice; - length = args.fill_wave(&samples[pos], sample_count - pos, &chunk[0]); + length = args.fill_wave(&samples[pos], sample_count - pos, chunk.get(), slice); if (length < 0) { err = error::INVALID_IMAGE; @@ -878,11 +891,12 @@ cassette_image::error cassette_image::legacy_construct(const LegacyWaveFiller *l if (length == 0) break; } + chunk.reset(); /* if there has to be a trailer */ if (args.trailer_samples > 0) { - length = args.fill_wave(&samples[pos], sample_count - pos, CODE_TRAILER); + length = args.fill_wave(&samples[pos], sample_count - pos, CODE_TRAILER, -1); if (length < 0) { err = error::INVALID_IMAGE; diff --git a/src/lib/formats/cassimg.h b/src/lib/formats/cassimg.h index 9714e8e223b..9867ecbdf4a 100644 --- a/src/lib/formats/cassimg.h +++ b/src/lib/formats/cassimg.h @@ -120,7 +120,7 @@ public: /* code to adapt existing legacy fill_wave functions */ struct LegacyWaveFiller { - int (*fill_wave)(int16_t *, int, uint8_t *) = nullptr; + int (*fill_wave)(int16_t *, int, const uint8_t *, int) = nullptr; int chunk_size = 0; int chunk_samples = 0; int (*chunk_sample_calc)(const uint8_t *bytes, int length) = nullptr; diff --git a/src/lib/formats/cbm_crt.cpp b/src/lib/formats/cbm_crt.cpp index b088507208f..6bc9e0f8d23 100644 --- a/src/lib/formats/cbm_crt.cpp +++ b/src/lib/formats/cbm_crt.cpp @@ -46,6 +46,8 @@ #include "osdcore.h" // osd_printf_* +#include <tuple> + //************************************************************************** // MACROS/CONSTANTS @@ -137,8 +139,7 @@ std::string cbm_crt_get_card(util::core_file &file) { // read the header cbm_crt_header header; - size_t actual; - std::error_condition err = file.read(&header, CRT_HEADER_LENGTH, actual); + auto const [err, actual] = read(file, &header, CRT_HEADER_LENGTH); if (!err && (CRT_HEADER_LENGTH == actual) && !memcmp(header.signature, CRT_SIGNATURE, 16)) { @@ -157,11 +158,13 @@ std::string cbm_crt_get_card(util::core_file &file) bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_size, int *exrom, int *game) { + std::error_condition err; size_t actual; // read the header cbm_crt_header header; - if (file.read(&header, CRT_HEADER_LENGTH, actual)) + std::tie(err, actual) = read(file, &header, CRT_HEADER_LENGTH); + if (err) return false; if ((CRT_HEADER_LENGTH != actual) || (memcmp(header.signature, CRT_SIGNATURE, 16) != 0)) @@ -184,7 +187,8 @@ bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_ while (!file.eof()) { cbm_crt_chip chip; - if (file.read(&chip, CRT_CHIP_LENGTH, actual) || (CRT_CHIP_LENGTH != actual)) + std::tie(err, actual) = read(file, &chip, CRT_CHIP_LENGTH); + if (err || (CRT_CHIP_LENGTH != actual)) return false; const uint16_t address = get_u16be(chip.start_address); @@ -228,21 +232,22 @@ bool cbm_crt_read_data(util::core_file &file, uint8_t *roml, uint8_t *romh) while (!file.eof()) { + std::error_condition err; size_t actual; cbm_crt_chip chip; - if (file.read(&chip, CRT_CHIP_LENGTH, actual) || (CRT_CHIP_LENGTH != actual)) + std::tie(err, actual) = read(file, &chip, CRT_CHIP_LENGTH); + if (err || (CRT_CHIP_LENGTH != actual)) return false; const uint16_t address = get_u16be(chip.start_address); const uint16_t size = get_u16be(chip.image_size); - std::error_condition err; switch (address) { - case 0x8000: err = file.read(roml + roml_offset, size, actual); roml_offset += size; break; - case 0xa000: err = file.read(romh + romh_offset, size, actual); romh_offset += size; break; - case 0xe000: err = file.read(romh + romh_offset, size, actual); romh_offset += size; break; + case 0x8000: std::tie(err, actual) = read(file, roml + roml_offset, size); roml_offset += size; break; + case 0xa000: std::tie(err, actual) = read(file, romh + romh_offset, size); romh_offset += size; break; + case 0xe000: std::tie(err, actual) = read(file, romh + romh_offset, size); romh_offset += size; break; // FIXME: surely one needs to report an error or skip over the data if the load address is not recognised? } if (err) // TODO: check size - all bets are off if the address isn't recognised anyway diff --git a/src/lib/formats/cbm_tap.cpp b/src/lib/formats/cbm_tap.cpp index 743ad4282d2..65c265ac86f 100644 --- a/src/lib/formats/cbm_tap.cpp +++ b/src/lib/formats/cbm_tap.cpp @@ -330,7 +330,7 @@ static int cbm_tap_to_wav_size( const uint8_t *tapdata, int taplen ) return size; } -static int cbm_tap_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) +static int cbm_tap_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { int16_t *p = buffer; diff --git a/src/lib/formats/ccvf_dsk.cpp b/src/lib/formats/ccvf_dsk.cpp index 829be2a0019..d2c8da8e083 100644 --- a/src/lib/formats/ccvf_dsk.cpp +++ b/src/lib/formats/ccvf_dsk.cpp @@ -50,8 +50,10 @@ const ccvf_format::format ccvf_format::file_formats[] = { int ccvf_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { char h[36]; - size_t actual; - io.read_at(0, h, 36, actual); + auto const [err, actual] = read_at(io, 0, h, 36); + if (err || (36 != actual)) + return 0; + if (!memcmp(h, "Compucolor Virtual Floppy Disk Image", 36)) return FIFID_SIGN; @@ -96,9 +98,9 @@ bool ccvf_format::load(util::random_read &io, uint32_t form_factor, const std::v if (io.length(size)) return false; - std::vector<uint8_t> img(size); - size_t actual; - io.read_at(0, &img[0], size, actual); + auto [err, img, actual] = read_at(io, 0, size); + if (err || (actual != size)) + return false; std::string ccvf = std::string((const char *)&img[0], size); std::vector<uint8_t> bytes(78720); diff --git a/src/lib/formats/cgen_cas.cpp b/src/lib/formats/cgen_cas.cpp index c0737db635b..1ae1682cba9 100644 --- a/src/lib/formats/cgen_cas.cpp +++ b/src/lib/formats/cgen_cas.cpp @@ -108,7 +108,7 @@ static int cgenie_handle_cas(int16_t *buffer, const uint8_t *casdata) /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int cgenie_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int cgenie_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { return cgenie_handle_cas(buffer, bytes); } diff --git a/src/lib/formats/concept_dsk.cpp b/src/lib/formats/concept_dsk.cpp index e026090e19f..52950b3e57d 100644 --- a/src/lib/formats/concept_dsk.cpp +++ b/src/lib/formats/concept_dsk.cpp @@ -114,8 +114,7 @@ bool cc525dsdd_format::load(util::random_read &io, uint32_t form_factor, const s int track_size = sector_count*512; for (int track=0; track < track_count; track++) { for (int head=0; head < head_count; head++) { - size_t actual; - io.read_at((track*head_count + head) * track_size, sectdata, track_size, actual); + /*auto const [err, acutal] =*/ read_at(io, (track*head_count + head) * track_size, sectdata, track_size); // FIXME: check for errors and premature EOF generate_track(cc_9_desc, track, head, sectors, sector_count, 100000, image); } } @@ -146,8 +145,7 @@ bool cc525dsdd_format::save(util::random_read_write &io, const std::vector<uint3 for (int track=0; track < track_count; track++) { for (int head=0; head < head_count; head++) { get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata); - size_t actual; - io.write_at((track*head_count + head)*track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ write_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors } } diff --git a/src/lib/formats/coupedsk.cpp b/src/lib/formats/coupedsk.cpp index 6e43aa9c301..a3edc5c7b40 100644 --- a/src/lib/formats/coupedsk.cpp +++ b/src/lib/formats/coupedsk.cpp @@ -94,8 +94,7 @@ bool mgt_format::load(util::random_read &io, uint32_t form_factor, const std::ve int track_size = sector_count*512; for(int head=0; head < 2; head++) { for(int track=0; track < 80; track++) { - size_t actual; - io.read_at((track*2+head)*track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ read_at(io, (track*2+head)*track_size, sectdata, track_size); // FIXME: check for errors and premature EOF generate_track(desc_10, track, head, sectors, sector_count+1, 100000, image); } } @@ -120,8 +119,7 @@ bool mgt_format::save(util::random_read_write &io, const std::vector<uint32_t> & for(int head=0; head < 2; head++) { for(int track=0; track < 80; track++) { get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata); - size_t actual; - io.write_at((track*2+head)*track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ write_at(io, (track*2+head)*track_size, sectdata, track_size); // FIXME: check for errors } } diff --git a/src/lib/formats/cp68_dsk.cpp b/src/lib/formats/cp68_dsk.cpp index c6eb005f1c0..5872da350b6 100644 --- a/src/lib/formats/cp68_dsk.cpp +++ b/src/lib/formats/cp68_dsk.cpp @@ -51,6 +51,9 @@ #include "ioprocs.h" +#include <tuple> + + namespace { class cp68_formats : public wd177x_format @@ -115,8 +118,8 @@ int cp68_format::find_size(util::random_read &io, uint32_t form_factor, const st std::error_condition ec; // Look at the boot sector. - ec = io.read_at(128 * 0, &boot0, sizeof(boot0), actual); - if (ec || actual == 0) + std::tie(ec, actual) = read_at(io, 128 * 0, &boot0, sizeof(boot0)); + if (ec || actual == 0) // FIXME: what's the actual minimum size boot sector to probe? return -1; uint8_t boot0_sector_id = 1; // uint8_t boot1_sector_id = 2; @@ -125,8 +128,7 @@ int cp68_format::find_size(util::random_read &io, uint32_t form_factor, const st // set the numbering of the first two sectors. If this is shown to not // be practical in some common cases then a separate format variant // might be needed. - if (boot0[0] == 0xbd && boot0[3] == 0x86) - { + if (boot0[0] == 0xbd && boot0[3] == 0x86) { // Found a 6800 jsr and ldaa, looks like a CP68 6800 boot sector. boot0_sector_id = 0; } @@ -135,8 +137,8 @@ int cp68_format::find_size(util::random_read &io, uint32_t form_factor, const st const format &f = cp68_formats::formats[i]; // Look at the system information sector. - ec = io.read_at(f.sector_base_size * 2, &info, sizeof(struct cp68_formats::sysinfo_sector_cp68), actual); - if (ec || actual == 0) + std::tie(ec, actual) = read_at(io, f.sector_base_size * 2, &info, sizeof(struct cp68_formats::sysinfo_sector_cp68)); + if (ec || actual == 0) // FIXME: what's the actual minimum sector size? continue; LOG_FORMATS("CP68 floppy dsk: size %d bytes, %d total sectors, %d remaining bytes, expected form factor %x\n", (uint32_t)size, (uint32_t)size / f.sector_base_size, (uint32_t)size % f.sector_base_size, form_factor); diff --git a/src/lib/formats/cqm_dsk.cpp b/src/lib/formats/cqm_dsk.cpp index 47fa0fddfbe..850d223da7a 100644 --- a/src/lib/formats/cqm_dsk.cpp +++ b/src/lib/formats/cqm_dsk.cpp @@ -258,8 +258,9 @@ const char *cqm_format::extensions() const noexcept int cqm_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t h[3]; - size_t actual; - io.read_at(0, h, 3, actual); + auto const [err, actual] = read_at(io, 0, h, 3); + if (err || (3 != actual)) + return 0; if (h[0] == 'C' && h[1] == 'Q' && h[2] == 0x14) return FIFID_SIGN; @@ -269,11 +270,10 @@ int cqm_format::identify(util::random_read &io, uint32_t form_factor, const std: bool cqm_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { - size_t actual; const int max_size = 4*1024*1024; // 4MB ought to be large enough for any floppy std::vector<uint8_t> imagebuf(max_size); uint8_t header[CQM_HEADER_SIZE]; - io.read_at(0, header, CQM_HEADER_SIZE, actual); + read_at(io, 0, header, CQM_HEADER_SIZE); // FIXME: check for errors and premature EOF int sector_size = get_u16le(&header[0x03]); int sector_per_track = get_u16le(&header[0x10]); @@ -317,8 +317,9 @@ bool cqm_format::load(util::random_read &io, uint32_t form_factor, const std::ve uint64_t cqm_size; if (io.length(cqm_size)) return false; - std::vector<uint8_t> cqmbuf(cqm_size); - io.read_at(0, &cqmbuf[0], cqm_size, actual); + auto const [err, cqmbuf, actual] = read_at(io, 0, cqm_size); + if (err || (actual != cqm_size)) + return false; // decode the RLE data for (int s = 0, pos = CQM_HEADER_SIZE + comment_size; pos < cqm_size; ) diff --git a/src/lib/formats/d64_dsk.cpp b/src/lib/formats/d64_dsk.cpp index 56fec80c8c9..e1fb024e74d 100644 --- a/src/lib/formats/d64_dsk.cpp +++ b/src/lib/formats/d64_dsk.cpp @@ -127,8 +127,7 @@ int d64_format::get_disk_id_offset(const format &f) const void d64_format::get_disk_id(const format &f, util::random_read &io, uint8_t &id1, uint8_t &id2) const { uint8_t id[2]; - size_t actual; - io.read_at(get_disk_id_offset(f), id, 2, actual); + /*auto const [err, actual] =*/ read_at(io, get_disk_id_offset(f), id, 2); // FIXME: check for errors and premature EOF id1 = id[0]; id2 = id[1]; } @@ -238,8 +237,7 @@ bool d64_format::load(util::random_read &io, uint32_t form_factor, const std::ve img.resize(size); } - size_t actual; - io.read_at(0, &img[0], size, actual); + /*auto const [err, actual] =*/ read_at(io, 0, &img[0], size); // FIXME: check for errors and premature EOF int track_offset = 0, error_offset = f.sector_count*f.sector_base_size; @@ -294,8 +292,7 @@ bool d64_format::save(util::random_read_write &io, const std::vector<uint32_t> & build_sector_description(f, sectdata, 0, 0, sectors, sector_count); extract_sectors(image, f, sectors, track, head, sector_count); - size_t actual; - io.write_at(offset, sectdata, track_size, actual); + /*auto const [err, actual] =*/ write_at(io, offset, sectdata, track_size); // FIXME: check for errors } } diff --git a/src/lib/formats/d88_dsk.cpp b/src/lib/formats/d88_dsk.cpp index e0482dadbf3..93a995a47fb 100644 --- a/src/lib/formats/d88_dsk.cpp +++ b/src/lib/formats/d88_dsk.cpp @@ -35,6 +35,8 @@ #include "ioprocs.h" #include "multibyte.h" +#include <tuple> + #define D88_HEADER_LEN 0x2b0 @@ -411,9 +413,11 @@ int d88_format::identify(util::random_read &io, uint32_t form_factor, const std: return 0; uint8_t h[32]; - size_t actual; - io.read_at(0, h, 32, actual); - if((little_endianize_int32(*(uint32_t *)(h+0x1c)) == size) && + auto const [err, actual] = read_at(io, 0, h, 32); + if(err || (32 != actual)) + return 0; + + if(((get_u32le(h+0x1c) == size) || (get_u32le(h+0x1c) == (size >> 1))) && (h[0x1b] == 0x00 || h[0x1b] == 0x10 || h[0x1b] == 0x20 || h[0x1b] == 0x30 || h[0x1b] == 0x40)) return FIFID_SIZE|FIFID_STRUCT; @@ -422,10 +426,13 @@ int d88_format::identify(util::random_read &io, uint32_t form_factor, const std: bool d88_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { + std::error_condition err; size_t actual; uint8_t h[32]; - io.read_at(0, h, 32, actual); + std::tie(err, actual) = read_at(io, 0, h, 32); + if(err || (32 != actual)) + return false; int cell_count = 0; int track_count = 0; @@ -470,8 +477,16 @@ bool d88_format::load(util::random_read &io, uint32_t form_factor, const std::ve if(!head_count) return false; + int img_tracks, img_heads; + image.get_maximal_geometry(img_tracks, img_heads); + if (track_count > img_tracks) + osd_printf_warning("d88: Floppy disk has too many tracks for this drive (floppy tracks=%d, drive tracks=%d).\n", track_count, img_tracks); + + if (head_count > img_heads) + osd_printf_warning("d88: Floppy disk has excess of heads for this drive that will be discarded (floppy heads=%d, drive heads=%d).\n", head_count, img_heads); + uint32_t track_pos[164]; - io.read_at(32, track_pos, 164*4, actual); + std::tie(err, actual) = read_at(io, 32, track_pos, 164*4); // FIXME: check for errors and premature EOF uint64_t file_size; if(io.length(file_size)) @@ -493,7 +508,7 @@ bool d88_format::load(util::random_read &io, uint32_t form_factor, const std::ve return true; uint8_t hs[16]; - io.read_at(pos, hs, 16, actual); + std::tie(err, actual) = read_at(io, pos, hs, 16); // FIXME: check for errors and premature EOF pos += 16; uint16_t size = little_endianize_int16(*(uint16_t *)(hs+14)); @@ -520,7 +535,7 @@ bool d88_format::load(util::random_read &io, uint32_t form_factor, const std::ve if(size) { sects[i].data = sect_data + sdatapos; - io.read_at(pos, sects[i].data, size, actual); + std::tie(err, actual) = read_at(io, pos, sects[i].data, size); // FIXME: check for errors and premature EOF pos += size; sdatapos += size; @@ -528,10 +543,12 @@ bool d88_format::load(util::random_read &io, uint32_t form_factor, const std::ve sects[i].data = nullptr; } - if(density == 0x40) - build_pc_track_fm(track, head, image, cell_count / 2, sector_count, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size)); - else - build_pc_track_mfm(track, head, image, cell_count, sector_count, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size)); + if(head < img_heads) { + if(density == 0x40) + build_pc_track_fm(track, head, image, cell_count / 2, sector_count, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size)); + else + build_pc_track_mfm(track, head, image, cell_count, sector_count, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size)); + } } return true; diff --git a/src/lib/formats/dcp_dsk.cpp b/src/lib/formats/dcp_dsk.cpp index f7740a0ea29..dfc9eea27dc 100644 --- a/src/lib/formats/dcp_dsk.cpp +++ b/src/lib/formats/dcp_dsk.cpp @@ -54,8 +54,7 @@ int dcp_format::identify(util::random_read &io, uint32_t form_factor, const std: int heads, tracks, spt, bps, count_tracks = 0; bool is_hdb = false; - size_t actual; - io.read_at(0, h, 0xa2, actual); + /*auto const [err, actual] =*/ read_at(io, 0, h, 0xa2); // FIXME: check for errors and premature EOF // First byte is the disk format (see below in load() for details) switch (h[0]) @@ -119,12 +118,11 @@ int dcp_format::identify(util::random_read &io, uint32_t form_factor, const std: bool dcp_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { - size_t actual; uint8_t h[0xa2]; int heads, tracks, spt, bps; bool is_hdb = false; - io.read_at(0, h, 0xa2, actual); + /*auto const [err, actual] =*/ read_at(io, 0, h, 0xa2); // FIXME: check for errors and premature EOF // First byte is the disk format: switch (h[0]) @@ -221,7 +219,7 @@ bool dcp_format::load(util::random_read &io, uint32_t form_factor, const std::ve for (int track = 0; track < tracks; track++) for (int head = 0; head < heads; head++) { - io.read_at(0xa2 + bps * spt * (track * heads + head), sect_data, bps * spt, actual); + /*auto const [err, actual] =*/ read_at(io, 0xa2 + bps * spt * (track * heads + head), sect_data, bps * spt); // FIXME: check for errors and premature EOF for (int i = 0; i < spt; i++) { @@ -241,7 +239,7 @@ bool dcp_format::load(util::random_read &io, uint32_t form_factor, const std::ve else // FIXME: the code below is untested, because no image was found... there might be some silly mistake in the disk geometry! { // Read Head 0 Track 0 is FM with 26 sectors of 128bytes instead of 256 - io.read_at(0xa2, sect_data, 128 * spt, actual); + /*auto const [err, actual] =*/ read_at(io, 0xa2, sect_data, 128 * spt); // FIXME: check for errors and premature EOF for (int i = 0; i < spt; i++) { @@ -258,7 +256,7 @@ bool dcp_format::load(util::random_read &io, uint32_t form_factor, const std::ve build_pc_track_fm(0, 0, image, cell_count, spt, sects, calc_default_pc_gap3_size(form_factor, 128)); // Read Head 1 Track 0 is MFM with 26 sectors of 256bytes - io.read_at(0xa2 + 128 * spt, sect_data, bps * spt, actual); + /*auto const [err, actual] =*/ read_at(io, 0xa2 + 128 * spt, sect_data, bps * spt); // FIXME: check for errors and premature EOF for (int i = 0; i < spt; i++) { @@ -279,7 +277,7 @@ bool dcp_format::load(util::random_read &io, uint32_t form_factor, const std::ve for (int track = 1; track < tracks; track++) for (int head = 0; head < heads; head++) { - io.read_at(data_offs + bps * spt * ((track - 1) * heads + head), sect_data, bps * spt, actual); + /*auto const [err, actual] =*/ read_at(io, data_offs + bps * spt * ((track - 1) * heads + head), sect_data, bps * spt); // FIXME: check for errors and premature EOF for (int i = 0; i < spt; i++) { diff --git a/src/lib/formats/dfi_dsk.cpp b/src/lib/formats/dfi_dsk.cpp index a0d216002bd..e2ea1fda0e3 100644 --- a/src/lib/formats/dfi_dsk.cpp +++ b/src/lib/formats/dfi_dsk.cpp @@ -18,6 +18,8 @@ #include "osdcore.h" // osd_printf_* +#include <tuple> + #define NUMBER_OF_MULTIREADS 3 // thresholds for brickwall windowing @@ -63,16 +65,23 @@ bool dfi_format::supports_save() const noexcept int dfi_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { char sign[4]; - size_t actual; - io.read_at(0, sign, 4, actual); + auto const [err, actual] = read_at(io, 0, sign, 4); + if(err || (4 != actual)) { + return 0; + } return memcmp(sign, "DFE2", 4) ? 0 : FIFID_SIGN; } bool dfi_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { + std::error_condition err; size_t actual; + char sign[4]; - io.read_at(0, sign, 4, actual); + std::tie(err, actual) = read_at(io, 0, sign, 4); + if(err || (4 != actual)) { + return false; + } if(memcmp(sign, "DFER", 4) == 0) { osd_printf_error("dfi_dsk: Old type Discferret image detected; the MAME Discferret decoder will not handle this properly, bailing out!\n"); return false; @@ -89,7 +98,7 @@ bool dfi_format::load(util::random_read &io, uint32_t form_factor, const std::ve [[maybe_unused]] int rpm=360; // drive rpm while(pos < size) { uint8_t h[10]; - io.read_at(pos, h, 10, actual); + std::tie(err, actual) = read_at(io, pos, h, 10); // FIXME: check for errors and premature EOF uint16_t track = get_u16be(&h[0]); uint16_t head = get_u16be(&h[2]); // Ignore sector @@ -105,7 +114,7 @@ bool dfi_format::load(util::random_read &io, uint32_t form_factor, const std::ve data.resize(tsize); pos += 10; // skip the header, we already read it - io.read_at(pos, &data[0], tsize, actual); + std::tie(err, actual) = read_at(io, pos, &data[0], tsize); // FIXME: check for errors and premature EOF pos += tsize; // for next time we read, increment to the beginning of next header int index_time = 0; // what point the last index happened diff --git a/src/lib/formats/dim_dsk.cpp b/src/lib/formats/dim_dsk.cpp index 52d3dfc1566..1f0902c2859 100644 --- a/src/lib/formats/dim_dsk.cpp +++ b/src/lib/formats/dim_dsk.cpp @@ -13,6 +13,8 @@ #include "ioprocs.h" #include <cstring> +#include <tuple> + dim_format::dim_format() { @@ -36,9 +38,9 @@ const char *dim_format::extensions() const noexcept int dim_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t h[16]; - - size_t actual; - io.read_at(0xab, h, 16, actual); + auto const [err, actual] = read_at(io, 0xab, h, 16); + if(err || (16 != actual)) + return 0; if(strncmp((const char *)h, "DIFC HEADER", 11) == 0) return FIFID_SIGN; @@ -48,13 +50,16 @@ int dim_format::identify(util::random_read &io, uint32_t form_factor, const std: bool dim_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { + std::error_condition err; size_t actual; int offset = 0x100; uint8_t h; uint8_t track_total = 77; int cell_count = form_factor == floppy_image::FF_35 ? 200000 : 166666; - io.read_at(0, &h, 1, actual); + std::tie(err, actual) = read_at(io, 0, &h, 1); + if (err || (1 != actual)) + return false; int spt, gap3, bps, size; switch(h) { @@ -114,7 +119,7 @@ bool dim_format::load(util::random_read &io, uint32_t form_factor, const std::ve sects[i].deleted = false; sects[i].bad_crc = false; sects[i].data = §_data[sdatapos]; - io.read_at(offset, sects[i].data, bps, actual); + std::tie(err, actual) = read_at(io, offset, sects[i].data, bps); // FIXME: check for errors and premature EOF offset += bps; sdatapos += bps; } diff --git a/src/lib/formats/dip_dsk.cpp b/src/lib/formats/dip_dsk.cpp index ecbd12bde1e..3ef8261d1da 100644 --- a/src/lib/formats/dip_dsk.cpp +++ b/src/lib/formats/dip_dsk.cpp @@ -72,8 +72,7 @@ bool dip_format::load(util::random_read &io, uint32_t form_factor, const std::ve for (int track = 0; track < tracks; track++) for (int head = 0; head < heads; head++) { - size_t actual; - io.read_at(0x100 + bps * spt * (track * heads + head), sect_data, bps * spt, actual); + /*auto const [err, actual] =*/ read_at(io, 0x100 + bps * spt * (track * heads + head), sect_data, bps * spt); // FIXME: check for errors and premature EOF for (int i = 0; i < spt; i++) { diff --git a/src/lib/formats/dmk_dsk.cpp b/src/lib/formats/dmk_dsk.cpp index 68257ff1f2b..d02401b56cf 100644 --- a/src/lib/formats/dmk_dsk.cpp +++ b/src/lib/formats/dmk_dsk.cpp @@ -8,7 +8,7 @@ TODO: - Add write/format support. -- Check support on other drivers besides msx. +- Check support on other drivers besides MSX. *********************************************************************/ @@ -18,14 +18,18 @@ TODO: #include "ioprocs.h" #include "multibyte.h" +#include <tuple> + namespace { +constexpr int HEADER_SIZE = 16; + uint32_t wide_fm(uint16_t val) { uint32_t res = 0; for (int i = 15; i >= 0; i--) { - res |= (util::BIT(val, i) << (i*2 + 1)); + res |= (util::BIT(val, i) << (i * 2 + 1)); } return res; } @@ -34,7 +38,7 @@ uint32_t data_to_wide_fm(uint8_t val) { uint16_t res = 0xaaaa; // clock for (int i = 7; i >= 0; i--) { - res |= (util::BIT(val, i) << i*2); // data + res |= (util::BIT(val, i) << i * 2); // data } return wide_fm(res); } @@ -72,14 +76,17 @@ int dmk_format::identify(util::random_read &io, uint32_t form_factor, const std: if (io.length(size)) return 0; - const int header_size = 16; - uint8_t header[header_size]; + std::error_condition err; size_t actual; - io.read_at(0, header, header_size, actual); - int tracks = header[1]; - int track_size = get_u16le(&header[2]); - int heads = (header[4] & 0x10) ? 1 : 2; + uint8_t header[HEADER_SIZE]; + std::tie(err, actual) = read_at(io, 0, header, HEADER_SIZE); + if (err || (HEADER_SIZE != actual)) + return 0; + + const int tracks_from_header = header[1]; + const int track_size = get_u16le(&header[2]); + const int heads = util::BIT(header[4], 4) ? 1 : 2; // The first header byte must be 00 or FF if (header[0] != 0x00 && header[0] != 0xff) @@ -87,66 +94,73 @@ int dmk_format::identify(util::random_read &io, uint32_t form_factor, const std: return 0; } - // Bytes C-F must be zero - if (header[0x0c] != 0 || header[0xd] != 0 || header[0xe] != 0 || header[0xf] != 0) + // Verify reserved/unsupported header bytes + for (int i = 5; i < 0x10; i++) { - return 0; + if (header[i] != 0x00) + return 0; } // Check track size within limits - if (track_size < 0x80 || track_size > 0x3FFF ) - { + if (track_size < 0x80 || track_size > 0x3fff) return 0; - } - if (size == header_size + heads * tracks * track_size) + const int tracks_in_file = (size - HEADER_SIZE) / (heads * track_size); + for (int track = 0; track < tracks_in_file; track++) { - return FIFID_STRUCT|FIFID_SIZE; - } + for (int head = 0; head < heads; head++) + { + // Read track + std::vector<uint8_t> track_data(track_size); + std::tie(err, actual) = read_at(io, HEADER_SIZE + (heads * track + head) * track_size, &track_data[0], track_size); + if (err || track_size != actual) + return 0; - return 0; + // Verify idam entries + for (int idam_index = 0; idam_index < 64; idam_index++) + { + const uint16_t idam_entry = get_u16le(&track_data[2 * idam_index]); + if (idam_entry == 0x0000) + continue; + const uint16_t idam_offset = idam_entry & 0x3fff; + if (idam_offset >= track_size) + return 0; + if (track_data[idam_offset] != 0xfe) + return 0; + } + } + } + if (size == HEADER_SIZE + heads * tracks_from_header * track_size) + return FIFID_HINT|FIFID_SIZE; + else + return FIFID_HINT; } bool dmk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { + std::error_condition err; size_t actual; - const int header_size = 16; - uint8_t header[header_size]; - io.read_at(0, header, header_size, actual); + uint64_t size; + if (io.length(size)) + return false; + + uint8_t header[HEADER_SIZE]; + std::tie(err, actual) = read_at(io, 0, header, HEADER_SIZE); + if (err || (HEADER_SIZE != actual)) + return false; - const int tracks = header[1]; const int track_size = get_u16le(&header[2]); - const int heads = (header[4] & 0x10) ? 1 : 2; - const bool is_sd = (header[4] & 0x40) ? true : false; + const int heads = util::BIT(header[4], 4) ? 1 : 2; + const bool is_sd = util::BIT(header[4], 6); + const int tracks = (size - HEADER_SIZE) / (heads * track_size); - auto variant = floppy_image::SSDD; - if (is_sd) - { - if (heads == 2) - { - variant = floppy_image::DSSD; - } - else - { - variant = floppy_image::SSSD; - } - } - else - { - if (heads == 2) - { - variant = floppy_image::DSDD; - } - else - { - variant = floppy_image::SSDD; - } - } + const auto variant = is_sd ? (heads == 2 ? floppy_image::DSSD : floppy_image::SSSD) + : (heads == 2 ? floppy_image::DSDD : floppy_image::SSDD); image.set_variant(variant); - int fm_stride = is_sd ? 1 : 2; + const int fm_stride = is_sd ? 1 : 2; for (int track = 0; track < tracks; track++) { @@ -155,15 +169,17 @@ bool dmk_format::load(util::random_read &io, uint32_t form_factor, const std::ve int fm_loss = 0; std::vector<uint8_t> track_data(track_size); std::vector<uint32_t> raw_track_data; - int mark_location[64*2+1]; - uint8_t mark_value[64*2+1]; - bool mark_is_mfm[64*2+1]; + int mark_location[64 * 2 + 1]; + uint8_t mark_value[64 * 2 + 1]; + bool mark_is_mfm[64 * 2 + 1]; int iam_location = -1; // Read track - io.read_at(header_size + (heads * track + head) * track_size, &track_data[0], track_size, actual); + std::tie(err, actual) = read_at(io, HEADER_SIZE + (heads * track + head) * track_size, &track_data[0], track_size); + if (err || track_size != actual) + return false; - for (int i = 0; i < 64*2+1; i++) + for (int i = 0; i < 64 * 2 + 1; i++) { mark_location[i] = -1; mark_value[i] = 0xfe; @@ -174,10 +190,10 @@ bool dmk_format::load(util::random_read &io, uint32_t form_factor, const std::ve // Find IDAM/DAM locations uint16_t track_header_offset = 0; uint16_t track_offset = get_u16le(&track_data[track_header_offset]) & 0x3fff; - bool idam_is_mfm = (track_data[track_header_offset + 1] & 0x80) ? true : false; + bool idam_is_mfm = util::BIT(track_data[track_header_offset + 1], 7); track_header_offset += 2; - while ( track_offset != 0 && track_offset >= 0x83 && track_offset < track_size && track_header_offset < 0x80 ) + while (track_offset != 0 && track_offset >= 0x83 && track_offset < track_size && track_header_offset < 0x80) { // Assume 3 bytes before IDAM pointers are the start of IDAM indicators int mark_offset = idam_is_mfm ? 3 : 0; @@ -188,11 +204,11 @@ bool dmk_format::load(util::random_read &io, uint32_t form_factor, const std::ve int stride = idam_is_mfm ? 1 : fm_stride; // Scan for DAM location - for (int i = track_offset + 10*stride; i < track_offset + 53*stride; i++) + for (int i = track_offset + 10 * stride; i < track_offset + 53 * stride; i++) { if ((track_data[i] >= 0xf8 && track_data[i] <= 0xfb)) { - if (!idam_is_mfm || (track_data[i-1] == 0xa1 && track_data[i-2] == 0xa1)) + if (!idam_is_mfm || get_u16le(&track_data[i - 2]) == 0xa1a1) { mark_location[mark_count] = i - mark_offset; mark_value[mark_count] = track_data[i]; @@ -204,7 +220,7 @@ bool dmk_format::load(util::random_read &io, uint32_t form_factor, const std::ve } } - idam_is_mfm = (track_data[track_header_offset + 1] & 0x80) ? true : false; + idam_is_mfm = util::BIT(track_data[track_header_offset + 1], 7); track_offset = get_u16le(&track_data[track_header_offset]) & 0x3fff; track_header_offset += 2; } @@ -216,10 +232,10 @@ bool dmk_format::load(util::random_read &io, uint32_t form_factor, const std::ve } // Find IAM location - for(int i = mark_location[0] - 1; i >= 3; i--) + for (int i = mark_location[0] - 1; i >= 3; i--) { // It's usually 3 bytes but several dumped tracks seem to contain only 2 bytes - if (track_data[i] == 0xfc && (is_sd || (track_data[i-1] == 0xc2 && track_data[i-2] == 0xc2))) + if (track_data[i] == 0xfc && (is_sd || get_u16le(&track_data[i - 2]) == 0xc2c2)) { iam_location = i - (is_sd ? 0 : 3); break; diff --git a/src/lib/formats/ds9_dsk.cpp b/src/lib/formats/ds9_dsk.cpp index 396a6ae3235..c0cbf0dccbc 100644 --- a/src/lib/formats/ds9_dsk.cpp +++ b/src/lib/formats/ds9_dsk.cpp @@ -109,8 +109,7 @@ bool ds9_format::load(util::random_read &io, uint32_t form_factor, const std::ve { for (int head = 0; head < head_count; head++) { - size_t actual; - io.read_at((track * head_count + head) * track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ read_at(io, (track * head_count + head) * track_size, sectdata, track_size); // FIXME: check for errors and premature EOF generate_track(ds9_desc, track, head, sectors, sector_count, 104000, image); } } diff --git a/src/lib/formats/dsk_dsk.cpp b/src/lib/formats/dsk_dsk.cpp index adfbe7735ed..e10e94f80be 100644 --- a/src/lib/formats/dsk_dsk.cpp +++ b/src/lib/formats/dsk_dsk.cpp @@ -311,13 +311,14 @@ bool dsk_format::supports_save() const noexcept int dsk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t header[16]; - - size_t actual; - io.read_at(0, &header, sizeof(header), actual); - if ( memcmp( header, DSK_FORMAT_HEADER, 8 ) ==0) { + auto const [err, actual] = read_at(io, 0, &header, sizeof(header)); + if (err) { + return 0; + } + if ((8 <= actual) && !memcmp(header, DSK_FORMAT_HEADER, 8)) { return FIFID_SIGN; } - if ( memcmp( header, EXT_FORMAT_HEADER, 16 ) ==0) { + if ((16 <= actual) && !memcmp(header, EXT_FORMAT_HEADER, 16)) { return FIFID_SIGN; } return 0; @@ -356,8 +357,6 @@ struct sector_header bool dsk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { - size_t actual; - uint8_t header[0x100]; bool extendformat = false; @@ -365,7 +364,7 @@ bool dsk_format::load(util::random_read &io, uint32_t form_factor, const std::ve if (io.length(image_size)) return false; - io.read_at(0, &header, sizeof(header), actual); + read_at(io, 0, &header, sizeof(header)); // FIXME: check for errors and premature EOF if ( memcmp( header, EXT_FORMAT_HEADER, 16 ) ==0) { extendformat = true; } @@ -431,7 +430,7 @@ bool dsk_format::load(util::random_read &io, uint32_t form_factor, const std::ve if(track_offsets[(track<<1)+side] >= image_size) continue; track_header tr; - io.read_at(track_offsets[(track<<1)+side], &tr, sizeof(tr), actual); + read_at(io, track_offsets[(track<<1)+side], &tr, sizeof(tr)); // FIXME: check for errors and premature EOF // skip if there are no sectors in this track if (tr.number_of_sector == 0) @@ -441,7 +440,7 @@ bool dsk_format::load(util::random_read &io, uint32_t form_factor, const std::ve int first_sector_code = -1; for(int j=0;j<tr.number_of_sector;j++) { sector_header sector; - io.read_at(track_offsets[(track<<1)+side]+sizeof(tr)+(sizeof(sector)*j), §or, sizeof(sector), actual); + read_at(io, track_offsets[(track<<1)+side]+sizeof(tr)+(sizeof(sector)*j), §or, sizeof(sector)); // FIXME: check for errors and premature EOF if (j == 0) first_sector_code = sector.sector_size_code; @@ -460,7 +459,7 @@ bool dsk_format::load(util::random_read &io, uint32_t form_factor, const std::ve for(int j=0;j<tr.number_of_sector;j++) { sector_header sector; - io.read_at(track_offsets[(track<<1)+side]+sizeof(tr)+(sizeof(sector)*j), §or, sizeof(sector), actual); + read_at(io, track_offsets[(track<<1)+side]+sizeof(tr)+(sizeof(sector)*j), §or, sizeof(sector)); // FIXME: check for errors and premature EOF sects[j].track = sector.track; sects[j].head = sector.side; @@ -483,7 +482,7 @@ bool dsk_format::load(util::random_read &io, uint32_t form_factor, const std::ve if(!(sector.fdc_status_reg1 & 0x04)) { sects[j].data = sect_data + sdatapos; - io.read_at(pos, sects[j].data, sects[j].actual_size, actual); + read_at(io, pos, sects[j].data, sects[j].actual_size); // FIXME: check for errors and premature EOF sdatapos += sects[j].actual_size; } else diff --git a/src/lib/formats/dvk_mx_dsk.cpp b/src/lib/formats/dvk_mx_dsk.cpp index 981bed010e7..c384dc38e00 100644 --- a/src/lib/formats/dvk_mx_dsk.cpp +++ b/src/lib/formats/dvk_mx_dsk.cpp @@ -23,65 +23,45 @@ #include "ioprocs.h" #include "multibyte.h" +constexpr uint32_t ID_PATTERN = 0xaaaaffaf; const floppy_image_format_t::desc_e dvk_mx_format::dvk_mx_new_desc[] = { /* 01 */ { FM, 0x00, 8*2 }, // eight 0x0000 words - /* 03 */ { FM, 0x00, 1 }, - /* 02 */ { FM, 0xf3, 1 }, // word 0x00f3 - /* 05 */ { FM, 0x00, 1 }, - /* 04 */ { TRACK_ID_FM }, // track number word - /* 05 */ { SECTOR_LOOP_START, 0, 10 }, // 11 sectors + /* 02 */ { FM, 0x00, 1 }, + /* 03 */ { FM, 0xf3, 1 }, // word 0x00f3 + /* 04 */ { FM, 0x00, 1 }, + /* 05 */ { TRACK_ID_FM }, // track number word + /* 06 */ { SECTOR_LOOP_START, 0, 10 }, // 11 sectors /* 07 */ { SECTOR_DATA_MX, -1 }, - /* 10 */ { SECTOR_LOOP_END }, - /* 13 */ { FM, 0x83, 1 }, + /* 08 */ { SECTOR_LOOP_END }, + /* 09 */ { FM, 0x83, 1 }, + /* 10 */ { OFFSET_ID_FM }, + /* 11 */ { FM, 0x83, 1 }, /* 12 */ { OFFSET_ID_FM }, - /* 15 */ { FM, 0x83, 1 }, + /* 13 */ { FM, 0x83, 1 }, /* 14 */ { OFFSET_ID_FM }, - /* 17 */ { FM, 0x83, 1 }, - /* 16 */ { OFFSET_ID_FM }, - /* 18 */ { END } + /* 15 */ { END } }; const floppy_image_format_t::desc_e dvk_mx_format::dvk_mx_old_desc[] = { /* 01 */ { FM, 0x00, 30*2 }, - /* 03 */ { FM, 0x00, 1 }, - /* 02 */ { FM, 0xf3, 1 }, // word 0x00f3 - /* 05 */ { FM, 0x00, 1 }, - /* 04 */ { TRACK_ID_FM }, // track number word + /* 02 */ { FM, 0x00, 1 }, + /* 03 */ { FM, 0xf3, 1 }, // word 0x00f3 + /* 04 */ { FM, 0x00, 1 }, + /* 05 */ { TRACK_ID_FM }, // track number word /* 06 */ { SECTOR_LOOP_START, 0, 10 }, // 11 sectors /* 07 */ { SECTOR_DATA_MX, -1 }, - /* 10 */ { SECTOR_LOOP_END }, - /* 13 */ { FM, 0x83, 1 }, - /* 11 */ { FM, 0x01, 1 }, - /* 15 */ { FM, 0x83, 1 }, - /* 14 */ { FM, 0x01, 1 }, - /* 16 */ { END } + /* 08 */ { SECTOR_LOOP_END }, + /* 09 */ { FM, 0x00, 1 }, + /* 10 */ { FM, 0116, 1 }, + /* 11 */ { FM, 0x00, 4*2 }, // four 0x0000 words + /* 12 */ { END } }; dvk_mx_format::dvk_mx_format() { } -const char *dvk_mx_format::name() const noexcept -{ - return "mx"; -} - -const char *dvk_mx_format::description() const noexcept -{ - return "DVK MX: floppy image"; -} - -const char *dvk_mx_format::extensions() const noexcept -{ - return "mx"; -} - -bool dvk_mx_format::supports_save() const noexcept -{ - return false; -} - void dvk_mx_format::find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t §or_count) { uint64_t size; @@ -95,17 +75,17 @@ void dvk_mx_format::find_size(util::random_read &io, uint8_t &track_count, uint8 { case 112640: track_count = 40; - sector_count = 11; + sector_count = MX_SECTORS; head_count = 1; break; case 225280: track_count = 40; - sector_count = 11; + sector_count = MX_SECTORS; head_count = 2; break; case 450560: track_count = 80; - sector_count = 11; + sector_count = MX_SECTORS; head_count = 2; break; default: @@ -123,8 +103,7 @@ int dvk_mx_format::identify(util::random_read &io, uint32_t form_factor, const s if (track_count) { uint8_t sectdata[512]; - size_t actual; - io.read_at(512, sectdata, 512, actual); + /*auto const [err, actual] =*/ read_at(io, 512, sectdata, 512); // FIXME: check for errors and premature EOF // check value in RT-11 home block. see src/tools/imgtool/modules/rt11.cpp if (get_u16le(§data[0724]) == 6) return FIFID_SIGN|FIFID_SIZE; @@ -143,22 +122,21 @@ bool dvk_mx_format::load(util::random_read &io, uint32_t form_factor, const std: find_size(io, track_count, head_count, sector_count); if (track_count == 0) return false; - uint8_t sectdata[11 * 256]; - desc_s sectors[11]; + uint8_t sectdata[MX_SECTORS * MX_SECTOR_SIZE]; + desc_s sectors[MX_SECTORS]; for (int i = 0; i < sector_count; i++) { - sectors[i].data = sectdata + 256 * i; - sectors[i].size = 256; + sectors[i].data = sectdata + MX_SECTOR_SIZE * i; + sectors[i].size = MX_SECTOR_SIZE; sectors[i].sector_id = i; } - int track_size = sector_count * 256; + int track_size = sector_count * MX_SECTOR_SIZE; for (int track = 0; track < track_count; track++) { for (int head = 0; head < head_count; head++) { - size_t actual; - io.read_at((track * head_count + head) * track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ read_at(io, (track * head_count + head) * track_size, sectdata, track_size); // FIXME: check for errors and premature EOF generate_track(dvk_mx_new_desc, track, head, sectors, sector_count, 45824, image); } } @@ -182,4 +160,65 @@ bool dvk_mx_format::load(util::random_read &io, uint32_t form_factor, const std: return true; } +bool dvk_mx_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const +{ + int tracks; + int heads; + bool res = false; + image.get_actual_geometry(tracks, heads); + + for (int cyl = 0; cyl < tracks; cyl++) + { + for (int head = 0; head < heads; head++) + { + auto bitstream = generate_bitstream_from_track(cyl, head, 4000, image, 0); + uint8_t sector_data[MX_SECTORS * MX_SECTOR_SIZE]; + if (get_next_sector(bitstream, sector_data)) + { + unsigned const offset_in_image = (cyl * heads + head) * MX_SECTOR_SIZE * MX_SECTORS; + res = true; + // FIXME: check for errors + /*auto const [err, actual] =*/ write_at(io, offset_in_image, sector_data, MX_SECTOR_SIZE * MX_SECTORS); + } + } + } + return res; +} + +bool dvk_mx_format::get_next_sector(const std::vector<bool> &bitstream, uint8_t *sector_data) +{ + uint32_t sr = 0; + int pos = 0; + while (pos < bitstream.size() && sr != ID_PATTERN) + { + sr = (sr << 1) | bitstream[pos]; + pos++; + } + if (pos == bitstream.size()) + { + // End of track reached + return false; + } + unsigned to_dump = MX_SECTORS * (MX_SECTOR_SIZE + 2); + pos += 32; // skip track ID + for (unsigned i = 0, k = 0; i < to_dump && pos < bitstream.size(); i++) + { + uint8_t byte = 0; + unsigned j; + for (j = 0; j < 8 && pos < bitstream.size(); j++) + { + bool bit = bitstream[pos + 1]; + pos += 2; + byte <<= 1; + byte |= bit; + } + if (j == 8 && (i % (MX_SECTOR_SIZE + 2)) < MX_SECTOR_SIZE) + { + sector_data[k ^ 1] = byte; + k++; + } + } + return true; +} + const dvk_mx_format FLOPPY_DVK_MX_FORMAT; diff --git a/src/lib/formats/dvk_mx_dsk.h b/src/lib/formats/dvk_mx_dsk.h index 83f93fd0878..b0d64deab9f 100644 --- a/src/lib/formats/dvk_mx_dsk.h +++ b/src/lib/formats/dvk_mx_dsk.h @@ -19,17 +19,22 @@ public: virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override; virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override; + virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override; - virtual const char *name() const noexcept override; - virtual const char *description() const noexcept override; - virtual const char *extensions() const noexcept override; - virtual bool supports_save() const noexcept override; + virtual const char *name() const noexcept override { return "mx"; } + virtual const char *description() const noexcept override { return "DVK MX disk image"; } + virtual const char *extensions() const noexcept override { return "mx"; } + virtual bool supports_save() const noexcept override { return true; } static const desc_e dvk_mx_old_desc[]; static const desc_e dvk_mx_new_desc[]; private: + static constexpr unsigned MX_SECTORS = 11; + static constexpr unsigned MX_SECTOR_SIZE = 256; + static void find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t §or_count); + static bool get_next_sector(const std::vector<bool> &bitstream , uint8_t *sector_data); }; extern const dvk_mx_format FLOPPY_DVK_MX_FORMAT; diff --git a/src/lib/formats/esq16_dsk.cpp b/src/lib/formats/esq16_dsk.cpp index a7e181b2cc7..f487e835665 100644 --- a/src/lib/formats/esq16_dsk.cpp +++ b/src/lib/formats/esq16_dsk.cpp @@ -113,8 +113,7 @@ bool esqimg_format::load(util::random_read &io, uint32_t form_factor, const std: int track_size = sector_count*512; for(int track=0; track < track_count; track++) { for(int head=0; head < head_count; head++) { - size_t actual; - io.read_at((track*head_count + head)*track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ read_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors and premature EOF generate_track(esq_10_desc, track, head, sectors, sector_count, 110528, image); } } @@ -145,8 +144,7 @@ bool esqimg_format::save(util::random_read_write &io, const std::vector<uint32_t for(int track=0; track < track_count; track++) { for(int head=0; head < head_count; head++) { get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata); - size_t actual; - io.write_at((track*head_count + head)*track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ write_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors } } diff --git a/src/lib/formats/esq8_dsk.cpp b/src/lib/formats/esq8_dsk.cpp index fb3bdb112d6..b7225d839a9 100644 --- a/src/lib/formats/esq8_dsk.cpp +++ b/src/lib/formats/esq8_dsk.cpp @@ -132,8 +132,7 @@ bool esq8img_format::load(util::random_read &io, uint32_t form_factor, const std { for(int head=0; head < head_count; head++) { - size_t actual; - io.read_at((track*head_count + head)*track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ read_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors and premature EOF generate_track(esq_6_desc, track, head, sectors, sector_count, 109376, image); } } @@ -180,8 +179,7 @@ bool esq8img_format::save(util::random_read_write &io, const std::vector<uint32_ return false; } - size_t actual; - io.write_at(file_offset, sectors[sector].data(), sector_expected_size, actual); + /*auto const [err, actual] =*/ write_at(io, file_offset, sectors[sector].data(), sector_expected_size); // FIXME: check for errors file_offset += sector_expected_size; } } diff --git a/src/lib/formats/fc100_cas.cpp b/src/lib/formats/fc100_cas.cpp index da2247d49b5..d991ce8de6a 100644 --- a/src/lib/formats/fc100_cas.cpp +++ b/src/lib/formats/fc100_cas.cpp @@ -104,7 +104,7 @@ static int fc100_handle_cassette(int16_t *buffer, const uint8_t *bytes) Generate samples for the tape image ********************************************************************/ -static int fc100_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int fc100_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { return fc100_handle_cassette(buffer, bytes); } diff --git a/src/lib/formats/fdd_dsk.cpp b/src/lib/formats/fdd_dsk.cpp index fc0741f2f5a..7519eb593dd 100644 --- a/src/lib/formats/fdd_dsk.cpp +++ b/src/lib/formats/fdd_dsk.cpp @@ -62,10 +62,11 @@ const char *fdd_format::extensions() const noexcept int fdd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t h[7]; - size_t actual; - io.read_at(0, h, 7, actual); + auto const [err, actual] = read_at(io, 0, h, 7); // FIXME: should it really be reading six bytes? also check for premature EOF. + if (err) + return false; - if (strncmp((const char *)h, "VFD1.0", 6) == 0) + if (memcmp(h, "VFD1.0", 6) == 0) return FIFID_SIGN; return 0; @@ -92,8 +93,7 @@ bool fdd_format::load(util::random_read &io, uint32_t form_factor, const std::ve for (int sect = 0; sect < 26; sect++) { // read sector map for this sector - size_t actual; - io.read_at(pos, hsec, 0x0c, actual); + /*auto const [err, actual] =*/ read_at(io, pos, hsec, 0x0c); // FIXME: check for errors and premature EOF pos += 0x0c; if (hsec[0] == 0xff) // unformatted/unused sector @@ -125,11 +125,10 @@ bool fdd_format::load(util::random_read &io, uint32_t form_factor, const std::ve cur_sec_map = track * 26 + i; sector_size = 128 << sec_sizes[cur_sec_map]; - size_t actual; if (sec_offs[cur_sec_map] == 0xffffffff) memset(sect_data + cur_pos, fill_vals[cur_sec_map], sector_size); else - io.read_at(sec_offs[cur_sec_map], sect_data + cur_pos, sector_size, actual); + /*auto const [err, actual] =*/ read_at(io, sec_offs[cur_sec_map], sect_data + cur_pos, sector_size); // FIXME: check for errors and premature EOF sects[i].track = tracks[cur_sec_map]; sects[i].head = heads[cur_sec_map]; diff --git a/src/lib/formats/fdos_dsk.cpp b/src/lib/formats/fdos_dsk.cpp index 2980376160e..138ad986011 100644 --- a/src/lib/formats/fdos_dsk.cpp +++ b/src/lib/formats/fdos_dsk.cpp @@ -39,9 +39,12 @@ #include "fdos_dsk.h" #include "imageutl.h" -#include "multibyte.h" #include "ioprocs.h" +#include "multibyte.h" + +#include <tuple> + namespace { @@ -116,7 +119,7 @@ int fdos_format::find_size(util::random_read &io, uint32_t form_factor, const st // 00330 2403 DE OB RESTRT LDX PROGX // Should be $BD and $DE respectively // There could be additional variations - ec = io.read_at(0, &boot0, f.sector_base_size, actual); + std::tie(ec, actual) = read_at(io, 0, &boot0, f.sector_base_size); if (ec || actual != f.sector_base_size) return -1; if (boot0[0] != 0xbd && boot0[3] != 0xde) @@ -129,7 +132,7 @@ int fdos_format::find_size(util::random_read &io, uint32_t form_factor, const st form_factor); // Directory entries start at Track 2 Sector 0 - ec = io.read_at(2 * f.sector_count * f.sector_base_size, &info, sizeof(struct fdos_formats::dirent_entry_fdos), actual); + std::tie(ec, actual) = read_at(io, 2 * f.sector_count * f.sector_base_size, &info, sizeof(struct fdos_formats::dirent_entry_fdos)); if (ec || actual != sizeof(struct fdos_formats::dirent_entry_fdos)) continue; diff --git a/src/lib/formats/flacfile.cpp b/src/lib/formats/flacfile.cpp index f3ccb676e52..c594a809a8e 100644 --- a/src/lib/formats/flacfile.cpp +++ b/src/lib/formats/flacfile.cpp @@ -16,10 +16,27 @@ #include <new> -static constexpr int MAX_CHANNELS = 8; +namespace { +constexpr int MAX_CHANNELS = 8; -static cassette_image::error flacfile_identify(cassette_image *cassette, cassette_image::Options *opts) + +// Copied from cassimg.cpp; put somewhere central? +/********************************************************************* + helper code +*********************************************************************/ +constexpr double map_double(double d, uint64_t low, uint64_t high, uint64_t value) +{ + return d * (value - low) / (high - low); +} + +constexpr size_t waveform_bytes_per_sample(int waveform_flags) +{ + return size_t(1 << ((waveform_flags & 0x06) / 2)); +} + + +cassette_image::error flacfile_identify(cassette_image *cassette, cassette_image::Options *opts) { cassette->get_raw_cassette_image()->seek(0, SEEK_SET); flac_decoder decoder(*cassette->get_raw_cassette_image()); @@ -42,7 +59,7 @@ static cassette_image::error flacfile_identify(cassette_image *cassette, cassett } -static cassette_image::error flacfile_load(cassette_image *cassette) +cassette_image::error flacfile_load(cassette_image *cassette) { cassette->get_raw_cassette_image()->seek(0, SEEK_SET); flac_decoder decoder(*cassette->get_raw_cassette_image()); @@ -73,10 +90,63 @@ static cassette_image::error flacfile_load(cassette_image *cassette) } +cassette_image::error flacfile_save(cassette_image *cassette, const cassette_image::Info *info) +{ + if (info->channels > MAX_CHANNELS) + return cassette_image::error::INVALID_IMAGE; + + cassette->get_raw_cassette_image()->seek(0, SEEK_SET); + flac_encoder encoder; + encoder.set_num_channels(info->channels); + encoder.set_sample_rate(info->sample_frequency); + if (!encoder.reset(*cassette->get_raw_cassette_image())) + return cassette_image::error::INTERNAL; + + size_t samples_saved = 0; + int16_t buffer[MAX_CHANNELS][4096]; + int16_t *buffer_ptr[MAX_CHANNELS]; + const size_t bytes_per_sample = waveform_bytes_per_sample(cassette_image::WAVEFORM_16BIT); + const size_t sample_spacing = bytes_per_sample * info->channels; + const double sample_period = info->sample_count / (double) info->sample_frequency; + + for (int channel = 0; channel < MAX_CHANNELS; channel++) + { + buffer_ptr[channel] = &buffer[channel][0]; + } + + while (samples_saved < info->sample_count) + { + size_t chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (info->sample_count - samples_saved)); + double chunk_sample_period = map_double(sample_period, 0, info->sample_count, chunk_sample_count); + double chunk_time_index = map_double(sample_period, 0, info->sample_count, samples_saved); + + for (int channel = 0; channel < info->channels; channel++) + { + cassette_image::error err = cassette->get_samples(channel, chunk_time_index, chunk_sample_period, + chunk_sample_count, sample_spacing, &buffer[channel * bytes_per_sample], cassette_image::WAVEFORM_16BIT); + + if (err != cassette_image::error::SUCCESS) + return err; + } + + if (!encoder.encode(buffer_ptr, chunk_sample_count)) + return cassette_image::error::INTERNAL; + + samples_saved += chunk_sample_count; + } + + encoder.finish(); + + return cassette_image::error::SUCCESS; +} + +} // anonymous namespace + + const cassette_image::Format cassette_image::flacfile_format = { "flac", flacfile_identify, flacfile_load, - nullptr + flacfile_save }; diff --git a/src/lib/formats/flex_dsk.cpp b/src/lib/formats/flex_dsk.cpp index 778e6c990b9..11a72963471 100644 --- a/src/lib/formats/flex_dsk.cpp +++ b/src/lib/formats/flex_dsk.cpp @@ -91,12 +91,11 @@ int flex_format::find_size(util::random_read &io, uint32_t form_factor, const st return -1; uint8_t boot0[256], boot1[256]; - size_t actual; // Look at the boot sector. // Density, sides, link?? - io.read_at(256 * 0, &boot0, sizeof(boot0), actual); - io.read_at(256 * 1, &boot1, sizeof(boot1), actual); + read_at(io, 256 * 0, &boot0, sizeof(boot0)); // FIXME: check for errors and premature EOF + read_at(io, 256 * 1, &boot1, sizeof(boot1)); // FIXME: check for errors and premature EOF LOG_FORMATS("FLEX floppy dsk: size %d bytes, %d total sectors, %d remaining bytes, expected form factor %x\n", (uint32_t)size, (uint32_t)size / 256, (uint32_t)size % 256, form_factor); @@ -129,7 +128,7 @@ int flex_format::find_size(util::random_read &io, uint32_t form_factor, const st if (f.sector_base_size == 128) { // FLEX 1.0: Look at the system information sector. sysinfo_sector_flex10 info; - io.read_at(f.sector_base_size * 2, &info, sizeof(struct sysinfo_sector_flex10), actual); + read_at(io, f.sector_base_size * 2, &info, sizeof(struct sysinfo_sector_flex10)); // FIXME: check for errors and premature EOF LOG_FORMATS("FLEX floppy dsk: size %d bytes, %d total sectors, %d remaining bytes, expected form factor %x\n", (uint32_t)size, (uint32_t)size / f.sector_base_size, (uint32_t)size % f.sector_base_size, form_factor); @@ -142,7 +141,7 @@ int flex_format::find_size(util::random_read &io, uint32_t form_factor, const st } else { // FLEX 2+: Look at the system information sector. sysinfo_sector info; - io.read_at(f.sector_base_size * 2, &info, sizeof(struct sysinfo_sector), actual); + read_at(io, f.sector_base_size * 2, &info, sizeof(struct sysinfo_sector)); // FIXME: check for errors and premature EOF LOG_FORMATS("FLEX floppy dsk: size %d bytes, %d total sectors, %d remaining bytes, expected form factor %x\n", (uint32_t)size, (uint32_t)size / f.sector_base_size, (uint32_t)size % f.sector_base_size, form_factor); diff --git a/src/lib/formats/flopimg.cpp b/src/lib/formats/flopimg.cpp index ec978817a6e..fa4903447fb 100644 --- a/src/lib/formats/flopimg.cpp +++ b/src/lib/formats/flopimg.cpp @@ -39,6 +39,39 @@ floppy_image::~floppy_image() { } +void floppy_image::set_variant(uint32_t _variant) +{ + variant = _variant; + + // Initialize hard sectors + index_array.clear(); + + uint32_t sectors; + switch(variant) { + case SSDD16: + case SSQD16: + case DSDD16: + case DSQD16: + sectors = 16; + break; + default: + sectors = 0; + } + if(sectors) { + uint32_t sector_angle = 200000000/sectors; + for(int i = 1; i < sectors; i++) + index_array.push_back(i*sector_angle); + index_array.push_back((sectors-1)*sector_angle + sector_angle/2); + } +} + +void floppy_image::find_index_hole(uint32_t pos, uint32_t &last, uint32_t &next) const +{ + auto nexti = std::lower_bound(index_array.begin(), index_array.end(), pos+1); + next = nexti == index_array.end() ? 200000000 : *nexti; + last = nexti == index_array.begin() ? 0 : *--nexti; +} + void floppy_image::get_maximal_geometry(int &_tracks, int &_heads) const noexcept { _tracks = tracks; @@ -104,13 +137,30 @@ bool floppy_image::track_is_formatted(int track, int head, int subtrack) const n const char *floppy_image::get_variant_name(uint32_t form_factor, uint32_t variant) noexcept { switch(variant) { - case SSSD: return "Single side, single density"; - case SSDD: return "Single side, double density"; - case SSQD: return "Single side, quad density"; - case DSDD: return "Double side, double density"; - case DSQD: return "Double side, quad density"; - case DSHD: return "Double side, high density"; - case DSED: return "Double side, extended density"; + case SSSD: return "Single side, single density"; + case SSSD10: return "Single side, single density, 10-sector"; + case SSSD16: return "Single side, single density, 16-sector"; + case SSSD32: return "Single side, single density, 32-sector"; + case SSDD: return "Single side, double density"; + case SSDD10: return "Single side, double density, 10-sector"; + case SSDD16: return "Single side, double density, 16 hard sector"; + case SSDD32: return "Single side, double density, 32-sector"; + case SSQD: return "Single side, quad density"; + case SSQD10: return "Single side, quad density, 10-sector"; + case SSQD16: return "Single side, quad density, 16 hard sector"; + case DSSD: return "Double side, single density"; + case DSSD10: return "Double side, single density, 10-sector"; + case DSSD16: return "Double side, single density, 16-sector"; + case DSSD32: return "Double side, single density, 32-sector"; + case DSDD: return "Double side, double density"; + case DSDD10: return "Double side, double density, 10-sector"; + case DSDD16: return "Double side, double density, 16 hard sector"; + case DSDD32: return "Double side, double density, 32-sector"; + case DSQD: return "Double side, quad density"; + case DSQD10: return "Double side, quad density, 10-sector"; + case DSQD16: return "Double side, quad density, 16 hard sector"; + case DSHD: return "Double side, high density"; + case DSED: return "Double side, extended density"; } return "Unknown"; } @@ -864,7 +914,7 @@ void floppy_image_format_t::generate_track_from_bitstream(int track, int head, c normalize_times(dest, track_size*2); - if(splice >= 0 || splice < track_size) { + if(splice >= 0 && splice < track_size) { int splpos = uint64_t(200000000) * splice / track_size; image.set_write_splice_position(track, head, splpos, subtrack); } diff --git a/src/lib/formats/flopimg.h b/src/lib/formats/flopimg.h index 4098ab3d418..18cc17ea73b 100644 --- a/src/lib/formats/flopimg.h +++ b/src/lib/formats/flopimg.h @@ -525,14 +525,30 @@ public: //! Variants enum { - SSSD = 0x44535353, //!< "SSSD", Single-sided single-density - SSDD = 0x44445353, //!< "SSDD", Single-sided double-density - SSQD = 0x44515353, //!< "SSQD", Single-sided quad-density - DSSD = 0x44535344, //!< "DSSD", Double-sided single-density - DSDD = 0x44445344, //!< "DSDD", Double-sided double-density (720K in 3.5, 360K in 5.25) - DSQD = 0x44515344, //!< "DSQD", Double-sided quad-density (720K in 5.25, means DD+80 tracks) - DSHD = 0x44485344, //!< "DSHD", Double-sided high-density (1440K) - DSED = 0x44455344 //!< "DSED", Double-sided extra-density (2880K) + SSSD = 0x44535353, //!< "SSSD", Single-sided single-density + SSSD10 = 0x30315353, //!< "SS10", Single-sided single-density 10 hard sector + SSSD16 = 0x36315353, //!< "SS16", Single-sided single-density 16 hard sector + SSSD32 = 0x32335353, //!< "SS32", Single-sided single-density 32 hard sector + SSDD = 0x44445353, //!< "SSDD", Single-sided double-density + SSDD10 = 0x30314453, //!< "SD10", Single-sided double-density 10 hard sector + SSDD16 = 0x36314453, //!< "SD16", Single-sided double-density 16 hard sector + SSDD32 = 0x32334453, //!< "SD32", Single-sided double-density 32 hard sector + SSQD = 0x44515353, //!< "SSQD", Single-sided quad-density + SSQD10 = 0x30315153, //!< "SQ10", Single-sided quad-density 10 hard sector + SSQD16 = 0x36315153, //!< "SQ16", Single-sided quad-density 16 hard sector + DSSD = 0x44535344, //!< "DSSD", Double-sided single-density + DSSD10 = 0x30315344, //!< "DS10", Double-sided single-density 10 hard sector + DSSD16 = 0x36315344, //!< "DS16", Double-sided single-density 16 hard sector + DSSD32 = 0x32335344, //!< "DS32", Double-sided single-density 32 hard sector + DSDD = 0x44445344, //!< "DSDD", Double-sided double-density (720K in 3.5, 360K in 5.25) + DSDD10 = 0x30314444, //!< "DD10", Double-sided double-density 10 hard sector + DSDD16 = 0x36314444, //!< "DD16", Double-sided double-density 16 hard sector (360K in 5.25) + DSDD32 = 0x32334444, //!< "DD32", Double-sided double-density 32 hard sector + DSQD = 0x44515344, //!< "DSQD", Double-sided quad-density (720K in 5.25, means DD+80 tracks) + DSQD10 = 0x30315144, //!< "DQ10", Double-sided quad-density 10 hard sector + DSQD16 = 0x36315144, //!< "DQ16", Double-sided quad-density 16 hard sector (720K in 5.25, means DD+80 tracks) + DSHD = 0x44485344, //!< "DSHD", Double-sided high-density (1440K) + DSED = 0x44455344 //!< "DSED", Double-sided extra-density (2880K) }; //! Encodings @@ -542,6 +558,14 @@ public: M2FM = 0x4D32464D //!< "M2FM", modified modified frequency modulation }; + //! Sectoring + enum { + SOFT = 0x54464F53, //!< "SOFT", Soft-sectored + H10 = 0x20303148, //!< "H10 ", Hard 10-sectored + H16 = 0x20363148, //!< "H16 ", Hard 16-sectored + H32 = 0x20323348 //!< "H32 ", Hard 32-sectored (8 inch disk) + }; + // construction/destruction @@ -558,10 +582,25 @@ public: uint32_t get_form_factor() const noexcept { return form_factor; } //! @return the variant. uint32_t get_variant() const noexcept { return variant; } + //! @return the disk sectoring. + uint32_t get_sectoring() const noexcept { return sectoring; } //! @param v the variant. - void set_variant(uint32_t v) { variant = v; } + void set_variant(uint32_t v); //! @param v the variant. - void set_form_variant(uint32_t f, uint32_t v) { if(form_factor == FF_UNKNOWN) form_factor = f; variant = v; } + void set_form_variant(uint32_t f, uint32_t v) { if(form_factor == FF_UNKNOWN) form_factor = f; set_variant(v); } + //! @param s the sectoring. + void set_sectoring(uint32_t s) { sectoring = s; } + + //! Find most recent and next index hole for provided angular position. + //! The most recent hole may be equal to provided position. The next + //! hole will be 200000000 if all holes of the current rotation are in + //! the past. + + /*! @param pos angular position + @param last most recent index hole + @param next next index hole + */ + void find_index_hole(uint32_t pos, uint32_t &last, uint32_t &next) const; /*! @param track @@ -608,7 +647,7 @@ public: private: int tracks, heads; - uint32_t form_factor, variant; + uint32_t form_factor, variant, sectoring; struct track_info { @@ -621,6 +660,14 @@ private: // track number multiplied by 4 then head // last array size may be bigger than actual track size std::vector<std::vector<track_info> > track_array; + + // Additional index holes in increasing order. Entries are absolute + // positions of index holes in the same units as cell_data. The + // positions are the start of the hole, not the center of the hole. The + // hole at angular position 0 is implicit, so an empty list encodes a + // regular soft-sectored disk. Additional holes are found on + // hard-sectored disks. + std::vector<uint32_t> index_array; }; #endif // MAME_FORMATS_FLOPIMG_H diff --git a/src/lib/formats/flopimg_legacy.cpp b/src/lib/formats/flopimg_legacy.cpp index 5b8aee0a72b..c209b7cab58 100644 --- a/src/lib/formats/flopimg_legacy.cpp +++ b/src/lib/formats/flopimg_legacy.cpp @@ -349,16 +349,14 @@ util::random_read_write &floppy_get_io(floppy_image_legacy *floppy) void floppy_image_read(floppy_image_legacy *floppy, void *buffer, uint64_t offset, size_t length) { - size_t actual; - floppy->io->read_at(offset, buffer, length, actual); + /*auto const [err, actual] =*/ read_at(*floppy->io, offset, buffer, length); // FIXME: check for errors and premature EOF } void floppy_image_write(floppy_image_legacy *floppy, const void *buffer, uint64_t offset, size_t length) { - size_t actual; - floppy->io->write_at(offset, buffer, length, actual); + /*auto const [err, actual] =*/ write_at(*floppy->io, offset, buffer, length); // FIXME: check for errors } @@ -371,8 +369,7 @@ void floppy_image_write_filler(floppy_image_legacy *floppy, uint8_t filler, uint while (length) { size_t const block = std::min(sizeof(buffer), length); - size_t actual; - floppy->io->write_at(offset, buffer, block, actual); + /*auto const [err, actual] =*/ write_at(*floppy->io, offset, buffer, block); // FIXME: check for errors offset += block; length -= block; } diff --git a/src/lib/formats/fm7_cas.cpp b/src/lib/formats/fm7_cas.cpp index 54a5095547a..25953627361 100644 --- a/src/lib/formats/fm7_cas.cpp +++ b/src/lib/formats/fm7_cas.cpp @@ -72,7 +72,7 @@ static int fm7_cas_to_wav_size (const uint8_t *casdata, int caslen) /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int fm7_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int fm7_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { return fm7_handle_t77(buffer,bytes); } diff --git a/src/lib/formats/fmsx_cas.cpp b/src/lib/formats/fmsx_cas.cpp index b6642568b55..c7969b28eca 100644 --- a/src/lib/formats/fmsx_cas.cpp +++ b/src/lib/formats/fmsx_cas.cpp @@ -49,7 +49,7 @@ static int fmsx_cas_to_wav_size (const uint8_t *casdata, int caslen) /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int fmsx_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int fmsx_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { int cas_pos, bit, state = 1, samples_pos, size, n, i, p; diff --git a/src/lib/formats/fs_cbmdos.cpp b/src/lib/formats/fs_cbmdos.cpp index 51d1369b04f..44c810a04a6 100644 --- a/src/lib/formats/fs_cbmdos.cpp +++ b/src/lib/formats/fs_cbmdos.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Nathan Woods +// copyright-holders:Nathan Woods, Wilbert Pol /*************************************************************************** fs_cbmdos.cpp @@ -8,21 +8,30 @@ http://fileformats.archiveteam.org/wiki/CBMFS +Current limitations: +- Writing is limited to the first 35 tracks. +- Determine or select the file type. Currently defaulting to PRG; 0 byte files + will be assigned file type DEL. + ***************************************************************************/ #include "fs_cbmdos.h" + #include "d64_dsk.h" #include "fsblk.h" #include "corestr.h" +#include "multibyte.h" #include "strformat.h" #include <array> #include <optional> +#include <regex> #include <set> #include <string_view> #include <tuple> + namespace fs { const cbmdos_image CBMDOS; }; @@ -33,6 +42,8 @@ namespace { class impl : public filesystem_t { public: + static constexpr u8 SECTOR_DIRECTORY_COUNT = 8; + struct cbmdos_dirent { u8 m_next_directory_track; @@ -55,30 +66,74 @@ public: block_iterator(const impl &fs, u8 first_track, u8 first_sector); bool next(); const void *data() const; - const std::array<cbmdos_dirent, 4> &dirent_data() const; + const std::array<cbmdos_dirent, SECTOR_DIRECTORY_COUNT> &dirent_data() const; u8 size() const; + u8 track() const { return m_track; } + u8 sector() const { return m_sector; } private: const impl & m_fs; - fsblk_t::block_t m_block; + fsblk_t::block_t::ptr m_block; std::set<std::tuple<u8, u8>> m_visited_set; u8 m_track; u8 m_sector; + u8 m_next_track; + u8 m_next_sector; }; impl(fsblk_t &blockdev); virtual ~impl() = default; virtual meta_data volume_metadata() override; - virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override; + virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override; private: - fsblk_t::block_t read_sector(int track, int sector) const; + static constexpr u32 BLOCK_SIZE = 256; + static constexpr u8 DIRECTORY_ENTRY_SIZE = 0x20; + static constexpr u8 FILE_TYPE_DEL = 0x80; + static constexpr u8 FILE_TYPE_SEQ = 0x81; + static constexpr u8 FILE_TYPE_PRG = 0x82; + static constexpr u8 FILE_TYPE_USR = 0x83; + static constexpr u8 FILE_TYPE_REL = 0x84; + static constexpr u8 TRACK_VARIANTS = 5; + static constexpr u8 MAX_SECTORS = 21; + static constexpr u8 MAX_TRACKS = 40; + static constexpr u8 DIRECTORY_TRACK = 18; + static constexpr u8 BAM_SECTOR = 0; + static constexpr u8 FIRST_DIRECTORY_SECTOR = 1; + static constexpr u8 SECTOR_DATA_BYTES = 254; + static constexpr u8 OFFSET_FILE_TYPE = 0x02; + static constexpr u8 OFFSET_FILE_FIRST_TRACK = 0x03; + static constexpr u8 OFFSET_FILE_FIRST_SECTOR = 0x04; + static constexpr u8 OFFSET_FILE_NAME = 0x05; + static constexpr u8 OFFSET_SECTOR_COUNT = 0x1e; + static constexpr u8 OFFSET_CHAIN_TRACK = 0x00; + static constexpr u8 OFFSET_CHAIN_SECTOR = 0x01; + static constexpr u8 CHAIN_END = 0x00; + + static const struct smap + { + u8 first_track; + u8 last_track; + int sector[MAX_SECTORS]; + } s_track_sector_map[TRACK_VARIANTS]; + static const u8 s_data_track_order[MAX_TRACKS - 1]; + u8 m_max_track; + + fsblk_t::block_t::ptr read_sector(int track, int sector) const; std::optional<cbmdos_dirent> dirent_from_path(const std::vector<std::string> &path) const; - void iterate_directory_entries(const std::function<bool(const cbmdos_dirent &dirent)> &callback) const; + void iterate_directory_entries(const std::function<bool(u8 track, u8 sector, u8 file_index, const cbmdos_dirent &dirent)> &callback) const; + void iterate_all_directory_entries(const std::function<bool(u8 track, u8 sector, u8 file_index, const cbmdos_dirent &dirent)> &callback) const; meta_data metadata_from_dirent(const cbmdos_dirent &dirent) const; + bool is_valid_filename(const std::string &filename) const; + std::pair<std::error_condition, u8> claim_track_sector(u8 track) const; + std::tuple<std::error_condition, u8, u8> claim_sector() const; + std::error_condition free_sector(u8 track, u8 sector) const; + u8 determine_file_type(const std::vector<u8> &data) const; }; // methods @@ -145,7 +200,7 @@ bool fs::cbmdos_image::can_read() const bool fs::cbmdos_image::can_write() const { - return false; + return true; } @@ -223,8 +278,22 @@ std::string_view strtrimright_cbm(const char (&str)[N]) // impl ctor //------------------------------------------------- +const u8 impl::s_data_track_order[MAX_TRACKS - 1] = { + 17, 19, 16, 20, 15, 21, 14, 22, 13, 23, 12, 24, 11, 25, 10, 26, 9, 27, 8, 28, 7, 29, 6, 30, 5, 31, 4, 32, 3, 33, 2, 34, 1, 35, 36, 37, 38, 39, 40 +}; + +const impl::smap impl::s_track_sector_map[TRACK_VARIANTS] = { + { 1, 17, { 0, 11, 1, 12, 2, 13, 3, 14, 4, 15, 5, 16, 6, 17, 7, 18, 8, 19, 9, 20, 10 } }, + { 18, 18, { 0, 1, 4, 7, 10, 13, 16, 2, 5, 8, 11, 14, 17, 3, 6, 9, 12, 15, 18, -1, -1 } }, + { 19, 24, { 0, 10, 1, 11, 2, 12, 3, 13, 4, 14, 5, 15, 6, 16, 7, 17, 8, 18, 9, -1, -1 } }, + { 25, 30, { 0, 9, 1, 10, 2, 11, 3, 12, 4, 13, 5, 14, 6, 15, 7, 16, 8, 17, -1, -1, -1 } }, + { 31, 40, { 0, 9, 1, 10, 2, 11, 3, 12, 4, 13, 5, 14, 6, 15, 7, 16, 8, -1, -1, -1, -1 } } +}; + + impl::impl(fsblk_t &blockdev) - : filesystem_t(blockdev, 256) + : filesystem_t(blockdev, BLOCK_SIZE) + , m_max_track(35) { } @@ -235,8 +304,8 @@ impl::impl(fsblk_t &blockdev) meta_data impl::volume_metadata() { - auto bam_block = read_sector(18, 0); - std::string_view disk_name = std::string_view((const char *) bam_block.rodata() + 0x90, 16); + auto bam_block = read_sector(DIRECTORY_TRACK, BAM_SECTOR); + std::string_view disk_name = bam_block->rstr(0x90, 16); meta_data results; results.set(meta_name::name, strtrimright_cbm(disk_name)); @@ -248,13 +317,13 @@ meta_data impl::volume_metadata() // impl::metadata //------------------------------------------------- -std::pair<err_t, meta_data> impl::metadata(const std::vector<std::string> &path) +std::pair<std::error_condition, meta_data> impl::metadata(const std::vector<std::string> &path) { std::optional<cbmdos_dirent> dirent = dirent_from_path(path); if (!dirent) - return std::make_pair(ERR_NOT_FOUND, meta_data()); + return std::make_pair(error::not_found, meta_data()); - return std::make_pair(ERR_OK, metadata_from_dirent(*dirent)); + return std::make_pair(std::error_condition(), metadata_from_dirent(*dirent)); } @@ -262,16 +331,16 @@ std::pair<err_t, meta_data> impl::metadata(const std::vector<std::string> &path) // impl::directory_contents //------------------------------------------------- -std::pair<err_t, std::vector<dir_entry>> impl::directory_contents(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<dir_entry>> impl::directory_contents(const std::vector<std::string> &path) { std::vector<dir_entry> results; - auto callback = [this, &results](const cbmdos_dirent &ent) + auto callback = [this, &results](u8 track, u8 sector, u8 file_index, const cbmdos_dirent &ent) { results.emplace_back(dir_entry_type::file, metadata_from_dirent(ent)); return false; }; iterate_directory_entries(callback); - return std::make_pair(ERR_OK, std::move(results)); + return std::make_pair(std::error_condition(), std::move(results)); } @@ -279,12 +348,12 @@ std::pair<err_t, std::vector<dir_entry>> impl::directory_contents(const std::vec // impl::file_read //------------------------------------------------- -std::pair<err_t, std::vector<u8>> impl::file_read(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<u8>> impl::file_read(const std::vector<std::string> &path) { // find the file std::optional<cbmdos_dirent> dirent = dirent_from_path(path); if (!dirent) - return std::make_pair(ERR_NOT_FOUND, std::vector<u8>()); + return std::make_pair(error::not_found, std::vector<u8>()); // and get the data std::vector<u8> result; @@ -292,7 +361,263 @@ std::pair<err_t, std::vector<u8>> impl::file_read(const std::vector<std::string> while (iter.next()) result.insert(result.end(), (const u8 *)iter.data(), (const u8 *)iter.data() + iter.size()); - return std::make_pair(ERR_OK, std::move(result)); + return std::make_pair(std::error_condition(), std::move(result)); +} + + +std::error_condition impl::file_create(const std::vector<std::string> &path, const meta_data &meta) +{ + std::string filename = meta.get_string(meta_name::name, ""); + if (!is_valid_filename(filename)) + return error::invalid_name; + + std::optional<cbmdos_dirent> result; + u8 track = 0; + u8 sector = 0; + u8 file_index = 0; + auto callback = [&result, &track, §or, &file_index](u8 t, u8 s, u8 i, const cbmdos_dirent &dirent) + { + bool found = dirent.m_file_type == 0x00; + if (found) + { + result = dirent; + track = t; + sector = s; + file_index = i; + } + return found; + }; + iterate_all_directory_entries(callback); + + if (!result) + { + // Claim a next directory sector + auto const [err, new_sector] = claim_track_sector(DIRECTORY_TRACK); + if (err) + return err; + auto new_block = read_sector(DIRECTORY_TRACK, new_sector); + for (int i = 2; i < BLOCK_SIZE; i++) + new_block->w8(i, 0); + // Find last directory sector + u8 last_sector = 0; + block_iterator iter(*this, DIRECTORY_TRACK, FIRST_DIRECTORY_SECTOR); + while (iter.next()) + last_sector = iter.sector(); + // Update chain on last directory sector + auto last_dir_block = read_sector(DIRECTORY_TRACK, last_sector); + last_dir_block->w8(OFFSET_CHAIN_TRACK, DIRECTORY_TRACK); + last_dir_block->w8(OFFSET_CHAIN_SECTOR, new_sector); + track = DIRECTORY_TRACK; + sector = new_sector; + } + + auto const [err, file_track, file_sector] = claim_sector(); + if (err) + return err; + + // Create the file + auto dirblk = read_sector(track, sector); + u32 offset = file_index * DIRECTORY_ENTRY_SIZE; + for (int i = 0; i < DIRECTORY_ENTRY_SIZE; i++) + dirblk->w8(offset + i, (i >= 5 && i < 5 + 16) ? 0xa0 : 0x00); + dirblk->w8(offset + OFFSET_FILE_TYPE, FILE_TYPE_PRG); + dirblk->w8(offset + OFFSET_FILE_FIRST_TRACK, file_track); + dirblk->w8(offset + OFFSET_FILE_FIRST_SECTOR, file_sector); + dirblk->wstr(offset + OFFSET_FILE_NAME, filename); + // TODO set first side sector block track (rel file) + // TODO set first side sector block sector (rel file) + // TODO set rel file record length + // sector count will be set while writing the data + + return std::error_condition(); +} + + +std::error_condition impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) +{ + if (path.size() != 1) + return error::not_found; + std::string_view path_part = path[0]; + + std::optional<cbmdos_dirent> result; + u8 dir_track = 0; + u8 dir_sector = 0; + u8 dir_file_index = 0; + auto const callback = [&result, &dir_track, &dir_sector, &dir_file_index, path_part] (u8 track, u8 sector, u8 file_index, const cbmdos_dirent &dirent) + { + bool found = strtrimright_cbm(dirent.m_file_name) == path_part; + if (found) + { + dir_track = track; + dir_sector = sector; + dir_file_index = file_index; + result = dirent; + } + return found; + }; + iterate_directory_entries(callback); + + if (!result) + return error::not_found; + + u8 data_track = result->m_file_first_track; + u8 data_sector = result->m_file_first_sector; + + const size_t data_length = data.size(); + size_t offset = 0; + u32 sector_count = 0; + while (offset < data_length) + { + auto datablk = read_sector(data_track, data_sector); + u8 bytes = (data_length - offset) > SECTOR_DATA_BYTES ? SECTOR_DATA_BYTES : data_length - offset; + datablk->write(2, data.data() + offset, bytes); + offset += SECTOR_DATA_BYTES; + sector_count++; + if (datablk->r8(OFFSET_CHAIN_TRACK) == CHAIN_END) + { + if (offset < data_length) + { + auto [err, next_track, next_sector] = claim_sector(); + if (err) + return err; + datablk->w8(OFFSET_CHAIN_TRACK, next_track); + datablk->w8(OFFSET_CHAIN_SECTOR, next_sector); + data_track = next_track; + data_sector = next_sector; + } + } + else + { + if (offset < data_length) + { + data_track = datablk->r8(OFFSET_CHAIN_TRACK); + data_sector = datablk->r8(OFFSET_CHAIN_SECTOR); + } + else + { + // Free the rest of the chain + u8 track_to_free = datablk->r8(OFFSET_CHAIN_TRACK); + u8 sector_to_free = datablk->r8(OFFSET_CHAIN_SECTOR); + + while (track_to_free != CHAIN_END) + { + std::error_condition const err = free_sector(track_to_free, sector_to_free); + if (err) + return err; + datablk = read_sector(track_to_free, sector_to_free); + track_to_free = datablk->r8(OFFSET_CHAIN_TRACK); + sector_to_free = datablk->r8(OFFSET_CHAIN_SECTOR); + } + } + } + if (offset >= data_length) + { + datablk->w8(OFFSET_CHAIN_TRACK, CHAIN_END); + datablk->w8(OFFSET_CHAIN_SECTOR, bytes + 1); + } + } + + // Write sector count and file type to directory entry + auto dirblk = read_sector(dir_track, dir_sector); + u8 file_type = determine_file_type(data); + if (file_type == FILE_TYPE_DEL) + { + // Free sector, update first file sector to 00 + u8 file_track = dirblk->r8(DIRECTORY_ENTRY_SIZE * dir_file_index + OFFSET_FILE_FIRST_TRACK); + u8 file_sector = dirblk->r8(DIRECTORY_ENTRY_SIZE * dir_file_index + OFFSET_FILE_FIRST_SECTOR); + std::error_condition err = free_sector(file_track, file_sector); + if (err) + return err; + dirblk->w8(DIRECTORY_ENTRY_SIZE * dir_file_index + OFFSET_FILE_FIRST_TRACK, 0); + dirblk->w8(DIRECTORY_ENTRY_SIZE * dir_file_index + OFFSET_FILE_FIRST_SECTOR, 0); + sector_count = 0; + } + dirblk->w8(DIRECTORY_ENTRY_SIZE * dir_file_index + OFFSET_FILE_TYPE, file_type); + dirblk->w16l(DIRECTORY_ENTRY_SIZE * dir_file_index + OFFSET_SECTOR_COUNT, sector_count); + + return std::error_condition(); +} + + +u8 impl::determine_file_type(const std::vector<u8> &data) const +{ + // TODO: Determine/set the actual file type, defaulting to PRG for now. + const size_t data_length = data.size(); + if (data_length == 0) + return FILE_TYPE_DEL; + return FILE_TYPE_PRG; +} + + +bool impl::is_valid_filename(const std::string &filename) const +{ + // We only support a subset of the character set supported by Commodore for filenames. + std::regex filename_regex("[A-Z0-9!\"#\\$%&'\\(\\)*+,-./:;<=>?]{1,16}"); + return std::regex_match(filename, filename_regex); +} + + +std::pair<std::error_condition, u8> impl::claim_track_sector(u8 track) const +{ + if (track == 0 || track > m_max_track) + return std::make_pair(error::invalid_block, 0); + + u8 map_index; + for (map_index = 0; map_index < TRACK_VARIANTS && !(s_track_sector_map[map_index].first_track <= track && track <= s_track_sector_map[map_index].last_track) ; map_index++); + if (map_index >= TRACK_VARIANTS) + return std::make_pair(error::invalid_block, 0); + + auto bamblk = read_sector(DIRECTORY_TRACK, BAM_SECTOR); + u8 free_count = bamblk->r8(4 * track); + u32 free_bitmap = bamblk->r24l(4 * track + 1); + + for (int s = 0; s < MAX_SECTORS; s++) + { + int sector = s_track_sector_map[map_index].sector[s]; + if (sector >= 0 && free_count > 0 && util::BIT(free_bitmap, sector)) + { + free_bitmap &= ~(1 << sector); + free_count--; + bamblk->w8(4 * track, free_count); + bamblk->w24l(4 * track + 1, free_bitmap); + // Write chain end marker in new sector + auto claimedlk = read_sector(track, sector); + claimedlk->w8(OFFSET_CHAIN_TRACK, CHAIN_END); + claimedlk->w8(OFFSET_CHAIN_SECTOR, 0xff); + return std::make_pair(std::error_condition(), sector); + } + } + return std::make_pair(error::no_space, 0); +} + + +std::tuple<std::error_condition, u8, u8> impl::claim_sector() const +{ + for (int track = 0; track < m_max_track - 1; track++) + { + auto const [err, sector] = claim_track_sector(s_data_track_order[track]); + if (!err) + return std::make_tuple(std::error_condition(), s_data_track_order[track], sector); + if (err != error::no_space) + return std::make_tuple(err, 0, 0); + } + return std::make_tuple(error::no_space, 0, 0); +} + + +std::error_condition impl::free_sector(u8 track, u8 sector) const +{ + if (track == 0 || track > m_max_track) + return error::invalid_block; + + auto bamblk = read_sector(DIRECTORY_TRACK, BAM_SECTOR); + u8 free_count = bamblk->r8(4 * track); + u32 free_bitmap = bamblk->r24l(4 * track + 1); + free_bitmap |= (1 << sector); + free_count++; + bamblk->w8(4 * track, free_count); + bamblk->w24l(4 * track + 1, free_bitmap); + return std::error_condition(); } @@ -300,7 +625,7 @@ std::pair<err_t, std::vector<u8>> impl::file_read(const std::vector<std::string> // impl::read_sector //------------------------------------------------- -fsblk_t::block_t impl::read_sector(int track, int sector) const +fsblk_t::block_t::ptr impl::read_sector(int track, int sector) const { // CBM thinks in terms of tracks/sectors, but we have a block device abstraction u32 block = 0; @@ -320,13 +645,13 @@ fsblk_t::block_t impl::read_sector(int track, int sector) const std::optional<impl::cbmdos_dirent> impl::dirent_from_path(const std::vector<std::string> &path) const { if (path.size() != 1) - return { }; + return std::nullopt; std::string_view path_part = path[0]; std::optional<cbmdos_dirent> result; - auto callback = [&result, path_part](const cbmdos_dirent &dirent) + auto const callback = [&result, path_part] (u8 track, u8 sector, u8 file_index, const cbmdos_dirent &dirent) { - bool found = strtrimright_cbm(dirent.m_file_name) == path_part; + bool const found = strtrimright_cbm(dirent.m_file_name) == path_part; if (found) result = dirent; return found; @@ -340,22 +665,38 @@ std::optional<impl::cbmdos_dirent> impl::dirent_from_path(const std::vector<std: // impl::iterate_directory_entries //------------------------------------------------- -void impl::iterate_directory_entries(const std::function<bool(const cbmdos_dirent &dirent)> &callback) const +void impl::iterate_directory_entries(const std::function<bool(u8 track, u8 sector, u8 file_index, const cbmdos_dirent &dirent)> &callback) const { - block_iterator iter(*this, 18, 1); + block_iterator iter(*this, DIRECTORY_TRACK, FIRST_DIRECTORY_SECTOR); while (iter.next()) { - for (const cbmdos_dirent &ent : iter.dirent_data()) + auto entries = iter.dirent_data(); + + for (int file_index = 0; file_index < SECTOR_DIRECTORY_COUNT; file_index++) { - if (ent.m_file_type != 0x00) + if (entries[file_index].m_file_type != 0x00) { - if (callback(ent)) + if (callback(iter.track(), iter.sector(), file_index, entries[file_index])) return; } } } } +void impl::iterate_all_directory_entries(const std::function<bool(u8 track, u8 sector, u8 file_index, const cbmdos_dirent &dirent)> &callback) const +{ + block_iterator iter(*this, DIRECTORY_TRACK, FIRST_DIRECTORY_SECTOR); + while (iter.next()) + { + auto entries = iter.dirent_data(); + + for (int file_index = 0; file_index < SECTOR_DIRECTORY_COUNT; file_index++) + { + if (callback(iter.track(), iter.sector(), file_index, entries[file_index])) + return; + } + } +} //------------------------------------------------- // impl::metadata_from_dirent @@ -366,19 +707,19 @@ meta_data impl::metadata_from_dirent(const cbmdos_dirent &dirent) const std::string file_type; switch (dirent.m_file_type) { - case 0x80: + case FILE_TYPE_DEL: file_type = "DEL"; break; - case 0x81: + case FILE_TYPE_SEQ: file_type = "SEQ"; break; - case 0x82: + case FILE_TYPE_PRG: file_type = "PRG"; break; - case 0x83: + case FILE_TYPE_USR: file_type = "USR"; break; - case 0x84: + case FILE_TYPE_REL: file_type = "REL"; break; default: @@ -409,6 +750,8 @@ impl::block_iterator::block_iterator(const impl &fs, u8 first_track, u8 first_se : m_fs(fs) , m_track(first_track) , m_sector(first_sector) + , m_next_track(first_track) + , m_next_sector(first_sector) { } @@ -420,19 +763,21 @@ impl::block_iterator::block_iterator(const impl &fs, u8 first_track, u8 first_se bool impl::block_iterator::next() { bool result; - if (m_track != 0x00) + m_track = m_next_track; + m_sector = m_next_sector; + if (m_track != CHAIN_END) { // check for the degenerate scenario where we have a cycle (this should not happen // on a non-corrupt disk) - auto visited_tuple = std::make_tuple(m_track, m_sector); + auto visited_tuple = std::make_tuple(m_next_track, m_next_sector); if (m_visited_set.find(visited_tuple) != m_visited_set.end()) return false; m_visited_set.insert(visited_tuple); // with that out of the way, proceed - m_block = m_fs.read_sector(m_track, m_sector); - m_track = m_block.r8(0); - m_sector = m_block.r8(1); + m_block = m_fs.read_sector(m_next_track, m_next_sector); + m_next_track = m_block->r8(OFFSET_CHAIN_TRACK); + m_next_sector = m_block->r8(OFFSET_CHAIN_SECTOR); result = true; } else @@ -450,7 +795,7 @@ bool impl::block_iterator::next() const void *impl::block_iterator::data() const { - return m_block.rodata() + 2; + return m_block->rodata() + 2; } @@ -458,9 +803,9 @@ const void *impl::block_iterator::data() const // impl::block_iterator::dirent_data //------------------------------------------------- -const std::array<impl::cbmdos_dirent, 4> &impl::block_iterator::dirent_data() const +const std::array<impl::cbmdos_dirent, impl::SECTOR_DIRECTORY_COUNT> &impl::block_iterator::dirent_data() const { - return *reinterpret_cast<const std::array<impl::cbmdos_dirent, 4> *>(m_block.rodata()); + return *reinterpret_cast<const std::array<impl::cbmdos_dirent, SECTOR_DIRECTORY_COUNT> *>(m_block->rodata()); } @@ -470,7 +815,7 @@ const std::array<impl::cbmdos_dirent, 4> &impl::block_iterator::dirent_data() co u8 impl::block_iterator::size() const { - return m_track != 0x00 ? 254 : m_sector - 1; + return (m_track != CHAIN_END) ? SECTOR_DATA_BYTES : (m_sector - 1); } } // anonymous namespace diff --git a/src/lib/formats/fs_coco_os9.cpp b/src/lib/formats/fs_coco_os9.cpp index f8a14c7acdd..8e9d9e8c8d0 100644 --- a/src/lib/formats/fs_coco_os9.cpp +++ b/src/lib/formats/fs_coco_os9.cpp @@ -39,23 +39,23 @@ public: class volume_header { public: - volume_header(fsblk_t::block_t &&block); + volume_header(fsblk_t::block_t::ptr &&block); volume_header(const volume_header &) = delete; volume_header(volume_header &&) = default; - u32 total_sectors() const { return m_block.r24b(0); } - u8 track_size_in_sectors() const { return m_block.r8(3); } - u16 allocation_bitmap_bytes() const { return m_block.r16b(4); } - u16 cluster_size() const { return m_block.r16b(6); } - u32 root_dir_lsn() const { return m_block.r24b(8); } - u16 owner_id() const { return m_block.r16b(11); } - u16 disk_id() const { return m_block.r16b(14); } - u8 format_flags() const { return m_block.r8(16); } - u16 sectors_per_track() const { return m_block.r16b(17); } - u32 bootstrap_lsn() const { return m_block.r24b(21); } - u16 bootstrap_size() const { return m_block.r16b(24); } - util::arbitrary_datetime creation_date() const { return from_os9_date(m_block.r24b(26), m_block.r16b(29)); } - u16 sector_size() const { u16 result = m_block.r16b(104); return result != 0 ? result : 256; } + u32 total_sectors() const { return m_block->r24b(0); } + u8 track_size_in_sectors() const { return m_block->r8(3); } + u16 allocation_bitmap_bytes() const { return m_block->r16b(4); } + u16 cluster_size() const { return m_block->r16b(6); } + u32 root_dir_lsn() const { return m_block->r24b(8); } + u16 owner_id() const { return m_block->r16b(11); } + u16 disk_id() const { return m_block->r16b(14); } + u8 format_flags() const { return m_block->r8(16); } + u16 sectors_per_track() const { return m_block->r16b(17); } + u32 bootstrap_lsn() const { return m_block->r24b(21); } + u16 bootstrap_size() const { return m_block->r16b(24); } + util::arbitrary_datetime creation_date() const { return from_os9_date(m_block->r24b(26), m_block->r16b(29)); } + u16 sector_size() const { u16 result = m_block->r16b(104); return result != 0 ? result : 256; } u8 sides() const { return (format_flags() & 0x01) ? 2 : 1; } bool double_density() const { return (format_flags() & 0x02) != 0; } bool double_track() const { return (format_flags() & 0x04) != 0; } @@ -65,7 +65,7 @@ public: std::string name() const; private: - fsblk_t::block_t m_block; + fsblk_t::block_t::ptr m_block; }; @@ -74,17 +74,17 @@ public: class file_header { public: - file_header(fsblk_t::block_t &&block, std::string &&filename); + file_header(fsblk_t::block_t::ptr &&block, std::string &&filename); file_header(const file_header &) = delete; file_header(file_header &&) = default; file_header &operator=(const file_header &) = delete; file_header &operator=(file_header &&) = default; - u8 attributes() const { return m_block.r8(0); } - u16 owner_id() const { return m_block.r16b(1); } - u8 link_count() const { return m_block.r8(8); } - u32 file_size() const { return m_block.r32b(9); } + u8 attributes() const { return m_block->r8(0); } + u16 owner_id() const { return m_block->r16b(1); } + u8 link_count() const { return m_block->r8(8); } + u32 file_size() const { return m_block->r32b(9); } util::arbitrary_datetime creation_date() const; bool is_directory() const { return (attributes() & 0x80) != 0; } bool is_non_sharable() const { return (attributes() & 0x40) != 0; } @@ -100,8 +100,8 @@ public: void get_sector_map_entry(int entry_number, u32 &start_lsn, u16 &count) const; private: - fsblk_t::block_t m_block; - std::string m_filename; + fsblk_t::block_t::ptr m_block; + std::string m_filename; }; // ctor/dtor @@ -109,10 +109,10 @@ public: virtual ~coco_os9_impl() = default; virtual meta_data volume_metadata() override; - virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override; - virtual err_t format(const meta_data &meta) override; + virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + virtual std::error_condition format(const meta_data &meta) override; std::optional<file_header> find(const std::vector<std::string> &path, std::optional<dir_entry_type> expected_entry_type) const; void iterate_directory_entries(const file_header &header, const std::function<bool(std::string &&, u32)> callback) const; @@ -301,14 +301,14 @@ meta_data coco_os9_impl::volume_metadata() // coco_os9_impl::metadata //------------------------------------------------- -std::pair<err_t, meta_data> coco_os9_impl::metadata(const std::vector<std::string> &path) +std::pair<std::error_condition, meta_data> coco_os9_impl::metadata(const std::vector<std::string> &path) { // look up the path std::optional<file_header> header = find(path, { }); if (!header) - return std::make_pair(ERR_NOT_FOUND, meta_data()); + return std::make_pair(error::not_found, meta_data()); - return std::make_pair(ERR_OK, header->metadata()); + return std::make_pair(std::error_condition(), header->metadata()); } @@ -316,12 +316,12 @@ std::pair<err_t, meta_data> coco_os9_impl::metadata(const std::vector<std::strin // coco_os9_impl::directory_contents //------------------------------------------------- -std::pair<err_t, std::vector<dir_entry>> coco_os9_impl::directory_contents(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<dir_entry>> coco_os9_impl::directory_contents(const std::vector<std::string> &path) { // look up the path std::optional<file_header> header = find(path, dir_entry_type::dir); if (!header) - return std::make_pair(ERR_NOT_FOUND, std::vector<dir_entry>()); + return std::make_pair(error::not_found, std::vector<dir_entry>()); // iterate through the directory std::vector<dir_entry> results; @@ -337,7 +337,7 @@ std::pair<err_t, std::vector<dir_entry>> coco_os9_impl::directory_contents(const iterate_directory_entries(*header, callback); // and we're done - return std::make_pair(ERR_OK, std::move(results)); + return std::make_pair(std::error_condition(), std::move(results)); } @@ -345,15 +345,15 @@ std::pair<err_t, std::vector<dir_entry>> coco_os9_impl::directory_contents(const // coco_os9_impl::file_read //------------------------------------------------- -std::pair<err_t, std::vector<u8>> coco_os9_impl::file_read(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<u8>> coco_os9_impl::file_read(const std::vector<std::string> &path) { // look up the path std::optional<file_header> header = find(path, dir_entry_type::file); if (!header) - return std::make_pair(ERR_NOT_FOUND, std::vector<u8>()); + return std::make_pair(error::not_found, std::vector<u8>()); std::vector<u8> data = read_file_data(*header); - return std::make_pair(ERR_OK, std::move(data)); + return std::make_pair(std::error_condition(), std::move(data)); } @@ -361,10 +361,10 @@ std::pair<err_t, std::vector<u8>> coco_os9_impl::file_read(const std::vector<std // coco_os9_impl::format //------------------------------------------------- -err_t coco_os9_impl::format(const meta_data &meta) +std::error_condition coco_os9_impl::format(const meta_data &meta) { // for some reason, the OS-9 world favored filling with 0xE5 - m_blockdev.fill(0xE5); + m_blockdev.fill_all(0xe5); // identify geometry info u8 sectors = 18; // TODO - we need a definitive technique to get the floppy geometry @@ -390,33 +390,33 @@ err_t coco_os9_impl::format(const meta_data &meta) // volume header auto volume_header = m_blockdev.get(0); - volume_header.fill(0x00); - volume_header.w24b(0, lsn_count); // DD.TOT - total secctors - volume_header.w8(3, sectors); // DD.TKS - track size in sectors - volume_header.w16b(4, (allocation_bitmap_bits + 7) / 8); // DD.MAP - allocation bitmap in bytes - volume_header.w16b(6, cluster_size); // DD.BIT - cluster size - volume_header.w24b(8, 1 + allocation_bitmap_lsns); // DD.DIR - root directory LSN - volume_header.w16b(11, owner_id); // DD.OWN - owner ID - volume_header.w8(13, attributes); // DD.ATT - Dattributes - volume_header.w16b(14, disk_id); // DD.DSK - disk ID - volume_header.w8(16, format_flags); // DD.FMT - format flags - volume_header.w16b(17, sectors); // DD.SPT - sectors per track - volume_header.w24b(26, creation_os9date); // DD.DAT - date of creation - volume_header.w16b(29, creation_os9time); // DD.DAT - time of creation - volume_header.wstr(31, to_os9_string(volume_title, 32)); // DD.NAM - title - volume_header.w16b(103, sector_bytes / 256); // sector bytes + volume_header->fill(0x00); + volume_header->w24b(0, lsn_count); // DD.TOT - total secctors + volume_header->w8(3, sectors); // DD.TKS - track size in sectors + volume_header->w16b(4, (allocation_bitmap_bits + 7) / 8); // DD.MAP - allocation bitmap in bytes + volume_header->w16b(6, cluster_size); // DD.BIT - cluster size + volume_header->w24b(8, 1 + allocation_bitmap_lsns); // DD.DIR - root directory LSN + volume_header->w16b(11, owner_id); // DD.OWN - owner ID + volume_header->w8(13, attributes); // DD.ATT - Dattributes + volume_header->w16b(14, disk_id); // DD.DSK - disk ID + volume_header->w8(16, format_flags); // DD.FMT - format flags + volume_header->w16b(17, sectors); // DD.SPT - sectors per track + volume_header->w24b(26, creation_os9date); // DD.DAT - date of creation + volume_header->w16b(29, creation_os9time); // DD.DAT - time of creation + volume_header->wstr(31, to_os9_string(volume_title, 32)); // DD.NAM - title + volume_header->w16b(103, sector_bytes / 256); // sector bytes // path descriptor options - volume_header.w8(0x3f + 0x00, 1); // device class - volume_header.w8(0x3f + 0x01, 1); // drive number - volume_header.w8(0x3f + 0x03, 0x20); // device type - volume_header.w8(0x3f + 0x04, 1); // density capability - volume_header.w16b(0x3f + 0x05, tracks); // number of tracks - volume_header.w8(0x3f + 0x07, heads); // number of sides - volume_header.w16b(0x3f + 0x09, sectors); // sectors per track - volume_header.w16b(0x3f + 0x0b, sectors); // sectors on track zero - volume_header.w8(0x3f + 0x0d, 3); // sector interleave factor - volume_header.w8(0x3f + 0x0e, 8); // default sectors per allocation + volume_header->w8(0x3f + 0x00, 1); // device class + volume_header->w8(0x3f + 0x01, 1); // drive number + volume_header->w8(0x3f + 0x03, 0x20); // device type + volume_header->w8(0x3f + 0x04, 1); // density capability + volume_header->w16b(0x3f + 0x05, tracks); // number of tracks + volume_header->w8(0x3f + 0x07, heads); // number of sides + volume_header->w16b(0x3f + 0x09, sectors); // sectors per track + volume_header->w16b(0x3f + 0x0b, sectors); // sectors on track zero + volume_header->w8(0x3f + 0x0d, 3); // sector interleave factor + volume_header->w8(0x3f + 0x0e, 8); // default sectors per allocation // allocation bitmap u32 total_allocated_sectors = 1 + allocation_bitmap_lsns + 1 + 8; @@ -428,7 +428,7 @@ err_t coco_os9_impl::format(const meta_data &meta) { u32 pos = (i * sector_bytes + j) * 8; if (pos + 8 < total_allocated_sectors) - abblk_bytes[j] = 0xFF; + abblk_bytes[j] = 0xff; else if (pos >= total_allocated_sectors) abblk_bytes[j] = 0x00; else @@ -436,35 +436,35 @@ err_t coco_os9_impl::format(const meta_data &meta) } auto abblk = m_blockdev.get(1 + i); - abblk.copy(0, abblk_bytes.data(), sector_bytes); + abblk->write(0, abblk_bytes.data(), sector_bytes); } // root directory header auto roothdr_blk = m_blockdev.get(1 + allocation_bitmap_lsns); - roothdr_blk.fill(0x00); - roothdr_blk.w8(0x00, 0xBF); - roothdr_blk.w8(0x01, 0x00); - roothdr_blk.w8(0x02, 0x00); - roothdr_blk.w24b(0x03, creation_os9date); - roothdr_blk.w16b(0x06, creation_os9time); - roothdr_blk.w8(0x08, 0x01); - roothdr_blk.w8(0x09, 0x00); - roothdr_blk.w8(0x0A, 0x00); - roothdr_blk.w8(0x0B, 0x00); - roothdr_blk.w8(0x0C, 0x40); - roothdr_blk.w24b(0x0D, creation_os9date); - roothdr_blk.w24b(0x10, 1 + allocation_bitmap_lsns + 1); - roothdr_blk.w16b(0x13, 8); + roothdr_blk->fill(0x00); + roothdr_blk->w8(0x00, 0xbf); + roothdr_blk->w8(0x01, 0x00); + roothdr_blk->w8(0x02, 0x00); + roothdr_blk->w24b(0x03, creation_os9date); + roothdr_blk->w16b(0x06, creation_os9time); + roothdr_blk->w8(0x08, 0x01); + roothdr_blk->w8(0x09, 0x00); + roothdr_blk->w8(0x0a, 0x00); + roothdr_blk->w8(0x0b, 0x00); + roothdr_blk->w8(0x0c, 0x40); + roothdr_blk->w24b(0x0d, creation_os9date); + roothdr_blk->w24b(0x10, 1 + allocation_bitmap_lsns + 1); + roothdr_blk->w16b(0x13, 8); // root directory data auto rootdata_blk = m_blockdev.get(1 + allocation_bitmap_lsns + 1); - rootdata_blk.fill(0x00); - rootdata_blk.w8(0x00, 0x2E); - rootdata_blk.w8(0x01, 0xAE); - rootdata_blk.w8(0x1F, 1 + allocation_bitmap_lsns); - rootdata_blk.w8(0x20, 0xAE); - rootdata_blk.w8(0x3F, 1 + allocation_bitmap_lsns); - return ERR_OK; + rootdata_blk->fill(0x00); + rootdata_blk->w8(0x00, 0x2e); + rootdata_blk->w8(0x01, 0xae); + rootdata_blk->w8(0x1f, 1 + allocation_bitmap_lsns); + rootdata_blk->w8(0x20, 0xae); + rootdata_blk->w8(0x3f, 1 + allocation_bitmap_lsns); + return std::error_condition(); } @@ -554,9 +554,9 @@ std::vector<u8> coco_os9_impl::read_file_data(const file_header &header) const for (u32 lsn = start_lsn; lsn < start_lsn + count; lsn++) { auto block = m_blockdev.get(lsn); - size_t block_size = std::min(std::min(u32(m_volume_header.sector_size()), block.size()), header.file_size() - u32(data.size())); + size_t block_size = std::min(std::min(u32(m_volume_header.sector_size()), block->size()), header.file_size() - u32(data.size())); for (auto i = 0; i < block_size; i++) - data.push_back(block.rodata()[i]); + data.push_back(block->rodata()[i]); } } return data; @@ -580,7 +580,7 @@ std::string coco_os9_impl::pick_os9_string(std::string_view raw_string) // and add the final character if we have to if (iter < raw_string.end() && *iter & 0x80) - result.append(1, *iter & 0x7F); + result.append(1, *iter & 0x7f); return result; } @@ -595,7 +595,7 @@ std::string coco_os9_impl::to_os9_string(std::string_view s, size_t length) std::string result(length, '\0'); for (auto i = 0; i < std::min(length, s.size()); i++) { - result[i] = (s[i] & 0x7F) + result[i] = (s[i] & 0x7f) | (i == s.size() ? 0x80 : 0x00); } return result; @@ -610,11 +610,11 @@ util::arbitrary_datetime coco_os9_impl::from_os9_date(u32 os9_date, u16 os9_time { util::arbitrary_datetime dt; memset(&dt, 0, sizeof(dt)); - dt.year = ((os9_date >> 16) & 0xFF) + 1900; - dt.month = (os9_date >> 8) & 0xFF; - dt.day_of_month = (os9_date >> 0) & 0xFF; - dt.hour = (os9_time >> 8) & 0xFF; - dt.minute = (os9_time >> 0) & 0xFF; + dt.year = ((os9_date >> 16) & 0xff) + 1900; + dt.month = (os9_date >> 8) & 0xff; + dt.day_of_month = (os9_date >> 0) & 0xff; + dt.hour = (os9_time >> 8) & 0xff; + dt.minute = (os9_time >> 0) & 0xff; return dt; } @@ -625,11 +625,11 @@ util::arbitrary_datetime coco_os9_impl::from_os9_date(u32 os9_date, u16 os9_time std::tuple<u32, u16> coco_os9_impl::to_os9_date(const util::arbitrary_datetime &datetime) { - u32 os9_date = ((datetime.year - 1900) & 0xFF) << 16 - | (datetime.month & 0xFF) << 8 - | (datetime.day_of_month & 0xFF) << 0; - u16 os9_time = (datetime.hour & 0xFF) << 8 - | (datetime.minute & 0xFF) << 0; + u32 os9_date = ((datetime.year - 1900) & 0xff) << 16 + | (datetime.month & 0xff) << 8 + | (datetime.day_of_month & 0xff) << 0; + u16 os9_time = (datetime.hour & 0xff) << 8 + | (datetime.minute & 0xff) << 0; return std::make_tuple(os9_date, os9_time); } @@ -664,7 +664,7 @@ bool coco_os9_impl::is_ignored_filename(std::string_view name) // volume_header ctor //------------------------------------------------- -coco_os9_impl::volume_header::volume_header(fsblk_t::block_t &&block) +coco_os9_impl::volume_header::volume_header(fsblk_t::block_t::ptr &&block) : m_block(std::move(block)) { } @@ -676,7 +676,7 @@ coco_os9_impl::volume_header::volume_header(fsblk_t::block_t &&block) std::string coco_os9_impl::volume_header::name() const { - std::string_view raw_name((const char *)&m_block.rodata()[31], 32); + std::string_view raw_name(m_block->rstr(31, 32)); return pick_os9_string(raw_name); } @@ -685,7 +685,7 @@ std::string coco_os9_impl::volume_header::name() const // file_header ctor //------------------------------------------------- -coco_os9_impl::file_header::file_header(fsblk_t::block_t &&block, std::string &&filename) +coco_os9_impl::file_header::file_header(fsblk_t::block_t::ptr &&block, std::string &&filename) : m_block(std::move(block)) , m_filename(std::move(filename)) { @@ -698,7 +698,7 @@ coco_os9_impl::file_header::file_header(fsblk_t::block_t &&block, std::string && util::arbitrary_datetime coco_os9_impl::file_header::creation_date() const { - return from_os9_date(m_block.r24b(13)); + return from_os9_date(m_block->r24b(13)); } @@ -735,7 +735,7 @@ meta_data coco_os9_impl::file_header::metadata() const int coco_os9_impl::file_header::get_sector_map_entry_count() const { - return (m_block.size() - 16) / 5; + return (m_block->size() - 16) / 5; } @@ -745,6 +745,6 @@ int coco_os9_impl::file_header::get_sector_map_entry_count() const void coco_os9_impl::file_header::get_sector_map_entry(int entry_number, u32 &start_lsn, u16 &count) const { - start_lsn = m_block.r24b(16 + (entry_number * 5) + 0); - count = m_block.r16b(16 + (entry_number * 5) + 3); + start_lsn = m_block->r24b(16 + (entry_number * 5) + 0); + count = m_block->r16b(16 + (entry_number * 5) + 3); } diff --git a/src/lib/formats/fs_coco_rsdos.cpp b/src/lib/formats/fs_coco_rsdos.cpp index 76b737d9011..0ce88cf01df 100644 --- a/src/lib/formats/fs_coco_rsdos.cpp +++ b/src/lib/formats/fs_coco_rsdos.cpp @@ -1,11 +1,14 @@ // license:BSD-3-Clause -// copyright-holders:Nathan Woods +// copyright-holders:Nathan Woods, Wilbert Pol /*************************************************************************** fs_coco_rsdos.cpp Management of CoCo "RS-DOS" floppy images +Limitation: +- The determination of the file type is very limited. + ***************************************************************************/ #include "fs_coco_rsdos.h" @@ -16,6 +19,7 @@ #include <bitset> #include <optional> +#include <regex> #include <string_view> using namespace fs; @@ -30,6 +34,8 @@ public: coco_rsdos_impl(fsblk_t &blockdev); virtual ~coco_rsdos_impl() = default; + static constexpr int SECTOR_DIRECTORY_ENTRY_COUNT = 8; + struct rsdos_dirent { char m_filename[11]; @@ -46,7 +52,7 @@ public: { rsdos_dirent m_dirent; u8 m_unused[16]; - } m_entries[8]; + } m_entries[SECTOR_DIRECTORY_ENTRY_COUNT]; }; class granule_iterator @@ -56,27 +62,54 @@ public: bool next(u8 &granule, u16 &byte_count); private: - fsblk_t::block_t m_granule_map; - std::optional<u8> m_current_granule; - u8 m_maximum_granules; - u16 m_last_sector_bytes; - std::bitset<256> m_visited_granules; + fsblk_t::block_t::ptr m_granule_map; + std::optional<u8> m_current_granule; + u8 m_maximum_granules; + u16 m_last_sector_bytes; + std::bitset<256> m_visited_granules; }; - virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override; - virtual err_t format(const meta_data &meta) override; + virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + virtual std::error_condition format(const meta_data &meta) override; + virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override; + virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override; static bool validate_filename(std::string_view name); private: - fsblk_t::block_t read_sector(int track, int sector) const; + static constexpr u8 TRACK_GRANULE_COUNT = 2; + static constexpr u8 GRANULE_SECTOR_COUNT = 9; + static constexpr u8 TRACK_SECTOR_COUNT = TRACK_GRANULE_COUNT * GRANULE_SECTOR_COUNT; + static constexpr u8 DIRECTORY_TRACK = 17; + static constexpr u8 DIRECTORY_ENTRY_SIZE = 0x20; + static constexpr u8 FNAME_LENGTH = 11; + static constexpr u16 SECTOR_SIZE = 0x100; + static constexpr u8 OFFSET_FILE_TYPE = 0x0b; + static constexpr u8 OFFSET_ASCII_FLAG = 0x0c; + static constexpr u8 OFFSET_FIRST_GRANULE = 0x0d; + static constexpr u8 OFFSET_LAST_SECTOR_BYTES = 0x0e; + static constexpr u8 FILE_TYPE_BASIC = 0x00; + static constexpr u8 FILE_TYPE_BASIC_DATA = 0x01; + static constexpr u8 FILE_TYPE_MACHINE_CODE = 0x02; + static constexpr u8 FILE_TYPE_TEXT_EDITOR = 0x03; + static constexpr u8 FILE_LAST_GRANULE_INDICATOR = 0xc0; + + fsblk_t::block_t::ptr read_sector(int track, int sector) const; + fsblk_t::block_t::ptr read_granule_sector(u8 granule, u8 sector) const; u8 maximum_granules() const; std::optional<rsdos_dirent> dirent_from_path(const std::vector<std::string> &path); - void iterate_directory_entries(const std::function<bool(const rsdos_dirent &dirent)> &callback); + template <typename T> + void iterate_directory_entries(T &&callback); meta_data get_metadata_from_dirent(const rsdos_dirent &dirent); static std::string get_filename_from_dirent(const rsdos_dirent &dirent); + std::pair<std::error_condition, std::string> build_direntry_filename(const std::string &filename); + std::pair<std::error_condition, u8> claim_granule(); + void write_granule_map(u8 granule, u8 map_data); + u8 read_granule_map(u8 granule) const; + bool is_ascii(const std::vector<u8> &data) const; + u8 determine_file_type(const std::vector<u8> &data) const; }; } // anonymous namespace @@ -139,7 +172,7 @@ bool coco_rsdos_image::can_read() const bool coco_rsdos_image::can_write() const { - return false; + return true; } @@ -197,7 +230,7 @@ bool coco_rsdos_impl::validate_filename(std::string_view name) //------------------------------------------------- coco_rsdos_impl::coco_rsdos_impl(fsblk_t &blockdev) - : filesystem_t(blockdev, 256) + : filesystem_t(blockdev, SECTOR_SIZE) { } @@ -206,14 +239,14 @@ coco_rsdos_impl::coco_rsdos_impl(fsblk_t &blockdev) // coco_rsdos_impl::metadata //------------------------------------------------- -std::pair<err_t, meta_data> coco_rsdos_impl::metadata(const std::vector<std::string> &path) +std::pair<std::error_condition, meta_data> coco_rsdos_impl::metadata(const std::vector<std::string> &path) { // attempt to find the file const std::optional<rsdos_dirent> dirent = dirent_from_path(path); if (!dirent) - return std::make_pair(ERR_NOT_FOUND, meta_data()); + return std::make_pair(error::not_found, meta_data()); - return std::make_pair(ERR_OK, get_metadata_from_dirent(*dirent)); + return std::make_pair(std::error_condition(), get_metadata_from_dirent(*dirent)); } @@ -221,16 +254,16 @@ std::pair<err_t, meta_data> coco_rsdos_impl::metadata(const std::vector<std::str // coco_rsdos_impl::directory_contents //------------------------------------------------- -std::pair<err_t, std::vector<dir_entry>> coco_rsdos_impl::directory_contents(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<dir_entry>> coco_rsdos_impl::directory_contents(const std::vector<std::string> &path) { std::vector<dir_entry> results; - auto callback = [this, &results](const rsdos_dirent &dirent) + auto const callback = [this, &results](u8 s, u8 i, const rsdos_dirent &dirent) { results.emplace_back(dir_entry_type::file, get_metadata_from_dirent(dirent)); return false; }; iterate_directory_entries(callback); - return std::make_pair(ERR_OK, std::move(results)); + return std::make_pair(std::error_condition(), std::move(results)); } @@ -238,12 +271,12 @@ std::pair<err_t, std::vector<dir_entry>> coco_rsdos_impl::directory_contents(con // coco_rsdos_impl::file_read //------------------------------------------------- -std::pair<err_t, std::vector<u8>> coco_rsdos_impl::file_read(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<u8>> coco_rsdos_impl::file_read(const std::vector<std::string> &path) { // attempt to find the file const std::optional<rsdos_dirent> dirent = dirent_from_path(path); if (!dirent) - return std::make_pair(ERR_NOT_FOUND, std::vector<u8>()); + return std::make_pair(error::not_found, std::vector<u8>()); std::vector<u8> result; u8 granule; @@ -264,11 +297,10 @@ std::pair<err_t, std::vector<u8>> coco_rsdos_impl::file_read(const std::vector<s { // read this sector auto block = read_sector(track, sector); - const u8 *data = block.rodata(); - u16 data_length = std::min(byte_count, u16(256)); + u16 data_length = std::min(byte_count, u16(SECTOR_SIZE)); // and append it to the results - memcpy(result.data() + current_size, data, data_length); + block->read(0, result.data() + current_size, data_length); // and advance current_size += data_length; @@ -276,7 +308,7 @@ std::pair<err_t, std::vector<u8>> coco_rsdos_impl::file_read(const std::vector<s sector++; } } - return std::make_pair(ERR_OK, std::move(result)); + return std::make_pair(std::error_condition(), std::move(result)); } @@ -284,23 +316,248 @@ std::pair<err_t, std::vector<u8>> coco_rsdos_impl::file_read(const std::vector<s // coco_rsdos_impl::format //------------------------------------------------- -err_t coco_rsdos_impl::format(const meta_data &meta) +std::error_condition coco_rsdos_impl::format(const meta_data &meta) { // formatting RS-DOS is easy - just fill everything with 0xFF - m_blockdev.fill(0xFF); - return ERR_OK; + m_blockdev.fill_all(0xFF); + return std::error_condition(); +} + + +std::pair<std::error_condition, std::string> coco_rsdos_impl::build_direntry_filename(const std::string &filename) +{ + // The manual does not say anything about valid characters for a file name. + const std::regex filename_regex("([^.]{0,8})(\\.([^.]{0,3}))?"); + std::smatch smatch; + if (!std::regex_match(filename, smatch, filename_regex)) + return std::make_pair(error::invalid_name, std::string()); + if (smatch.size() != 4) + return std::make_pair(error::invalid_name, std::string()); + + std::string fname; + fname.resize(FNAME_LENGTH, ' '); + + for (int i = 0; i < 8 && i < smatch.str(1).size(); i++) + fname[i] = smatch.str(1)[i]; + + for (int j = 0; j < 3 && j < smatch.str(3).size(); j++) + fname[8 + j] = smatch.str(3)[j]; + + return std::make_pair(std::error_condition(), std::move(fname)); +} + + +std::error_condition coco_rsdos_impl::file_create(const std::vector<std::string> &path, const meta_data &meta) +{ + if (!path.empty()) + return error::unsupported; + + const std::string filename = meta.get_string(meta_name::name, ""); + auto [err, fname] = build_direntry_filename(filename); + if (err) + return err; + + bool found_entry = false; + u8 dir_sector = 0; + u8 file_index = 0; + for (dir_sector = 3; !found_entry && dir_sector <= TRACK_SECTOR_COUNT; dir_sector++) + { + auto dir_block = read_sector(DIRECTORY_TRACK, dir_sector); + + for (file_index = 0; !found_entry && file_index < SECTOR_DIRECTORY_ENTRY_COUNT; file_index++) + { + const u8 first_byte = dir_block->r8(file_index * DIRECTORY_ENTRY_SIZE); + // 0xff marks the end of the directory, 0x00 marks a deleted file + found_entry = (first_byte == 0xff || first_byte == 0x00); + if (found_entry) + { + for (int i = 0; i < DIRECTORY_ENTRY_SIZE; i++) + dir_block->w8(file_index * DIRECTORY_ENTRY_SIZE + i, 0); + + auto [cerr, granule] = claim_granule(); + if (cerr != std::error_condition()) + return cerr; + + dir_block->wstr(file_index * DIRECTORY_ENTRY_SIZE + 0, fname); + dir_block->w8(file_index * DIRECTORY_ENTRY_SIZE + OFFSET_FIRST_GRANULE, granule); + // The file type, ASCII flag, and number of bytes in last sector of file will be set during writing. + } + } + } + if (!found_entry) + return error::no_space; + + return std::error_condition(); +} + + +std::error_condition coco_rsdos_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) +{ + if (path.size() != 1) + return error::not_found; + const std::string &target = path[0]; + + std::optional<rsdos_dirent> result; + u8 dir_sector = 0; + u8 dir_file_index = 0; + auto const callback = [&result, &target, &dir_sector, &dir_file_index](u8 s, u8 i, const rsdos_dirent &dirent) + { + const bool found = get_filename_from_dirent(dirent) == target; + if (found) + { + result = dirent; + dir_sector = s; + dir_file_index = i; + } + return found; + }; + iterate_directory_entries(callback); + + if (!result) + return error::not_found; + + const size_t data_length = data.size(); + const u8 max_granule = maximum_granules(); + u8 granule = result->m_first_granule; + u8 granule_sector = 1; + size_t offset = 0; + u16 bytes_in_last_sector = 0; + u8 linked_granule = read_granule_map(granule); + + while (offset < data_length) + { + auto data_block = read_granule_sector(granule, granule_sector); + bytes_in_last_sector = (data_length - offset) > SECTOR_SIZE ? SECTOR_SIZE : data_length - offset; + data_block->write(0, data.data() + offset, bytes_in_last_sector); + offset += SECTOR_SIZE; + write_granule_map(granule, FILE_LAST_GRANULE_INDICATOR + granule_sector); + if (offset < data_length) + { + granule_sector++; + if (granule_sector > GRANULE_SECTOR_COUNT) + { + // Re-use linked granule or claim a new granule + if (linked_granule < max_granule) + { + granule = linked_granule; + linked_granule = read_granule_map(linked_granule); + } + else + { + auto [err, next_granule] = claim_granule(); + if (err) + return err; + write_granule_map(granule, next_granule); + granule = next_granule; + } + granule_sector = 1; + } + } + } + + // Free unused space + while (linked_granule < max_granule) + { + const u8 granule_to_free = linked_granule; + linked_granule = read_granule_map(granule_to_free); + write_granule_map(granule_to_free, 0xff); + } + + // Update directory entry + auto dir_block = read_sector(DIRECTORY_TRACK, dir_sector); + dir_block->w8(dir_file_index * DIRECTORY_ENTRY_SIZE + OFFSET_FILE_TYPE, determine_file_type(data)); + dir_block->w8(dir_file_index * DIRECTORY_ENTRY_SIZE + OFFSET_ASCII_FLAG, is_ascii(data) ? 0xff : 0x00); + dir_block->w16b(dir_file_index * DIRECTORY_ENTRY_SIZE + OFFSET_LAST_SECTOR_BYTES, bytes_in_last_sector); + + return std::error_condition(); +} + + +bool coco_rsdos_impl::is_ascii(const std::vector<u8> &data) const +{ + const size_t data_length = data.size(); + + if (data_length == 0) + return false; + + for (int i = 0; i < data_length; i++) + { + if (!(data[i] == 0x0d || data[i] == 0x0a || (data[i] >= 0x20 && data[i] < 0x60))) + return false; + } + + return true; +} + + +u8 coco_rsdos_impl::determine_file_type(const std::vector<u8> &data) const +{ + if (is_ascii(data)) + { + // TODO: Distinguish between BASIC code and text editor data + return FILE_TYPE_BASIC; + } + + const size_t data_length = data.size(); + // Binary BASIC code seems to begin with ff <16bit size> 26 + if (data_length > 4 && data[0] == 0xff && data[3] == 0x26 && ((data[1] << 8) | data[2]) == data_length - 3) + return FILE_TYPE_BASIC; + + // TODO: Distinguish between Machine Code and Basic Data + + return FILE_TYPE_MACHINE_CODE; +} + + +std::pair<std::error_condition, u8> coco_rsdos_impl::claim_granule() +{ + // Granules are likely not assigned in this order on hardware. + auto granule_block = read_sector(DIRECTORY_TRACK, 2); + for (int g = 0; g < maximum_granules(); g++) + { + if (granule_block->r8(g) == 0xff) + { + granule_block->w8(g, FILE_LAST_GRANULE_INDICATOR); + return std::make_pair(std::error_condition(), g); + } + } + return std::make_pair(error::no_space, 0); +} + + +void coco_rsdos_impl::write_granule_map(u8 granule, u8 map_data) +{ + auto granule_block = read_sector(DIRECTORY_TRACK, 2); + granule_block->w8(granule, map_data); +} + + +u8 coco_rsdos_impl::read_granule_map(u8 granule) const +{ + auto granule_block = read_sector(DIRECTORY_TRACK, 2); + return granule_block->r8(granule); } //------------------------------------------------- -// fsblk_t::block_t coco_rsdos_impl::read_sector +// coco_rsdos_impl::read_sector //------------------------------------------------- -fsblk_t::block_t coco_rsdos_impl::read_sector(int track, int sector) const +fsblk_t::block_t::ptr coco_rsdos_impl::read_sector(int track, int sector) const { // the CoCo RS-DOS world thinks in terms of tracks/sectors, but we have a block device // abstraction - return m_blockdev.get(track * 18 + sector - 1); + return m_blockdev.get(track * TRACK_SECTOR_COUNT + sector - 1); +} + + +fsblk_t::block_t::ptr coco_rsdos_impl::read_granule_sector(u8 granule, u8 sector) const +{ + // Track 17 does not hold granules + if (granule < (DIRECTORY_TRACK * TRACK_GRANULE_COUNT)) + return m_blockdev.get(granule * GRANULE_SECTOR_COUNT + sector - 1); + else + return m_blockdev.get((granule + TRACK_GRANULE_COUNT) * GRANULE_SECTOR_COUNT + sector - 1); } @@ -311,7 +568,7 @@ fsblk_t::block_t coco_rsdos_impl::read_sector(int track, int sector) const u8 coco_rsdos_impl::maximum_granules() const { u32 sector_count = m_blockdev.block_count(); - u32 granule_count = (sector_count / 9) - 2; + u32 granule_count = (sector_count / GRANULE_SECTOR_COUNT) - 2; return granule_count <= 0xFF ? u8(granule_count) : 0xFF; } @@ -327,7 +584,7 @@ std::optional<coco_rsdos_impl::rsdos_dirent> coco_rsdos_impl::dirent_from_path(c const std::string &target = path[0]; std::optional<rsdos_dirent> result; - auto callback = [&result, &target](const rsdos_dirent &dirent) + auto const callback = [&result, &target](u8 s, u8 i, const rsdos_dirent &dirent) { bool found = get_filename_from_dirent(dirent) == target; if (found) @@ -343,28 +600,29 @@ std::optional<coco_rsdos_impl::rsdos_dirent> coco_rsdos_impl::dirent_from_path(c // coco_rsdos_impl::iterate_directory_entries //------------------------------------------------- -void coco_rsdos_impl::iterate_directory_entries(const std::function<bool(const rsdos_dirent &dirent)> &callback) +template <typename T> +void coco_rsdos_impl::iterate_directory_entries(T &&callback) { bool done = false; for (int dir_sector = 3; !done && dir_sector <= 18; dir_sector++) { // read this directory sector - auto dir_block = read_sector(17, dir_sector); - const rsdos_dirent_sector §or = *reinterpret_cast<const rsdos_dirent_sector *>(dir_block.rodata()); + auto dir_block = read_sector(DIRECTORY_TRACK, dir_sector); + const rsdos_dirent_sector §or = *reinterpret_cast<const rsdos_dirent_sector *>(dir_block->rodata()); // and loop through all entries - for (const auto &ent : sector.m_entries) + for (int file_index = 0; file_index < 8; file_index++) { // 0xFF marks the end of the directory - if (ent.m_dirent.m_filename[0] == '\xFF') + if (sector.m_entries[file_index].m_dirent.m_filename[0] == '\xFF') { done = true; } else { // 0x00 marks a deleted file - if (ent.m_dirent.m_filename[0] != '\0') - done = callback(ent.m_dirent); + if (sector.m_entries[file_index].m_dirent.m_filename[0] != '\0') + done = callback(dir_sector, 0, sector.m_entries[file_index].m_dirent); } if (done) @@ -427,7 +685,7 @@ std::string coco_rsdos_impl::get_filename_from_dirent(const rsdos_dirent &dirent //------------------------------------------------- coco_rsdos_impl::granule_iterator::granule_iterator(coco_rsdos_impl &fs, const rsdos_dirent &dirent) - : m_granule_map(fs.read_sector(17, 2)) + : m_granule_map(fs.read_sector(DIRECTORY_TRACK, 2)) , m_current_granule(dirent.m_first_granule) , m_maximum_granules(fs.maximum_granules()) , m_last_sector_bytes((u16(dirent.m_last_sector_bytes_msb) << 8) | dirent.m_last_sector_bytes_lsb) @@ -450,13 +708,13 @@ bool coco_rsdos_impl::granule_iterator::next(u8 &granule, u16 &byte_count) if (m_current_granule) { std::optional<u8> next_granule; - const u8 *granule_map_data = m_granule_map.rodata(); + const u8 *granule_map_data = m_granule_map->rodata(); if (granule_map_data[*m_current_granule] < m_maximum_granules) { // this entry points to the next granule success = true; granule = *m_current_granule; - byte_count = 9 * 256; + byte_count = GRANULE_SECTOR_COUNT * SECTOR_SIZE; next_granule = granule_map_data[*m_current_granule]; // check for cycles, which should only happen if the disk is corrupt (or not in RS-DOS format) @@ -465,13 +723,13 @@ bool coco_rsdos_impl::granule_iterator::next(u8 &granule, u16 &byte_count) else m_visited_granules.set(*next_granule); } - else if (granule_map_data[*m_current_granule] >= 0xC0 && granule_map_data[*m_current_granule] <= 0xC9) + else if (granule_map_data[*m_current_granule] >= FILE_LAST_GRANULE_INDICATOR && granule_map_data[*m_current_granule] <= FILE_LAST_GRANULE_INDICATOR + GRANULE_SECTOR_COUNT) { // this is the last granule in the file success = true; granule = *m_current_granule; u16 sector_count = std::max(granule_map_data[*m_current_granule], u8(0xC1)) - 0xC1; - byte_count = sector_count * 256 + m_last_sector_bytes; + byte_count = sector_count * SECTOR_SIZE + m_last_sector_bytes; next_granule = std::nullopt; } else diff --git a/src/lib/formats/fs_fat.cpp b/src/lib/formats/fs_fat.cpp index f4cd99e36ed..8e47bbc5abe 100644 --- a/src/lib/formats/fs_fat.cpp +++ b/src/lib/formats/fs_fat.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Nathan Woods +// copyright-holders:Nathan Woods,Wilbert Pol /*************************************************************************** fs_fat.cpp @@ -7,11 +7,13 @@ PC FAT disk images Current Limitations: - - Read only - Only supports floppy disks - No FAT32 support - No Long Filenames Support + Removal of files is untested; floptool does not have a command to delete + a file. + ***************************************************************************** Master boot record format: @@ -66,13 +68,13 @@ 14 2 Reserved sector count (including boot sector) 16 1 Number of FATs (file allocation tables) 17 2 Number of root directory entries - 19 2 Total sectors (bits 0-15) + 19 2 Total sectors (0 if 0x10000 or more) 21 1 Media descriptor 22 2 Sectors per FAT 24 2 Sectors per track 26 2 Number of heads 28 4 Hidden sectors - 32 4 Total sectors (bits 16-47) + 32 4 Total sectors (0 if less than 0x10000) 36 1 Physical drive number 37 1 Current head 38 1 Signature @@ -147,6 +149,9 @@ #include "strformat.h" #include <optional> +#include <regex> +#include <string_view> + using namespace fs; @@ -163,41 +168,58 @@ namespace { class directory_entry { public: - static const int SIZE = 32; - - directory_entry(const fsblk_t::block_t &block, u32 offset) + static constexpr int SIZE = 32; + static constexpr int OFFSET_FNAME = 0; + static constexpr int FNAME_LENGTH = 11; + static constexpr int OFFSET_ATTRIBUTES = 11; + static constexpr int OFFSET_CREATE_DATETIME = 14; + static constexpr int OFFSET_START_CLUSTER_HI = 20; + static constexpr int OFFSET_MODIFIED_DATETIME = 22; + static constexpr int OFFSET_START_CLUSTER = 26; + static constexpr int OFFSET_FILE_SIZE = 28; + static constexpr u8 DELETED_FILE_MARKER = 0xe5; + static constexpr u8 ATTR_READ_ONLY = 0x01; + static constexpr u8 ATTR_HIDDEN = 0x02; + static constexpr u8 ATTR_SYSTEM = 0x04; + static constexpr u8 ATTR_VOLUME_LABEL = 0x08; + static constexpr u8 ATTR_DIRECTORY = 0x10; + static constexpr u8 ATTR_ARCHIVE = 0x20; + + directory_entry(fsblk_t::block_t::ptr block, u32 offset) : m_block(block) , m_offset(offset) { } - std::string_view raw_stem() const { return std::string_view((const char *) &m_block.rodata()[m_offset + 0], 8); } - std::string_view raw_ext() const { return std::string_view((const char *) &m_block.rodata()[m_offset + 8], 3); } - u8 attributes() const { return m_block.r8(m_offset + 11); } - u32 raw_create_datetime() const { return m_block.r32l(m_offset + 14); } - u32 raw_modified_datetime() const { return m_block.r32l(m_offset + 22); } - u32 start_cluster() const { return ((u32)m_block.r16l(m_offset + 20)) << 16 | m_block.r16l(m_offset + 26); } - u32 file_size() const { return m_block.r32l(m_offset + 28); } + std::string_view raw_stem() const { return m_block->rstr(m_offset + OFFSET_FNAME, 8); } + std::string_view raw_ext() const { return m_block->rstr(m_offset + OFFSET_FNAME + 8, 3); } + u8 attributes() const { return m_block->r8(m_offset + OFFSET_ATTRIBUTES); } + u32 raw_create_datetime() const { return m_block->r32l(m_offset + OFFSET_CREATE_DATETIME); } + u32 raw_modified_datetime() const { return m_block->r32l(m_offset + OFFSET_MODIFIED_DATETIME); } + u32 start_cluster() const { return ((u32)m_block->r16l(m_offset + OFFSET_START_CLUSTER_HI)) << 16 | m_block->r16l(m_offset + OFFSET_START_CLUSTER); } + u32 file_size() const { return m_block->r32l(m_offset + OFFSET_FILE_SIZE); } bool is_read_only() const { return (attributes() & 0x01) != 0x00; } bool is_hidden() const { return (attributes() & 0x02) != 0x00; } bool is_system() const { return (attributes() & 0x04) != 0x00; } bool is_volume_label() const { return (attributes() & 0x08) != 0x00; } + bool is_long_file_name() const { return attributes() == 0x0f; } bool is_subdirectory() const { return (attributes() & 0x10) != 0x00; } bool is_archive() const { return (attributes() & 0x20) != 0x00; } - bool is_deleted() const { return m_block.r8(m_offset) == DELETED_FILE_MARKER; } + bool is_deleted() const { return m_block->r8(m_offset) == DELETED_FILE_MARKER; } std::string name() const; meta_data metadata() const; -private: - static constexpr u8 DELETED_FILE_MARKER = 0xe5; + void set_file_size(u32 file_size) { m_block->w32l(m_offset + OFFSET_FILE_SIZE, file_size); } + void set_raw_modified_datetime(u32 datetime) { m_block->w32l(m_offset + OFFSET_MODIFIED_DATETIME, datetime); } + void mark_deleted() { m_block->w8(m_offset + OFFSET_FNAME, DELETED_FILE_MARKER); } - fsblk_t::block_t m_block; - u32 m_offset; +private: + fsblk_t::block_t::ptr m_block; + u32 m_offset; }; - // ======================> directory_span class directory_span @@ -218,7 +240,7 @@ class impl : public filesystem_t { public: // ctor/dtor - impl(fsblk_t &blockdev, fsblk_t::block_t &&boot_sector_block, std::vector<u8> &&file_allocation_table, u32 starting_sector, u32 sector_count, u16 reserved_sector_count, u8 bits_per_fat_entry); + impl(fsblk_t &blockdev, fsblk_t::block_t::ptr &&boot_sector_block, std::vector<u8> &&file_allocation_table, u32 starting_sector, u32 sector_count, u16 reserved_sector_count, u8 bits_per_fat_entry); virtual ~impl() = default; // accessors @@ -228,27 +250,60 @@ public: // virtuals virtual meta_data volume_metadata() override; - virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override; + virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override; + virtual std::error_condition remove(const std::vector<std::string> &path) override; // methods std::vector<u32> get_sectors_from_fat(const directory_entry &dirent) const; + // Boot sector settings + static constexpr u32 OFFSET_BYTES_PER_SECTOR = 0x0b; + static constexpr u32 OFFSET_CLUSTER_SECTOR_COUNT = 0x0d; + static constexpr u32 OFFSET_RESERVED_SECTOR_COUNT = 0x0e; + static constexpr u32 OFFSET_FAT_COUNT = 0x10; + static constexpr u32 OFFSET_DIRECTORY_ENTRY_COUNT = 0x11; + static constexpr u32 OFFSET_FAT_SECTOR_COUNT = 0x16; + private: - fsblk_t::block_t m_boot_sector_block; + static constexpr u32 FIRST_VALID_CLUSTER = 2; + + fsblk_t::block_t::ptr m_boot_sector_block; std::vector<u8> m_file_allocation_table; u32 m_starting_sector; u32 m_sector_count; u16 m_reserved_sector_count; u16 m_bytes_per_sector; + u16 m_root_directory_size; + u16 m_sectors_per_cluster; + u8 m_fat_count; + u16 m_fat_sector_count; u8 m_bits_per_fat_entry; + u32 m_last_cluster_indicator; + u32 m_last_valid_cluster; // methods std::optional<directory_entry> find_entity(const std::vector<std::string> &path) const; directory_span::ptr find_directory(std::vector<std::string>::const_iterator path_begin, std::vector<std::string>::const_iterator path_end) const; std::optional<directory_entry> find_child(const directory_span ¤t_dir, std::string_view target) const; - void iterate_directory_entries(const directory_span &dir, const std::function<bool(const directory_entry &dirent)> &callback) const; + template <typename T> void iterate_directory_entries(const directory_span &dir, T &&callback) const; + bool is_valid_short_filename(std::string const &filename); + std::error_condition build_direntry_filename(std::string const &filename, std::string &fname); + std::error_condition file_create_root(std::string &fname, u8 attributes = 0); + std::error_condition file_create_directory(directory_entry &dirent, std::string &fname, u8 attributes = 0); + std::error_condition file_create_sector(u32 sector, std::string &fname, u8 attributes); + std::error_condition initialize_directory(u32 directory_cluster, u32 parent_cluster); + std::error_condition initialize_directory_entry(fsblk_t::block_t &dirblk, u32 offset, std::string_view fname, u8 attributes, u32 start_cluster); + std::error_condition free_clusters(u32 start_cluster); + void clear_cluster_sectors(u32 cluster, u8 fill_byte); + u32 first_cluster_sector(u32 cluster); + u32 get_next_cluster(u32 cluster); + void set_next_cluster(u32 cluster, u32 next_cluster); + u32 find_free_cluster(); + }; @@ -297,7 +352,7 @@ private: namespace { bool validate_filename(std::string_view name) { - auto is_invalid_filename_char = [](char ch) + auto const is_invalid_filename_char = [] (char ch) { return ch == '\0' || strchr("\\/:*?\"<>|", ch); }; @@ -316,15 +371,27 @@ util::arbitrary_datetime decode_fat_datetime(u32 dt) util::arbitrary_datetime result; memset(&result, 0, sizeof(result)); - result.year = ((dt >> 25) & 0x7F) + 1980; - result.month = (dt >> 21) & 0x0F; - result.day_of_month = (dt >> 16) & 0x1F; - result.hour = (dt >> 11) & 0x1F; - result.minute = (dt >> 5) & 0x3F; - result.second = ((dt >> 0) & 0x1F) * 2; + result.year = ((dt >> 25) & 0x7f) + 1980; + result.month = (dt >> 21) & 0x0f; + result.day_of_month = (dt >> 16) & 0x1f; + result.hour = (dt >> 11) & 0x1f; + result.minute = (dt >> 5) & 0x3f; + result.second = ((dt >> 0) & 0x1f) * 2; return result; } +u32 encode_now_fat_datetime() +{ + auto now = util::arbitrary_datetime::now(); + + return u32((((now.year - 1980) & 0x7f) << 25) | + ((now.month & 0x0f) << 21) | + ((now.day_of_month & 0x1f) << 16) | + ((now.hour & 0x1f) << 11) | + ((now.minute & 0x3f) << 5) | + ((now.second >> 1) & 0x1f)); +} + } @@ -354,7 +421,7 @@ bool fs::fat_image::can_read() const bool fs::fat_image::can_write() const { - return false; + return true; } @@ -385,8 +452,8 @@ char fs::fat_image::directory_separator() const std::vector<meta_description> fs::fat_image::volume_meta_description() const { std::vector<meta_description> results; - results.emplace_back(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 11; }, "Volume name, up to 11 characters"); - results.emplace_back(meta_name::oem_name, "", false, [](const meta_value &m) { return m.as_string().size() <= 8; }, "OEM name, up to 8 characters"); + results.emplace_back(meta_name::name, "UNTITLED", false, [] (const meta_value &m) { return validate_filename(m.as_string()); }, "Volume name"); + results.emplace_back(meta_name::oem_name, "", false, [] (const meta_value &m) { return m.as_string().size() <= 8; }, "OEM name, up to 8 characters"); return results; } @@ -398,7 +465,7 @@ std::vector<meta_description> fs::fat_image::volume_meta_description() const std::vector<meta_description> fs::fat_image::file_meta_description() const { std::vector<meta_description> results; - results.emplace_back(meta_name::name, "", false, [](const meta_value &m) { return validate_filename(m.as_string()); }, "File name"); + results.emplace_back(meta_name::name, "", false, [] (const meta_value &m) { return validate_filename(m.as_string()); }, "File name"); results.emplace_back(meta_name::creation_date, util::arbitrary_datetime::now(), false, nullptr, "Creation time"); results.emplace_back(meta_name::modification_date, util::arbitrary_datetime::now(), false, nullptr, "Modification time"); results.emplace_back(meta_name::length, 0, true, nullptr, "Size of the file in bytes"); @@ -413,7 +480,7 @@ std::vector<meta_description> fs::fat_image::file_meta_description() const std::vector<meta_description> fs::fat_image::directory_meta_description() const { std::vector<meta_description> results; - results.emplace_back(meta_name::name, "", false, [](const meta_value &m) { return validate_filename(m.as_string()); }, "File name"); + results.emplace_back(meta_name::name, "", false, [] (const meta_value &m) { return validate_filename(m.as_string()); }, "File name"); results.emplace_back(meta_name::creation_date, util::arbitrary_datetime::now(), false, nullptr, "Creation time"); results.emplace_back(meta_name::modification_date, util::arbitrary_datetime::now(), false, nullptr, "Modification time"); return results; @@ -427,19 +494,19 @@ std::vector<meta_description> fs::fat_image::directory_meta_description() const std::unique_ptr<filesystem_t> fs::fat_image::mount_partition(fsblk_t &blockdev, u32 starting_sector, u32 sector_count, u8 bits_per_fat_entry) { // load the boot sector block and get some basic info - fsblk_t::block_t boot_sector_block = blockdev.get(starting_sector); - u16 reserved_sector_count = boot_sector_block.r16l(14); + fsblk_t::block_t::ptr boot_sector_block = blockdev.get(starting_sector); + u16 reserved_sector_count = boot_sector_block->r16l(impl::OFFSET_RESERVED_SECTOR_COUNT); // load all file allocation table sectors - u32 fat_count = boot_sector_block.r8(16); - u32 sectors_per_fat = boot_sector_block.r16l(22); - u16 bytes_per_sector = boot_sector_block.r16l(11); + u32 fat_count = boot_sector_block->r8(impl::OFFSET_FAT_COUNT); + u32 sectors_per_fat = boot_sector_block->r16l(impl::OFFSET_FAT_SECTOR_COUNT); + u16 bytes_per_sector = boot_sector_block->r16l(impl::OFFSET_BYTES_PER_SECTOR); std::vector<u8> file_allocation_table; file_allocation_table.reserve(fat_count * sectors_per_fat * bytes_per_sector); for (auto i = 0; i < fat_count * sectors_per_fat; i++) { - fsblk_t::block_t fatblk = blockdev.get(starting_sector + reserved_sector_count + i); - file_allocation_table.insert(file_allocation_table.end(), fatblk.rodata(), fatblk.rodata() + bytes_per_sector); + fsblk_t::block_t::ptr fatblk = blockdev.get(starting_sector + reserved_sector_count + i); + file_allocation_table.insert(file_allocation_table.end(), fatblk->rodata(), fatblk->rodata() + bytes_per_sector); } // and return the implementation @@ -481,16 +548,24 @@ meta_data directory_entry::metadata() const // impl ctor //------------------------------------------------- -impl::impl(fsblk_t &blockdev, fsblk_t::block_t &&boot_sector_block, std::vector<u8> &&file_allocation_table, u32 starting_sector, u32 sector_count, u16 reserved_sector_count, u8 bits_per_fat_entry) +impl::impl(fsblk_t &blockdev, fsblk_t::block_t::ptr &&boot_sector_block, std::vector<u8> &&file_allocation_table, u32 starting_sector, u32 sector_count, u16 reserved_sector_count, u8 bits_per_fat_entry) : filesystem_t(blockdev, 512) , m_boot_sector_block(std::move(boot_sector_block)) , m_file_allocation_table(std::move(file_allocation_table)) , m_starting_sector(starting_sector) , m_sector_count(sector_count) , m_reserved_sector_count(reserved_sector_count) - , m_bytes_per_sector(m_boot_sector_block.r16l(11)) + , m_bytes_per_sector(m_boot_sector_block->r16l(OFFSET_BYTES_PER_SECTOR)) + , m_root_directory_size(m_boot_sector_block->r16l(OFFSET_DIRECTORY_ENTRY_COUNT)) + , m_sectors_per_cluster(m_boot_sector_block->r8(OFFSET_CLUSTER_SECTOR_COUNT)) + , m_fat_count(m_boot_sector_block->r8(OFFSET_FAT_COUNT)) + , m_fat_sector_count(m_boot_sector_block->r16l(OFFSET_FAT_SECTOR_COUNT)) , m_bits_per_fat_entry(bits_per_fat_entry) + , m_last_cluster_indicator(((u64)1 << bits_per_fat_entry) - 1) + , m_last_valid_cluster(m_last_cluster_indicator - 0x10) { + if (m_bytes_per_sector == 0) + m_bytes_per_sector = 512; } @@ -500,9 +575,26 @@ impl::impl(fsblk_t &blockdev, fsblk_t::block_t &&boot_sector_block, std::vector< meta_data impl::volume_metadata() { + std::vector<std::string> root_path; + directory_span::ptr root_dir = find_directory(root_path.begin(), root_path.end()); + assert(root_dir); + + // Get the volume label from the root directory, not the extended BPB (whose name field may not be kept up-to-date even when it exists) meta_data results; - results.set(meta_name::name, m_boot_sector_block.rstr(43, 11)); - results.set(meta_name::oem_name, m_boot_sector_block.rstr(3, 8)); + auto const callback = [&results] (const directory_entry &dirent) + { + if (dirent.is_volume_label()) + { + results.set(meta_name::name, dirent.name()); + return true; + } + return false; + }; + iterate_directory_entries(*root_dir, callback); + if (!results.has(meta_name::name)) + results.set(meta_name::name, "UNTITLED"); + + results.set(meta_name::oem_name, m_boot_sector_block->rstr(3, 8)); return results; } @@ -511,13 +603,13 @@ meta_data impl::volume_metadata() // impl::metadata //------------------------------------------------- -std::pair<err_t, meta_data> impl::metadata(const std::vector<std::string> &path) +std::pair<std::error_condition, meta_data> impl::metadata(const std::vector<std::string> &path) { std::optional<directory_entry> dirent = find_entity(path); if (!dirent) - return std::make_pair(ERR_NOT_FOUND, meta_data()); + return std::make_pair(error::not_found, meta_data()); - return std::make_pair(ERR_OK, dirent->metadata()); + return std::make_pair(std::error_condition(), dirent->metadata()); } @@ -525,21 +617,24 @@ std::pair<err_t, meta_data> impl::metadata(const std::vector<std::string> &path) // impl::directory_contents //------------------------------------------------- -std::pair<err_t, std::vector<dir_entry>> impl::directory_contents(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<dir_entry>> impl::directory_contents(const std::vector<std::string> &path) { directory_span::ptr dir = find_directory(path.begin(), path.end()); if (!dir) - return std::make_pair(ERR_NOT_FOUND, std::vector<dir_entry>()); + return std::make_pair(error::not_found, std::vector<dir_entry>()); std::vector<dir_entry> results; - auto callback = [&results](const directory_entry &dirent) + auto const callback = [&results] (const directory_entry &dirent) { - dir_entry_type entry_type = dirent.is_subdirectory() ? dir_entry_type::dir : dir_entry_type::file; - results.emplace_back(entry_type, dirent.metadata()); + if (!dirent.is_volume_label()) + { + dir_entry_type entry_type = dirent.is_subdirectory() ? dir_entry_type::dir : dir_entry_type::file; + results.emplace_back(entry_type, dirent.metadata()); + } return false; }; iterate_directory_entries(*dir, callback); - return std::make_pair(ERR_OK, std::move(results)); + return std::make_pair(std::error_condition(), std::move(results)); } @@ -547,12 +642,14 @@ std::pair<err_t, std::vector<dir_entry>> impl::directory_contents(const std::vec // impl::file_read //------------------------------------------------- -std::pair<err_t, std::vector<u8>> impl::file_read(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<u8>> impl::file_read(const std::vector<std::string> &path) { // find the file std::optional<directory_entry> dirent = find_entity(path); - if (!dirent || dirent->is_subdirectory()) - return std::make_pair(ERR_NOT_FOUND, std::vector<u8>()); + if (!dirent) + return std::make_pair(error::not_found, std::vector<u8>()); + if (dirent->is_subdirectory()) + return std::make_pair(error::invalid_name, std::vector<u8>()); // get the list of sectors for this file std::vector<u32> sectors = get_sectors_from_fat(*dirent); @@ -564,15 +661,353 @@ std::pair<err_t, std::vector<u8>> impl::file_read(const std::vector<std::string> // and add data from all sectors for (u32 sector : sectors) { - fsblk_t::block_t block = m_blockdev.get(sector); - const u8 *data = block.rodata(); - size_t length = std::min((size_t)dirent->file_size() - result.size(), (size_t)block.size()); + fsblk_t::block_t::ptr block = m_blockdev.get(sector); + const u8 *data = block->rodata(); + size_t length = std::min((size_t)dirent->file_size() - result.size(), (size_t)block->size()); result.insert(result.end(), data, data + length); } - return std::make_pair(ERR_OK, std::move(result)); + return std::make_pair(std::error_condition(), std::move(result)); +} + + +bool impl::is_valid_short_filename(std::string const &filename) +{ + /* + Valid characters in DOS file names: + - Upper case letters A-Z + - Numbers 0-9 + - Space (though there is no way to identify a trailing space) + - ! # $ % & ( ) - @ ^ _ ` { } ~ + - Characters 128-255, except e5 (though the code page is indeterminate) + We currently do not check for characters 128-255. + */ + std::regex filename_regex("([A-Z0-9!#\\$%&\\(\\)\\-@^_`\\{\\}~]{0,8})(\\.([A-Z0-9!#\\$%&\\(\\)\\-@^_`\\{\\}~]{0,3}))?"); + return std::regex_match(filename, filename_regex); +} + + +std::error_condition impl::build_direntry_filename(std::string const &filename, std::string &fname) +{ + std::regex filename_regex("([A-Z0-9!#\\$%&\\(\\)\\-@^_`\\{\\}~]{0,8})(\\.([A-Z0-9!#\\$%&\\(\\)\\-@^_`\\{\\}~]{0,3}))?"); + std::smatch smatch; + if (!std::regex_match(filename, smatch, filename_regex)) + return error::invalid_name; + if (smatch.size() != 4) + return error::invalid_name; + + fname.resize(directory_entry::FNAME_LENGTH, ' '); + + for (int i = 0; i < 8 && i < smatch.str(1).size(); i++) + fname[i] = smatch.str(1)[i]; + + for (int j = 0; j < 3 && j < smatch.str(3).size(); j++) + fname[8 + j] = smatch.str(3)[j]; + + return std::error_condition(); +} + + +std::error_condition impl::file_create(const std::vector<std::string> &path, const meta_data &meta) +{ + std::string filename = meta.get_string(meta_name::name, ""); + std::string fname; + std::error_condition err = build_direntry_filename(filename, fname); + if (err) + return err; + + if (path.empty()) + { + return file_create_root(fname); + } + else + { + // Make sure that all parts of the path exist, creating the path parts as needed. + std::optional<directory_entry> dirent = find_entity(path); + if (!dirent) + { + std::vector<std::string> partial_path; + std::optional<directory_entry> parent_entry; + for (auto const &path_part : path) + { + partial_path.emplace_back(path_part); + std::optional<directory_entry> dir_entry = find_entity(partial_path); + if (!dir_entry) + { + if (!is_valid_short_filename(path_part)) + return error::invalid_name; + + std::string part_fname; + std::error_condition err = build_direntry_filename(path_part, part_fname); + if (err) + return err; + err = !parent_entry ? + file_create_root(part_fname, directory_entry::ATTR_DIRECTORY) : + file_create_directory(parent_entry.value(), part_fname, directory_entry::ATTR_DIRECTORY); + if (err) + return err; + + dir_entry = find_entity(partial_path); + if (!dir_entry) + return error::invalid_name; + + err = initialize_directory(dir_entry->start_cluster(), parent_entry ? parent_entry->start_cluster() : 0); + if (err) + return err; + } + else + { + if (!dir_entry->is_subdirectory()) + return error::invalid_name; + } + parent_entry = dir_entry; + } + + dirent = find_entity(path); + if (!dirent) + return error::invalid_name; + } + + return file_create_directory(*dirent, fname); + } +} + + +std::error_condition impl::initialize_directory(u32 directory_cluster, u32 parent_cluster) +{ + clear_cluster_sectors(directory_cluster, 0x00); + + auto dirblk = m_blockdev.get(first_cluster_sector(directory_cluster)); + + // Add special directory entries for . and .. + std::string dir_fname; + dir_fname.resize(directory_entry::FNAME_LENGTH, ' '); + dir_fname[0] = '.'; + std::error_condition err = initialize_directory_entry(*dirblk, 0, dir_fname, directory_entry::ATTR_DIRECTORY, directory_cluster); + if (err) + return err; + + dir_fname[1] = '.'; + err = initialize_directory_entry(*dirblk, directory_entry::SIZE, dir_fname, directory_entry::ATTR_DIRECTORY, parent_cluster); + if (err) + return err; + + return std::error_condition(); +} + + +std::error_condition impl::initialize_directory_entry(fsblk_t::block_t &dirblk, u32 offset, std::string_view fname, u8 attributes, u32 start_cluster) +{ + if (fname.size() != directory_entry::FNAME_LENGTH) + return error::invalid_name; + + for (int i = 0; i < directory_entry::SIZE; i += 4) + dirblk.w32l(offset + i, 0); + + dirblk.wstr(offset + directory_entry::OFFSET_FNAME, fname); + dirblk.w8(offset + directory_entry::OFFSET_ATTRIBUTES, attributes); + dirblk.w32l(offset + directory_entry::OFFSET_CREATE_DATETIME, encode_now_fat_datetime()); + dirblk.w32l(offset + directory_entry::OFFSET_MODIFIED_DATETIME, encode_now_fat_datetime()); + dirblk.w16l(offset + directory_entry::OFFSET_START_CLUSTER_HI, u16(start_cluster >> 16)); + dirblk.w16l(offset + directory_entry::OFFSET_START_CLUSTER, u16(start_cluster & 0xffff)); + + return std::error_condition(); +} + + +std::error_condition impl::file_create_root(std::string &fname, u8 attributes) +{ + const u32 first_directory_sector = m_starting_sector + m_reserved_sector_count + ((u32)m_file_allocation_table.size() / m_bytes_per_sector); + const u32 directory_sector_count = (m_root_directory_size * directory_entry::SIZE) / m_bytes_per_sector; + for (u32 sector = first_directory_sector; sector < first_directory_sector + directory_sector_count; sector++) + { + std::error_condition err = file_create_sector(sector, fname, attributes); + if (err != error::not_found) + return err; + } + return error::no_space; +} + + +std::error_condition impl::file_create_directory(directory_entry &dirent, std::string &fname, u8 attributes) +{ + u32 current_cluster = dirent.start_cluster(); + do { + const u32 first_sector = first_cluster_sector(current_cluster); + for (int i = 0; i < m_sectors_per_cluster; i++) + { + std::error_condition err = file_create_sector(first_sector + i, fname, attributes); + if (err != error::not_found) + return err; + } + + // File could not be created yet. Move to next cluster, allocating a new cluster when needed. + u32 next_cluster = get_next_cluster(current_cluster); + if (next_cluster >= m_last_valid_cluster) + { + next_cluster = find_free_cluster(); + if (next_cluster == 0) + return error::no_space; + + set_next_cluster(current_cluster, next_cluster); + set_next_cluster(next_cluster, m_last_cluster_indicator); + + clear_cluster_sectors(next_cluster, 0x00); + } + current_cluster = next_cluster; + } while (current_cluster > FIRST_VALID_CLUSTER && current_cluster < m_last_valid_cluster); + return error::no_space; +} + + +u32 impl::first_cluster_sector(u32 cluster) +{ + return m_starting_sector + m_reserved_sector_count + + ((u32)m_file_allocation_table.size() / m_bytes_per_sector) + + ((m_root_directory_size + 1) / dirents_per_sector()) + + ((cluster - FIRST_VALID_CLUSTER) * m_sectors_per_cluster); +} + + +void impl::clear_cluster_sectors(u32 cluster, u8 fill_byte) +{ + const u32 sector = first_cluster_sector(cluster); + for (int i = 0; i < m_sectors_per_cluster; i++) + { + auto dirblk = m_blockdev.get(sector + i); + for (int offset = 0; offset < m_bytes_per_sector; offset++) + dirblk->w8(offset, fill_byte); + } } +// Returns error::not_found when no room could be found to create the file in the sector. +std::error_condition impl::file_create_sector(u32 sector, std::string &fname, u8 attributes) +{ + auto dirblk = m_blockdev.get(sector); + for (u32 blkoffset = 0; blkoffset < m_bytes_per_sector; blkoffset += directory_entry::SIZE) + { + u8 first_byte = dirblk->r8(blkoffset); + if (first_byte == 0x00 || first_byte == directory_entry::DELETED_FILE_MARKER) + { + u32 start_cluster = find_free_cluster(); + if (start_cluster == 0) + return error::no_space; + set_next_cluster(start_cluster, m_last_cluster_indicator); + + std::error_condition err = initialize_directory_entry(*dirblk, blkoffset, fname, attributes, start_cluster); + if (err) + return err; + + return std::error_condition(); + } + } + return error::not_found; +} + + +std::error_condition impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) +{ + std::optional<directory_entry> dirent = find_entity(path); + if (!dirent) + return error::not_found; + + if (dirent->is_subdirectory()) + return error::invalid_name; + + u32 current_length = dirent->file_size(); + const size_t data_length = data.size(); + const u32 bytes_per_cluster = m_sectors_per_cluster * bytes_per_sector(); + const u32 current_clusters = (current_length + bytes_per_cluster - 1) / bytes_per_cluster; + const u32 required_clusters = (data_length + bytes_per_cluster - 1) / bytes_per_cluster; + + if (required_clusters > current_clusters) + { + u32 current_cluster = dirent->start_cluster(); + u32 next_cluster = 0; + do { + next_cluster = get_next_cluster(current_cluster); + if (next_cluster < FIRST_VALID_CLUSTER) + return error::invalid_block; + } while (next_cluster < m_last_valid_cluster); + for (int i = current_clusters; i < required_clusters; i++) + { + u32 free_cluster = find_free_cluster(); + if (free_cluster < FIRST_VALID_CLUSTER) + return error::no_space; + + set_next_cluster(current_cluster, free_cluster); + set_next_cluster(free_cluster, m_last_cluster_indicator); + current_cluster = free_cluster; + } + } + if (required_clusters < current_clusters) + { + u32 current_cluster = dirent->start_cluster(); + for (int i = 0; i < required_clusters; i++) + { + current_cluster = get_next_cluster(current_cluster); + } + u32 next_cluster = get_next_cluster(current_cluster); + set_next_cluster(current_cluster, m_last_cluster_indicator); + + std::error_condition err = free_clusters(next_cluster); + if (err) + return err; + } + + auto sectors = get_sectors_from_fat(*dirent); + size_t offset = 0; + for (auto sector : sectors) + { + if (offset < data_length) + { + auto datablk = m_blockdev.get(sector); + u32 bytes = (data_length - offset > m_bytes_per_sector) ? m_bytes_per_sector : data_length - offset; + datablk->write(0, data.data() + offset, bytes); + offset += m_bytes_per_sector; + } + } + + dirent->set_raw_modified_datetime(encode_now_fat_datetime()); + dirent->set_file_size(data_length); + + return std::error_condition(); +} + + +std::error_condition impl::remove(const std::vector<std::string> &path) +{ + if (path.size() != 0) + return error::unsupported; + + std::optional<directory_entry> dirent = find_entity(path); + if (!dirent) + return std::error_condition(); + + // Removing directories is not supported yet + if (dirent->is_subdirectory()) + return error::unsupported; + + dirent->mark_deleted(); + + return std::error_condition(); +} + + +std::error_condition impl::free_clusters(u32 start_cluster) +{ + while (start_cluster < m_last_valid_cluster) + { + if (start_cluster < FIRST_VALID_CLUSTER) + return error::invalid_block; + + u32 next_cluster = get_next_cluster(start_cluster); + set_next_cluster(start_cluster, 0); + start_cluster = next_cluster; + } + return std::error_condition(); +} + //------------------------------------------------- // impl::get_sectors_from_fat //------------------------------------------------- @@ -584,22 +1019,20 @@ std::vector<u32> impl::get_sectors_from_fat(const directory_entry &dirent) const results.reserve(dirent.file_size() / bytes_per_sector()); // get critical information - u8 sectors_per_cluster = m_boot_sector_block.r8(13); - u16 root_directory_entry_count = m_boot_sector_block.r16l(17); - u16 root_directory_sector_count = (root_directory_entry_count + 1) / dirents_per_sector(); - u32 fat_sector_count = (u32)(m_file_allocation_table.size() / m_bytes_per_sector); + u16 root_directory_sector_count = (m_root_directory_size + 1) / dirents_per_sector(); + u32 fat_sector_count = m_fat_count * m_fat_sector_count; u32 data_starting_sector = m_starting_sector + m_reserved_sector_count + fat_sector_count + root_directory_sector_count; - u32 data_cluster_count = (m_sector_count - data_starting_sector) / sectors_per_cluster; + u32 data_cluster_count = (m_sector_count - data_starting_sector) / m_sectors_per_cluster; // find all clusters u32 start_cluster_mask = ((u64)1 << m_bits_per_fat_entry) - 1; u32 cluster = dirent.start_cluster() & start_cluster_mask; - while (cluster >= 2 && cluster < (data_cluster_count + 2)) + while (cluster >= FIRST_VALID_CLUSTER && cluster < (data_cluster_count + 2)) { // add the sectors for this cluster - for (auto i = 0; i < sectors_per_cluster; i++) - results.push_back(data_starting_sector + (cluster - 2) * sectors_per_cluster + i); + for (auto i = 0; i < m_sectors_per_cluster; i++) + results.push_back(data_starting_sector + (cluster - FIRST_VALID_CLUSTER) * m_sectors_per_cluster + i); // determine the bit position of this entry u32 entry_bit_position = cluster * m_bits_per_fat_entry; @@ -623,8 +1056,8 @@ std::vector<u32> impl::get_sectors_from_fat(const directory_entry &dirent) const } // normalize special cluster IDs - if (new_cluster > ((u32)1 << m_bits_per_fat_entry) - 0x10) - new_cluster |= ~(((u32)1 << m_bits_per_fat_entry) - 1); + if (new_cluster > m_last_valid_cluster) + new_cluster |= ~m_last_cluster_indicator; } cluster = new_cluster; } @@ -633,6 +1066,91 @@ std::vector<u32> impl::get_sectors_from_fat(const directory_entry &dirent) const } +u32 impl::get_next_cluster(u32 cluster) +{ + u32 entry_bit_position = cluster * m_bits_per_fat_entry; + u32 new_cluster = 0; + if (entry_bit_position + m_bits_per_fat_entry <= m_file_allocation_table.size() * 8) + { + u32 current_bit = 0; + while (current_bit < m_bits_per_fat_entry) + { + u32 pos = entry_bit_position + current_bit; + u32 shift = pos % 8; + u32 bit_count = std::min(8 - shift, m_bits_per_fat_entry - current_bit); + u32 bits = (m_file_allocation_table[pos / 8] >> shift) & ((1 << bit_count) - 1); + + new_cluster |= (bits << current_bit); + current_bit += bit_count; + } + } + return new_cluster; +} + + +void impl::set_next_cluster(u32 cluster, u32 next_cluster) +{ + const u32 m_fat_start_sector = m_starting_sector + m_reserved_sector_count; + const u32 entry_bit_position = cluster * m_bits_per_fat_entry; + if (entry_bit_position + m_bits_per_fat_entry <= m_file_allocation_table.size() * 8) + { + u32 current_bit = 0; + while (current_bit < m_bits_per_fat_entry) + { + u32 pos = entry_bit_position + current_bit; + u32 shift = pos % 8; + u32 bit_count = std::min(8 - shift, m_bits_per_fat_entry - current_bit); + u32 byte_pos = pos / 8; + u32 mask = ((1 << bit_count) - 1); + m_file_allocation_table[byte_pos] = (m_file_allocation_table[byte_pos] & ~(mask << shift)) | ((next_cluster & mask) << shift); + next_cluster = next_cluster >> bit_count; + current_bit += bit_count; + // Write back to backing blocks + for (int i = 0; i < m_fat_count; i++) + { + u32 fat_sector = m_fat_start_sector + (i * m_fat_sector_count) + (byte_pos / m_bytes_per_sector); + auto fatblk = m_blockdev.get(fat_sector); + fatblk->w8(byte_pos % m_bytes_per_sector, m_file_allocation_table[byte_pos]); + } + } + } +} + + +// Returns 0 if no free cluster could be found +u32 impl::find_free_cluster() +{ + u16 root_directory_sector_count = (m_root_directory_size + 1) / dirents_per_sector(); + u32 fat_sector_count = m_fat_count * m_fat_sector_count; + u32 data_starting_sector = m_starting_sector + m_reserved_sector_count + fat_sector_count + root_directory_sector_count; + u32 data_cluster_count = (m_sector_count - data_starting_sector) / m_sectors_per_cluster; + + for (u32 cluster = FIRST_VALID_CLUSTER; cluster < (data_cluster_count + 2); cluster++) + { + u32 entry_bit_position = cluster * m_bits_per_fat_entry; + + if (entry_bit_position + m_bits_per_fat_entry <= m_file_allocation_table.size() * 8) + { + u32 new_cluster = 0; + u32 current_bit = 0; + while (current_bit < m_bits_per_fat_entry) + { + u32 pos = entry_bit_position + current_bit; + u32 shift = pos % 8; + u32 bit_count = std::min(8 - shift, m_bits_per_fat_entry - current_bit); + u32 bits = (m_file_allocation_table[pos / 8] >> shift) & ((1 << bit_count) - 1); + + new_cluster |= (bits << current_bit); + current_bit += bit_count; + } + + if (new_cluster == 0) + return cluster; + } + } + return 0; +} + //------------------------------------------------- // impl::find_entity //------------------------------------------------- @@ -661,8 +1179,7 @@ directory_span::ptr impl::find_directory(std::vector<std::string>::const_iterato { // the root directory is treated differently u32 first_sector = m_starting_sector + m_reserved_sector_count + (u32)m_file_allocation_table.size() / m_bytes_per_sector; - u16 directory_entry_count = m_boot_sector_block.r16l(17); - directory_span::ptr current_dir = std::make_unique<root_directory_span>(*this, first_sector, directory_entry_count); + directory_span::ptr current_dir = std::make_unique<root_directory_span>(*this, first_sector, m_root_directory_size); // traverse the directory for (auto iter = path_begin; iter != path_end; iter++) @@ -687,7 +1204,7 @@ directory_span::ptr impl::find_directory(std::vector<std::string>::const_iterato std::optional<directory_entry> impl::find_child(const directory_span ¤t_dir, std::string_view target) const { std::optional<directory_entry> result; - auto callback = [&result, target](const directory_entry &dirent) + auto const callback = [&result, target] (const directory_entry &dirent) { bool found = dirent.name() == target; if (found) @@ -703,17 +1220,18 @@ std::optional<directory_entry> impl::find_child(const directory_span ¤t_di // impl::iterate_directory_entries //------------------------------------------------- -void impl::iterate_directory_entries(const directory_span &dir, const std::function<bool(const directory_entry &dirent)> &callback) const +template <typename T> +void impl::iterate_directory_entries(const directory_span &dir, T &&callback) const { std::vector<u32> sectors = dir.get_directory_sectors(); for (u32 sector : sectors) { bool done = false; - fsblk_t::block_t block = m_blockdev.get(sector); + fsblk_t::block_t::ptr block = m_blockdev.get(sector); for (u32 index = 0; !done && (index < dirents_per_sector()); index++) { directory_entry dirent(block, index * 32); - if (dirent.raw_stem()[0] != 0x00 && !dirent.is_deleted()) + if (dirent.raw_stem()[0] != 0x00 && !dirent.is_deleted() && !dirent.is_long_file_name()) { // get the filename std::string_view stem = trim_end_spaces(dirent.raw_stem()); @@ -815,6 +1333,12 @@ void pc_fat_image::enumerate_f(floppy_enumerator &fe) const fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 737280, "pc_fat_dsdd", "PC FAT 3.5\" dual-sided double density"); fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_35, floppy_image::DSHD, 1474560, "pc_fat_dshd", "PC FAT 3.5\" dual-sided high density"); fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_35, floppy_image::DSED, 2949120, "pc_fat_dsed", "PC FAT 3.5\" dual-sided extra density"); + fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_525, floppy_image::SSDD, 163840, "pc_fat_525ssdd_8", "PC FAT 5.25\" single-sided double density, 8 sectors/track"); + fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_525, floppy_image::SSDD, 184320, "pc_fat_525ssdd", "PC FAT 5.25\" single-sided double density, 9 sectors/track"); + fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_525, floppy_image::DSDD, 327680, "pc_fat_525dsdd_8", "PC FAT 5.25\" dual-sided double density, 8 sectors/track"); + fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_525, floppy_image::DSDD, 368640, "pc_fat_525dsdd", "PC FAT 5.25\" dual-sided double density, 9 sectors/track"); + fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_525, floppy_image::DSQD, 737280, "pc_fat_525dsqd", "PC FAT 5.25\" dual-sided quad density"); + fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_525, floppy_image::DSHD, 1228800, "pc_fat_525dshd", "PC FAT 5.25\" dual-sided high density"); } diff --git a/src/lib/formats/fs_hp98x5.cpp b/src/lib/formats/fs_hp98x5.cpp index a8eb139ef7a..f070d01b30f 100644 --- a/src/lib/formats/fs_hp98x5.cpp +++ b/src/lib/formats/fs_hp98x5.cpp @@ -107,17 +107,17 @@ namespace { virtual meta_data volume_metadata() override; - virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; - virtual err_t file_create(const std::vector<std::string> &path, const meta_data &meta) override; + virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override; - virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override; - virtual err_t file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override; + virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override; - virtual err_t format(const meta_data &meta) override; + virtual std::error_condition format(const meta_data &meta) override; private: // Map of used/free sectors @@ -202,7 +202,7 @@ namespace { using dir_t = std::vector<entry>; - err_t parse_filename(const std::string& name, std::string& basename, int& bpr, u8& file_type) const; + std::error_condition parse_filename(const std::string& name, std::string& basename, int& bpr, u8& file_type) const; bool validate_filename(const std::string& s) const; void ensure_dir_loaded(); std::pair<dir_t, sect_map> decode_dir() const; @@ -323,10 +323,10 @@ meta_data hp98x5_impl::volume_metadata() return res; } -std::pair<err_t, meta_data> hp98x5_impl::metadata(const std::vector<std::string> &path) +std::pair<std::error_condition, meta_data> hp98x5_impl::metadata(const std::vector<std::string> &path) { if (path.size() != 1) { - return std::make_pair(ERR_NOT_FOUND, meta_data{}); + return std::make_pair(error::not_found, meta_data{}); } std::string basename; @@ -334,21 +334,21 @@ std::pair<err_t, meta_data> hp98x5_impl::metadata(const std::vector<std::string> u8 file_type; auto err = parse_filename(path.front(), basename, bpr, file_type); - if (err != ERR_OK) { - return std::make_pair(ERR_NOT_FOUND, meta_data{}); + if (err) { + return std::make_pair(error::not_found, meta_data{}); } ensure_dir_loaded(); auto it = scan_dir(m_dir, basename); if (it == m_dir.end()) { - return std::make_pair(ERR_NOT_FOUND, meta_data{}); + return std::make_pair(error::not_found, meta_data{}); } auto meta = get_metadata(*it); - return std::make_pair(ERR_OK, std::move(meta)); + return std::make_pair(std::error_condition(), std::move(meta)); } -std::pair<err_t, std::vector<dir_entry>> hp98x5_impl::directory_contents(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<dir_entry>> hp98x5_impl::directory_contents(const std::vector<std::string> &path) { if (path.empty()) { ensure_dir_loaded(); @@ -360,16 +360,16 @@ std::pair<err_t, std::vector<dir_entry>> hp98x5_impl::directory_contents(const s dir_entries.emplace_back(dir_entry_type::file, std::move(meta)); } } - return std::make_pair(ERR_OK, dir_entries); + return std::make_pair(std::error_condition(), dir_entries); } else { - return std::make_pair(ERR_NOT_FOUND, std::vector<dir_entry>{}); + return std::make_pair(error::not_found, std::vector<dir_entry>{}); } } -err_t hp98x5_impl::file_create(const std::vector<std::string> &path, const meta_data &meta) +std::error_condition hp98x5_impl::file_create(const std::vector<std::string> &path, const meta_data &meta) { if (!path.empty()) { - return ERR_INVALID; + return error::invalid_name; } auto name = meta.get_string(meta_name::name); @@ -381,9 +381,13 @@ err_t hp98x5_impl::file_create(const std::vector<std::string> &path, const meta_ // Parse & validate filename // Extension must be specified auto err = parse_filename(name, basename, bpr, file_type); - if (err != ERR_OK || file_type == NO_FILE_TYPE) { - return ERR_INVALID; + if (err) { + return err; + } + if (file_type == NO_FILE_TYPE) { + return error::invalid_name; } + if (bpr < 0) { // Use default bpr when not specified bpr = DEF_BPR; @@ -393,7 +397,7 @@ err_t hp98x5_impl::file_create(const std::vector<std::string> &path, const meta_ ensure_dir_loaded(); auto it = scan_dir(m_dir, basename); if (it != m_dir.end()) { - return ERR_INVALID; + return error::already_exists; } // Find a free entry in directory @@ -404,7 +408,7 @@ err_t hp98x5_impl::file_create(const std::vector<std::string> &path, const meta_ m_dir.emplace_back(entry()); it2 = m_dir.end() - 1; } else { - return ERR_NO_SPACE; + return error::no_space; } } @@ -421,13 +425,13 @@ err_t hp98x5_impl::file_create(const std::vector<std::string> &path, const meta_ store_dir_map(); - return ERR_OK; + return std::error_condition(); } -std::pair<err_t, std::vector<u8>> hp98x5_impl::file_read(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<u8>> hp98x5_impl::file_read(const std::vector<std::string> &path) { if (path.size() != 1) { - return std::make_pair(ERR_NOT_FOUND, std::vector<u8>{}); + return std::make_pair(error::not_found, std::vector<u8>{}); } std::string basename; int bpr; @@ -435,10 +439,10 @@ std::pair<err_t, std::vector<u8>> hp98x5_impl::file_read(const std::vector<std:: auto err = parse_filename(path.front(), basename, bpr, file_type); - if (err == ERR_OK) { + if (!err) { auto it = find_file(basename, bpr, file_type); if (it == m_dir.end()) { - err = ERR_NOT_FOUND; + err = error::not_found; } else { auto file_data = get_sector_range(it->m_1st_sect, it->m_sectors); if (file_type == TEXT_TYPE) { @@ -446,16 +450,16 @@ std::pair<err_t, std::vector<u8>> hp98x5_impl::file_read(const std::vector<std:: } else { file_data.resize(it->m_size); } - return std::make_pair(ERR_OK, std::move(file_data)); + return std::make_pair(std::error_condition(), std::move(file_data)); } } return std::make_pair(err, std::vector<u8>{}); } -err_t hp98x5_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) +std::error_condition hp98x5_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) { if (path.size() != 1) { - return ERR_NOT_FOUND; + return error::not_found; } std::string basename; @@ -463,13 +467,13 @@ err_t hp98x5_impl::file_write(const std::vector<std::string> &path, const std::v u8 file_type; auto err = parse_filename(path.front(), basename, bpr, file_type); - if (err != ERR_OK) { + if (err) { return err; } // Check that file already exists auto it = find_file(basename, bpr, file_type); if (it == m_dir.end()) { - return ERR_NOT_FOUND; + return error::not_found; } // De-allocate current blocks @@ -488,7 +492,7 @@ err_t hp98x5_impl::file_write(const std::vector<std::string> &path, const std::v // Allocate space it->m_sectors = (data_ptr->size() + SECTOR_SIZE - 1) / SECTOR_SIZE; if (!allocate(it->m_sectors, it->m_1st_sect)) { - return ERR_NO_SPACE; + return error::no_space; } // Store file content @@ -498,10 +502,10 @@ err_t hp98x5_impl::file_write(const std::vector<std::string> &path, const std::v // Update directory & map store_dir_map(); - return ERR_OK; + return std::error_condition(); } -err_t hp98x5_impl::format(const meta_data &meta) +std::error_condition hp98x5_impl::format(const meta_data &meta) { bool is_ds = m_blockdev.block_count() == DS_SECTORS; @@ -540,11 +544,11 @@ err_t hp98x5_impl::format(const meta_data &meta) // Interleave factor w16b(out_ins, m_fmt_interleave[ m_fs_type ]); - m_blockdev.get(0).copy(0, sys_rec.data(), sys_rec.size()); + m_blockdev.get(0)->write(0, sys_rec.data(), sys_rec.size()); // 9825 & 9831 have a backup copy of system record in spare track but 9845 hasn't, go figure if (m_fs_type != FS_TYPE_9845) { - m_blockdev.get(m_spare_start).copy(0, sys_rec.data(), sys_rec.size()); + m_blockdev.get(m_spare_start)->write(0, sys_rec.data(), sys_rec.size()); } m_dir.clear(); @@ -553,10 +557,10 @@ err_t hp98x5_impl::format(const meta_data &meta) store_dir_map(); - return ERR_OK; + return std::error_condition(); } -err_t hp98x5_impl::parse_filename(const std::string& name, std::string& basename, int& bpr, u8& file_type) const +std::error_condition hp98x5_impl::parse_filename(const std::string& name, std::string& basename, int& bpr, u8& file_type) const { // General form of filenames: // basename[[.&bpr].ext] @@ -579,18 +583,18 @@ err_t hp98x5_impl::parse_filename(const std::string& name, std::string& basename if (p2 != std::string::npos) { // [p1+1 p2) should have "&bpr" if (name[ p1 + 1 ] != '&') { - return ERR_INVALID; + return error::invalid_name; } if (!std::all_of(name.cbegin() + p1 + 2, name.cbegin() + p2, [](char c) { return c >= '0' && c <= '9'; })) { - return ERR_INVALID; + return error::invalid_name; } try { bpr = std::stoul(std::string{name, p1 + 2, p2 - p1 - 2}); } catch (...) { - return ERR_INVALID; + return error::invalid_name; } if (bpr < 4 || bpr > 32767 || (bpr & 1) != 0) { - return ERR_INVALID; + return error::invalid_name; } } else { p2 = p1; @@ -607,7 +611,7 @@ err_t hp98x5_impl::parse_filename(const std::string& name, std::string& basename } } if (file_type == NO_FILE_TYPE) { - return ERR_INVALID; + return error::invalid_name; } } } @@ -615,9 +619,9 @@ err_t hp98x5_impl::parse_filename(const std::string& name, std::string& basename // [0 p1) has "basename" basename = std::string(name, 0, p1); if (validate_filename(basename)) { - return ERR_OK; + return std::error_condition(); } else { - return ERR_INVALID; + return error::invalid_name; } } @@ -659,14 +663,14 @@ void hp98x5_impl::ensure_dir_loaded() // 8 Count of data tracks // 9 Interleave factor // 10..127 N/U - u16 sects_per_track = sys_rec.r16b(2); - u16 tot_tracks = sys_rec.r16b(4); - u16 spare_track = sys_rec.r16b(6); - u16 dir_start = sys_rec.r16b(8); - u16 av_start = sys_rec.r16b(10); - u16 av_end = sys_rec.r16b(12); - u16 sys_tracks = sys_rec.r16b(14); - u16 data_tracks = sys_rec.r16b(16); + u16 sects_per_track = sys_rec->r16b(2); + u16 tot_tracks = sys_rec->r16b(4); + u16 spare_track = sys_rec->r16b(6); + u16 dir_start = sys_rec->r16b(8); + u16 av_start = sys_rec->r16b(10); + u16 av_end = sys_rec->r16b(12); + u16 sys_tracks = sys_rec->r16b(14); + u16 data_tracks = sys_rec->r16b(16); m_img_tracks = m_blockdev.block_count() / SECTORS; @@ -1155,7 +1159,7 @@ std::vector<u8> hp98x5_impl::get_sector_range(lba_t first, unsigned size) const for (lba_t idx = first; idx < first + size; idx++) { auto data_sect = m_blockdev.get(u32(idx)); - memcpy(ptr, data_sect.rodata(), SECTOR_SIZE); + data_sect->read(0, ptr, SECTOR_SIZE); ptr += SECTOR_SIZE; } return res; @@ -1169,7 +1173,7 @@ void hp98x5_impl::store_sector_range(lba_t first, const std::vector<u8>& data) for (lba_t idx = first; idx < first + sects; idx++) { u32 count = std::min<u32>(to_go, SECTOR_SIZE); auto blk = m_blockdev.get(u32(idx)); - blk.copy(0, ptr, count); + blk->write(0, ptr, count); ptr += count; to_go -= count; } diff --git a/src/lib/formats/fs_hplif.cpp b/src/lib/formats/fs_hplif.cpp index 48dde255cb4..8a3d2c52da4 100644 --- a/src/lib/formats/fs_hplif.cpp +++ b/src/lib/formats/fs_hplif.cpp @@ -64,27 +64,27 @@ public: u8 size() const; private: - const impl & m_fs; - fsblk_t::block_t m_block; - u8 m_sector; - u32 m_sector_count; + const impl & m_fs; + fsblk_t::block_t::ptr m_block; + u8 m_sector; + u32 m_sector_count; }; impl(fsblk_t &blockdev); virtual ~impl() = default; virtual meta_data volume_metadata() override; - virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override; private: - fsblk_t::block_t read_sector(u32 starting_sector) const; + fsblk_t::block_t::ptr read_sector(u32 starting_sector) const; std::optional<hplif_dirent> dirent_from_path(const std::vector<std::string> &path) const; void iterate_directory_entries(const std::function<bool(const hplif_dirent &dirent)> &callback) const; util::arbitrary_datetime decode_datetime(const hplif_time *time) const; meta_data metadata_from_dirent(const hplif_dirent &dirent) const; - err_t format(const meta_data &meta) override; + std::error_condition format(const meta_data &meta) override; }; // methods @@ -120,10 +120,10 @@ const char *fs::hplif_image::description() const void fs::hplif_image::enumerate_f(floppy_enumerator &fe) const { - fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 630784, "hp_lif_9121_format_1", "HP 9212 LIF 3.5\" dual-sided double density Format 1"); + fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 630784, "hp_lif_9121_format_1", "HP 9121 LIF 3.5\" dual-sided double density Format 1"); fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 709632, "hp_lif_9121_format_2", "HP 9121 LIF 3.5\" dual-sided double density Format 2"); fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 788480, "hp_lif_9121_format_3", "HP 9121 LIF 3.5\" dual-sided double density Format 3"); - fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::SSDD, 286720, "hp_lif_9121_format_4", "HP 9121 LIF 3.5\" single-sided double density Format 4"); + fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::SSDD, 270336, "hp_lif_9121_format_4", "HP 9121 LIF 3.5\" single-sided double density Format 4"); fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 737280, "hp_lif_9121_format_16", "HP 9121 LIF 3.5\" dual-sided double density Format 16"); fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSHD, 1261568, "hp_lif_9122_format_014", "HP 9122 LIF 3.5\" dual-sided high density Format 0, 1, 4"); @@ -252,11 +252,11 @@ impl::impl(fsblk_t &blockdev) meta_data impl::volume_metadata() { auto block = read_sector(0); - std::string_view disk_name = std::string_view((const char *) block.rodata() + 2, 6); + std::string_view disk_name = block->rstr(2, 6); meta_data results; results.set(meta_name::name, strtrimright_hplif(disk_name)); - results.set(meta_name::creation_date, decode_datetime(reinterpret_cast<const hplif_time *>(block.rodata() + 36))); + results.set(meta_name::creation_date, decode_datetime(reinterpret_cast<const hplif_time *>(block->rodata() + 36))); return results; } @@ -265,13 +265,13 @@ meta_data impl::volume_metadata() // impl::metadata //------------------------------------------------- -std::pair<err_t, meta_data> impl::metadata(const std::vector<std::string> &path) +std::pair<std::error_condition, meta_data> impl::metadata(const std::vector<std::string> &path) { std::optional<hplif_dirent> dirent = dirent_from_path(path); if (!dirent) - return std::make_pair(ERR_NOT_FOUND, meta_data()); + return std::make_pair(error::not_found, meta_data()); - return std::make_pair(ERR_OK, metadata_from_dirent(*dirent)); + return std::make_pair(std::error_condition(), metadata_from_dirent(*dirent)); } @@ -279,7 +279,7 @@ std::pair<err_t, meta_data> impl::metadata(const std::vector<std::string> &path) // impl::directory_contents //------------------------------------------------- -std::pair<err_t, std::vector<dir_entry>> impl::directory_contents(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<dir_entry>> impl::directory_contents(const std::vector<std::string> &path) { std::vector<dir_entry> results; auto callback = [this, &results](const hplif_dirent &ent) @@ -288,7 +288,7 @@ std::pair<err_t, std::vector<dir_entry>> impl::directory_contents(const std::vec return false; }; iterate_directory_entries(callback); - return std::make_pair(ERR_OK, std::move(results)); + return std::make_pair(std::error_condition(), std::move(results)); } @@ -296,12 +296,12 @@ std::pair<err_t, std::vector<dir_entry>> impl::directory_contents(const std::vec // impl::file_read //------------------------------------------------- -std::pair<err_t, std::vector<u8>> impl::file_read(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<u8>> impl::file_read(const std::vector<std::string> &path) { // find the file std::optional<hplif_dirent> dirent = dirent_from_path(path); if (!dirent) - return std::make_pair(ERR_NOT_FOUND, std::vector<u8>()); + return std::make_pair(error::not_found, std::vector<u8>()); // and get the data u32 sector_count = big_endianize_int32(dirent->m_sector_count); @@ -316,7 +316,7 @@ std::pair<err_t, std::vector<u8>> impl::file_read(const std::vector<std::string> while (iter.next()) result.insert(result.end(), (const u8 *)iter.data(), (const u8 *)iter.data() + 256); - return std::make_pair(ERR_OK, std::move(result)); + return std::make_pair(std::error_condition(), std::move(result)); } @@ -324,7 +324,7 @@ std::pair<err_t, std::vector<u8>> impl::file_read(const std::vector<std::string> // impl::read_sector //------------------------------------------------- -fsblk_t::block_t impl::read_sector(u32 sector) const +fsblk_t::block_t::ptr impl::read_sector(u32 sector) const { return m_blockdev.get(sector); } @@ -359,10 +359,10 @@ std::optional<impl::hplif_dirent> impl::dirent_from_path(const std::vector<std:: void impl::iterate_directory_entries(const std::function<bool(const hplif_dirent &dirent)> &callback) const { - fsblk_t::block_t block = m_blockdev.get(0); - block_iterator iter(*this, block.r32b(8), block.r32b(16)); + fsblk_t::block_t::ptr block = m_blockdev.get(0); + block_iterator iter(*this, block->r32b(8), block->r32b(16)); - if (block.r16b(0) != 0x8000) + if (block->r16b(0) != 0x8000) { return; } @@ -461,30 +461,30 @@ bool impl::block_iterator::next() const void *impl::block_iterator::data() const { - return m_block.rodata(); + return m_block->rodata(); } //------------------------------------------------- // impl::format //------------------------------------------------- -err_t impl::format(const meta_data &meta) +std::error_condition impl::format(const meta_data &meta) { std::string volume_name = meta.get_string(meta_name::name, "B9826 "); - fsblk_t::block_t block = m_blockdev.get(0); + fsblk_t::block_t::ptr block = m_blockdev.get(0); if (volume_name.size() < 6) volume_name.insert(volume_name.end(), 6 - volume_name.size(), ' '); if (volume_name.size() > 6) volume_name.resize(6); - block.w16b(0, 0x8000); // LIF magic - block.wstr(2, volume_name); - block.w32b(8, 2); // directory start - block.w16b(12, 0x1000); // LIF identifier - block.w32b(16, 14); // directory size - block.w16b(20, 1); // LIF version - return ERR_OK; + block->w16b(0, 0x8000); // LIF magic + block->wstr(2, volume_name); + block->w32b(8, 2); // directory start + block->w16b(12, 0x1000); // LIF identifier + block->w32b(16, 14); // directory size + block->w16b(20, 1); // LIF version + return std::error_condition(); } //------------------------------------------------- @@ -493,7 +493,7 @@ err_t impl::format(const meta_data &meta) const std::array<impl::hplif_dirent, 8> &impl::block_iterator::dirent_data() const { - return *reinterpret_cast<const std::array<impl::hplif_dirent, 8> *>(m_block.rodata()); + return *reinterpret_cast<const std::array<impl::hplif_dirent, 8> *>(m_block->rodata()); } } // anonymous namespace diff --git a/src/lib/formats/fs_isis.cpp b/src/lib/formats/fs_isis.cpp index 3b79851522b..fb79314a360 100644 --- a/src/lib/formats/fs_isis.cpp +++ b/src/lib/formats/fs_isis.cpp @@ -45,17 +45,17 @@ namespace { virtual meta_data volume_metadata() override; - virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; - virtual err_t file_create(const std::vector<std::string> &path, const meta_data &meta) override; + virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override; - virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override; - virtual err_t file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override; + virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override; - virtual err_t format(const meta_data &meta) override; + virtual std::error_condition format(const meta_data &meta) override; private: using sect_map = std::bitset<DD_SECTORS>; @@ -225,24 +225,24 @@ meta_data isis_impl::volume_metadata() return res; } -std::pair<err_t, meta_data> isis_impl::metadata(const std::vector<std::string> &path) +std::pair<std::error_condition, meta_data> isis_impl::metadata(const std::vector<std::string> &path) { if (path.size() != 1) { - return std::make_pair(ERR_NOT_FOUND, meta_data{}); + return std::make_pair(error::not_found, meta_data{}); } ensure_dir_loaded(); auto it = scan_dir(path.front()); if (it == m_dir.end()) { - return std::make_pair(ERR_NOT_FOUND, meta_data{}); + return std::make_pair(error::not_found, meta_data{}); } auto meta = get_metadata(*it); - return std::make_pair(ERR_OK, std::move(meta)); + return std::make_pair(std::error_condition(), std::move(meta)); } -std::pair<err_t, std::vector<dir_entry>> isis_impl::directory_contents(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<dir_entry>> isis_impl::directory_contents(const std::vector<std::string> &path) { if (path.empty()) { ensure_dir_loaded(); @@ -254,16 +254,16 @@ std::pair<err_t, std::vector<dir_entry>> isis_impl::directory_contents(const std dir_entries.emplace_back(dir_entry_type::file, std::move(meta)); } } - return std::make_pair(ERR_OK, dir_entries); + return std::make_pair(std::error_condition(), dir_entries); } else { - return std::make_pair(ERR_NOT_FOUND, std::vector<dir_entry>{}); + return std::make_pair(error::not_found, std::vector<dir_entry>{}); } } -err_t isis_impl::file_create(const std::vector<std::string> &path, const meta_data &meta) +std::error_condition isis_impl::file_create(const std::vector<std::string> &path, const meta_data &meta) { if (!path.empty()) { - return ERR_INVALID; + return error::invalid_name; } auto name = meta.get_string(meta_name::name); @@ -274,13 +274,13 @@ err_t isis_impl::file_create(const std::vector<std::string> &path, const meta_da std::string filename = name.substr(0, pt_pos); if (filename.size() < 1 || filename.size() > 6 || !validate_filename(filename)) { - return ERR_INVALID; + return error::invalid_name; } if (pt_pos != std::string::npos) { auto ext = name.substr(pt_pos + 1); if (ext.size() < 1 || ext.size() > 3 || !validate_filename(ext)) { - return ERR_INVALID; + return error::invalid_name; } filename.push_back('.'); filename += ext; @@ -288,14 +288,14 @@ err_t isis_impl::file_create(const std::vector<std::string> &path, const meta_da // Check that file can be created by user if (!user_can_create(filename)) { - return ERR_INVALID; + return error::unsupported; } // Check that file doesn't exist ensure_dir_loaded(); auto it = scan_dir(filename); if (it != m_dir.end()) { - return ERR_INVALID; + return error::already_exists; } // Find a free entry in directory @@ -305,13 +305,13 @@ err_t isis_impl::file_create(const std::vector<std::string> &path, const meta_da } } if (it == m_dir.end()) { - return ERR_NO_SPACE; + return error::no_space; } // Allocate space lba_list lbas; if (!allocate(0, lbas)) { - return ERR_NO_SPACE; + return error::no_space; } // Fill dir entry @@ -326,42 +326,42 @@ err_t isis_impl::file_create(const std::vector<std::string> &path, const meta_da // Update directory & map store_dir_map(); - return ERR_OK; + return std::error_condition(); } -std::pair<err_t, std::vector<u8>> isis_impl::file_read(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<u8>> isis_impl::file_read(const std::vector<std::string> &path) { const auto& [ lbas, size ] = find_file(path); if (lbas != nullptr) { auto file_data = get_file_content(*lbas, size); - return std::make_pair(ERR_OK, std::move(file_data)); + return std::make_pair(std::error_condition(), std::move(file_data)); } - return std::make_pair(ERR_NOT_FOUND, std::vector<u8>{}); + return std::make_pair(error::not_found, std::vector<u8>{}); } -err_t isis_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) +std::error_condition isis_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) { if (path.size() != 1) { - return ERR_NOT_FOUND; + return error::not_found; } // Check that file is user-writeable const auto& filename = path.front(); if (!user_can_write(filename)) { - return ERR_INVALID; + return error::unsupported; } // ISIS.T0 can only be 2944 bytes long bool is_t0 = filename == ISIS_T0; if (is_t0 && data.size() != ISIS_T0_SIZE) { - return ERR_INVALID; + return error::incorrect_size; } // Check that file already exists ensure_dir_loaded(); auto it = scan_dir(filename); if (it == m_dir.end()) { - return ERR_NOT_FOUND; + return error::not_found; } // De-allocate current blocks @@ -372,7 +372,7 @@ err_t isis_impl::file_write(const std::vector<std::string> &path, const std::vec if (is_t0) { allocate_t0(lbas); } else if (!allocate(data.size(), lbas)) { - return ERR_NO_SPACE; + return error::no_space; } // Update dir entry @@ -386,10 +386,10 @@ err_t isis_impl::file_write(const std::vector<std::string> &path, const std::vec // Update directory & map store_dir_map(); - return ERR_OK; + return std::error_condition(); } -err_t isis_impl::format(const meta_data &meta) +std::error_condition isis_impl::format(const meta_data &meta) { entry dir_e; dir_e.m_alloc_state = DIR_IN_USE; @@ -442,7 +442,7 @@ err_t isis_impl::format(const meta_data &meta) } store_dir_map(); - return ERR_OK; + return std::error_condition(); } isis_impl::lba_t isis_impl::ts_2_lba(const track_sect &ts) const @@ -786,14 +786,14 @@ isis_impl::lba_list isis_impl::get_file_allocation(lba_t first_link, unsigned si res.push_back(curr_map); // Get a linkage block auto map_sect = m_blockdev.get(curr_map); - auto prev_ptr = lba_from_2b(map_sect , 0); - auto next_ptr = lba_from_2b(map_sect , 2); + auto prev_ptr = lba_from_2b(*map_sect , 0); + auto next_ptr = lba_from_2b(*map_sect , 2); if (prev_lba != prev_ptr) { throw std::runtime_error(util::string_format("Incorrect backward linking in sector %d", curr_map)); } // Scan all pointers in linkage block for (u32 i = 4; i < SECTOR_SIZE; i += 2) { - auto ptr = lba_from_2b(map_sect , i); + auto ptr = lba_from_2b(*map_sect , i); if (size > 0) { if (ptr < 0) { throw std::runtime_error(util::string_format("Unexpected end of pointer list in sector %d", curr_map)); @@ -839,7 +839,7 @@ std::vector<u8> isis_impl::get_file_content(const lba_list& sects, unsigned size if ((idx % (PTRS_PER_BLOCK + 1)) != 0) { unsigned to_copy = std::min(size, SECTOR_SIZE); auto data_sect = m_blockdev.get(sects[ idx ]); - memcpy(res.data() + pos, data_sect.rodata(), to_copy); + data_sect->read(0, res.data() + pos, to_copy); size -= to_copy; pos += to_copy; } @@ -856,7 +856,7 @@ void isis_impl::store_file_content(const lba_list& sects, const std::vector<u8>& lba_t link_lba = sects[ idx ]; auto blk = m_blockdev.get(link_lba); // Pointer to previous block - lba_to_2b(prev_ptr, blk, 0); + lba_to_2b(prev_ptr, *blk, 0); prev_ptr = link_lba; lba_t next_ptr; if (sects.size() - idx > (PTRS_PER_BLOCK + 1)) { @@ -867,10 +867,10 @@ void isis_impl::store_file_content(const lba_list& sects, const std::vector<u8>& next_ptr = -1; } // Pointer to next block - lba_to_2b(next_ptr, blk, 2); + lba_to_2b(next_ptr, *blk, 2); // Pointers to data blocks for (unsigned j = 0; j < PTRS_PER_BLOCK; j++) { - lba_to_2b((j + idx + 1 < sects.size()) ? sects[ j + idx + 1 ] : -1, blk, 4 + 2 * j); + lba_to_2b((j + idx + 1 < sects.size()) ? sects[ j + idx + 1 ] : -1, *blk, 4 + 2 * j); } } // Write data blocks @@ -883,7 +883,7 @@ void isis_impl::store_file_content(const lba_list& sects, const std::vector<u8>& } u32 count = std::min<u32>(to_go, SECTOR_SIZE); auto blk = m_blockdev.get(sects[ idx ]); - blk.copy(0, ptr, count); + blk->write(0, ptr, count); ptr += count; to_go -= count; } diff --git a/src/lib/formats/fs_oric_jasmin.cpp b/src/lib/formats/fs_oric_jasmin.cpp index 8cc79d917a1..db24a4a93f8 100644 --- a/src/lib/formats/fs_oric_jasmin.cpp +++ b/src/lib/formats/fs_oric_jasmin.cpp @@ -56,20 +56,20 @@ public: virtual ~oric_jasmin_impl() = default; virtual meta_data volume_metadata() override; - virtual err_t volume_metadata_change(const meta_data &info) override; - virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override; - virtual err_t metadata_change(const std::vector<std::string> &path, const meta_data &meta) override; + virtual std::error_condition volume_metadata_change(const meta_data &info) override; + virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override; + virtual std::error_condition metadata_change(const std::vector<std::string> &path, const meta_data &meta) override; - virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; - virtual err_t rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) override; - virtual err_t remove(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; + virtual std::error_condition rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) override; + virtual std::error_condition remove(const std::vector<std::string> &path) override; - virtual err_t file_create(const std::vector<std::string> &path, const meta_data &meta) override; + virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override; - virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override; - virtual err_t file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override; + virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override; - virtual err_t format(const meta_data &meta) override; + virtual std::error_condition format(const meta_data &meta) override; static bool validate_filename(std::string name); @@ -88,7 +88,7 @@ private: static std::string file_name_prepare(std::string name); static bool file_is_system(const u8 *entry); meta_data file_metadata(const u8 *entry); - std::tuple<fsblk_t::block_t, u32, bool> file_find(std::string name); + std::tuple<fsblk_t::block_t::ptr, u32, bool> file_find(std::string name); }; } @@ -196,40 +196,40 @@ bool oric_jasmin_impl::validate_filename(std::string name) return name.size() > 0 && name.size() <= 8; } -err_t oric_jasmin_impl::format(const meta_data &meta) +std::error_condition oric_jasmin_impl::format(const meta_data &meta) { std::string volume_name = meta.get_string(meta_name::name, "UNTITLED"); u32 blocks = m_blockdev.block_count(); - m_blockdev.fill(0x6c); + m_blockdev.fill_all(0x6c); u32 bblk = 20*17; auto fmap = m_blockdev.get(bblk); u32 off = 0; for(u32 blk = 0; blk != blocks; blk += 17) { if(blk == bblk) - fmap.w24l(off, 0x07fff); + fmap->w24l(off, 0x07fff); else - fmap.w24l(off, 0x1ffff); + fmap->w24l(off, 0x1ffff); off += 3; } for(u32 blk = blocks; blk != 17*42*2; blk += 17) { - fmap.w24l(off, 0x800000); + fmap->w24l(off, 0x800000); off += 3; } - fmap.w8(0xf6, 0x80); - fmap.w8(0xf7, 0x80); + fmap->w8(0xf6, 0x80); + fmap->w8(0xf7, 0x80); volume_name.resize(8, ' '); - fmap.wstr(0xf8, volume_name); + fmap->wstr(0xf8, volume_name); auto bdir = m_blockdev.get(20*17+1); - bdir.fill(0xff); - bdir.w16l(0, 0x0000); - bdir.w16l(2, 0x0000); + bdir->fill(0xff); + bdir->w16l(0, 0x0000); + bdir->w16l(2, 0x0000); - return ERR_OK; + return std::error_condition(); } meta_data oric_jasmin_impl::volume_metadata() @@ -237,21 +237,21 @@ meta_data oric_jasmin_impl::volume_metadata() meta_data res; auto bdir = m_blockdev.get(20*17); int len = 8; - while(len > 0 && bdir.rodata()[0xf8 + len - 1] == ' ') + while(len > 0 && bdir->rodata()[0xf8 + len - 1] == ' ') len--; - res.set(meta_name::name, bdir.rstr(0xf8, len)); + res.set(meta_name::name, bdir->rstr(0xf8, len)); return res; } -err_t oric_jasmin_impl::volume_metadata_change(const meta_data &meta) +std::error_condition oric_jasmin_impl::volume_metadata_change(const meta_data &meta) { if(meta.has(meta_name::name)) { std::string volume_name = meta.get_string(meta_name::name); volume_name.resize(8, ' '); - m_blockdev.get(20*17).wstr(0xf8, volume_name); + m_blockdev.get(20*17)->wstr(0xf8, volume_name); } - return ERR_OK; + return std::error_condition(); } std::string oric_jasmin_impl::file_name_prepare(std::string fname) @@ -315,28 +315,28 @@ meta_data oric_jasmin_impl::file_metadata(const u8 *entry) else { u16 ref = get_u16be(entry); auto dblk = m_blockdev.get(cs_to_block(ref)); - res.set(meta_name::loading_address, dblk.r16l(2)); - res.set(meta_name::length, dblk.r16l(4)); + res.set(meta_name::loading_address, dblk->r16l(2)); + res.set(meta_name::length, dblk->r16l(4)); } return res; } -std::tuple<fsblk_t::block_t, u32, bool> oric_jasmin_impl::file_find(std::string name) +std::tuple<fsblk_t::block_t::ptr, u32, bool> oric_jasmin_impl::file_find(std::string name) { name = file_name_prepare(name); auto bdir = m_blockdev.get(20*17+1); for(;;) { for(u32 i = 0; i != 14; i ++) { u32 off = 4 + i*18; - u16 fref = bdir.r16b(off); - if(ref_valid(fref) || file_is_system(bdir.rodata()+off)) { - if(memcmp(bdir.rodata() + off + 3, name.data(), 12)) { - bool sys = file_is_system(bdir.rodata() + off); + u16 fref = bdir->r16b(off); + if(ref_valid(fref) || file_is_system(bdir->rodata()+off)) { + if(memcmp(bdir->rodata() + off + 3, name.data(), 12)) { + bool sys = file_is_system(bdir->rodata() + off); return std::make_tuple(bdir, off, sys); } } } - u16 ref = bdir.r16b(2); + u16 ref = bdir->r16b(2); if(!ref || !ref_valid(ref)) return std::make_tuple(bdir, 0U, false); @@ -344,29 +344,29 @@ std::tuple<fsblk_t::block_t, u32, bool> oric_jasmin_impl::file_find(std::string } } -std::pair<err_t, meta_data> oric_jasmin_impl::metadata(const std::vector<std::string> &path) +std::pair<std::error_condition, meta_data> oric_jasmin_impl::metadata(const std::vector<std::string> &path) { if(path.size() != 1) - return std::make_pair(ERR_NOT_FOUND, meta_data()); + return std::make_pair(error::not_found, meta_data()); auto [bdir, off, sys] = file_find(path[0]); std::ignore = sys; if(!off) - return std::make_pair(ERR_NOT_FOUND, meta_data()); + return std::make_pair(error::not_found, meta_data()); - return std::make_pair(ERR_OK, file_metadata(bdir.rodata() + off)); + return std::make_pair(std::error_condition(), file_metadata(bdir->rodata() + off)); } -err_t oric_jasmin_impl::metadata_change(const std::vector<std::string> &path, const meta_data &meta) +std::error_condition oric_jasmin_impl::metadata_change(const std::vector<std::string> &path, const meta_data &meta) { if(path.size() != 1) - return ERR_NOT_FOUND; + return error::not_found; auto [bdir, off, sys] = file_find(path[0]); if(!off) - return ERR_NOT_FOUND; + return error::not_found; - u8 *entry = bdir.data() + off; + u8 *entry = bdir->data() + off; if(meta.has(meta_name::locked)) entry[0x02] = meta.get_flag(meta_name::locked) ? 'L' : 'U'; if(meta.has(meta_name::name)) @@ -374,33 +374,33 @@ err_t oric_jasmin_impl::metadata_change(const std::vector<std::string> &path, co if(meta.has(meta_name::sequential)) entry[0x0f] = meta.get_flag(meta_name::sequential) ? 'D' : 'S'; if(!sys && meta.has(meta_name::loading_address)) - m_blockdev.get(cs_to_block(get_u16be(entry))).w16l(2, meta.get_number(meta_name::loading_address)); + m_blockdev.get(cs_to_block(get_u16be(entry)))->w16l(2, meta.get_number(meta_name::loading_address)); - return ERR_OK; + return std::error_condition(); } -std::pair<err_t, std::vector<dir_entry>> oric_jasmin_impl::directory_contents(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<dir_entry>> oric_jasmin_impl::directory_contents(const std::vector<std::string> &path) { - std::pair<err_t, std::vector<dir_entry>> res; + std::pair<std::error_condition, std::vector<dir_entry>> res; if(path.size() != 0) { - res.first = ERR_NOT_FOUND; + res.first = error::not_found; return res; } - res.first = ERR_OK; + res.first = std::error_condition(); auto bdir = m_blockdev.get(20*17+1); for(;;) { for(u32 i = 0; i != 14; i ++) { u32 off = 4 + i*18; - u16 fref = bdir.r16b(off); - if(ref_valid(fref) || file_is_system(bdir.rodata()+off)) { - meta_data meta = file_metadata(bdir.rodata()+off); + u16 fref = bdir->r16b(off); + if(ref_valid(fref) || file_is_system(bdir->rodata()+off)) { + meta_data meta = file_metadata(bdir->rodata()+off); res.second.emplace_back(dir_entry(dir_entry_type::file, meta)); } } - u16 ref = bdir.r16b(2); + u16 ref = bdir->r16b(2); if(!ref || !ref_valid(ref)) break; bdir = m_blockdev.get(cs_to_block(ref)); @@ -408,30 +408,30 @@ std::pair<err_t, std::vector<dir_entry>> oric_jasmin_impl::directory_contents(co return res; } -err_t oric_jasmin_impl::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) +std::error_condition oric_jasmin_impl::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) { if(opath.size() != 1 || npath.size() != 1) - return ERR_NOT_FOUND; + return error::not_found; auto [bdir, off, sys] = file_find(opath[0]); std::ignore = sys; if(!off) - return ERR_NOT_FOUND; + return error::not_found; - wstr(bdir.data() + off + 0x03, file_name_prepare(npath[0])); + wstr(bdir->data() + off + 0x03, file_name_prepare(npath[0])); - return ERR_OK; + return std::error_condition(); } -err_t oric_jasmin_impl::remove(const std::vector<std::string> &path) +std::error_condition oric_jasmin_impl::remove(const std::vector<std::string> &path) { - return ERR_UNSUPPORTED; + return error::unsupported; } -err_t oric_jasmin_impl::file_create(const std::vector<std::string> &path, const meta_data &meta) +std::error_condition oric_jasmin_impl::file_create(const std::vector<std::string> &path, const meta_data &meta) { if(path.size() != 0) - return ERR_NOT_FOUND; + return error::not_found; // One block of sector list, one block of data u32 nb = 2; @@ -442,12 +442,12 @@ err_t oric_jasmin_impl::file_create(const std::vector<std::string> &path, const for(;;) { for(u32 i = 0; i != 14; i ++) { u32 off = 4 + i*18; - u16 ref = bdir.r16b(off); + u16 ref = bdir->r16b(off); if(!ref_valid(ref)) goto found; id++; } - u16 ref = bdir.r16b(2); + u16 ref = bdir->r16b(2); if(!ref || !ref_valid(ref)) { nb ++; break; @@ -457,68 +457,68 @@ err_t oric_jasmin_impl::file_create(const std::vector<std::string> &path, const found: auto block = allocate_blocks(nb); if(block.empty()) - return ERR_NO_SPACE; + return error::no_space; auto sblk = m_blockdev.get(cs_to_block(block[0])); - sblk.w16b(0, 0xff00); // Next sector - sblk.w16l(2, meta.get_number(meta_name::loading_address, 0x500)); - sblk.w16l(4, 0); // Length - sblk.w16b(6, block[1]); // Data block + sblk->w16b(0, 0xff00); // Next sector + sblk->w16l(2, meta.get_number(meta_name::loading_address, 0x500)); + sblk->w16l(4, 0); // Length + sblk->w16b(6, block[1]); // Data block if(nb == 3) { - bdir.w16l(0, block[2]); // Link to the next directory sector + bdir->w16l(0, block[2]); // Link to the next directory sector bdir = m_blockdev.get(cs_to_block(block[2])); - bdir.fill(0xff); - bdir.w16l(0, block[2]); // Reference to itself - bdir.w16l(2, 0xff00); // No next directory sector + bdir->fill(0xff); + bdir->w16l(0, block[2]); // Reference to itself + bdir->w16l(2, 0xff00); // No next directory sector } u32 off = 4 + (id % 14) * 18; - bdir.w16b(off+0x00, block[0]); // First (and only) sector in the sector list - bdir.w8 (off+0x02, meta.get_flag(meta_name::locked, false) ? 'L' : 'U'); - bdir.wstr(off+0x03, file_name_prepare(meta.get_string(meta_name::name, ""))); - bdir.w8 (off+0x0f, meta.get_flag(meta_name::sequential, true) ? 'S' : 'D'); - bdir.w16l(off+0x10, 2); // 2 sectors for an empty file + bdir->w16b(off+0x00, block[0]); // First (and only) sector in the sector list + bdir->w8 (off+0x02, meta.get_flag(meta_name::locked, false) ? 'L' : 'U'); + bdir->wstr(off+0x03, file_name_prepare(meta.get_string(meta_name::name, ""))); + bdir->w8 (off+0x0f, meta.get_flag(meta_name::sequential, true) ? 'S' : 'D'); + bdir->w16l(off+0x10, 2); // 2 sectors for an empty file - return ERR_OK; + return std::error_condition(); } -std::pair<err_t, std::vector<u8>> oric_jasmin_impl::file_read(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<u8>> oric_jasmin_impl::file_read(const std::vector<std::string> &path) { std::vector<u8> data; if(path.size() != 1) - return std::make_pair(ERR_NOT_FOUND, data); + return std::make_pair(error::not_found, data); auto [bdir, off, sys] = file_find(path[0]); if(!off) - return std::make_pair(ERR_NOT_FOUND, data); + return std::make_pair(error::not_found, data); if(sys) { data.resize(0x3e00); for(u32 i = 0; i != 62; i++) { auto dblk = m_blockdev.get(i); - memcpy(data.data() + 256 * i, dblk.rodata(), 256); + dblk->read(0, data.data() + 256 * i, 256); } } else { - const u8 *entry = bdir.rodata() + off; + const u8 *entry = bdir->rodata() + off; u16 ref = get_u16be(entry); auto iblk = m_blockdev.get(cs_to_block(ref)); - u32 length = iblk.r16l(4); + u32 length = iblk->r16l(4); while(ref_valid(ref)) { for(u32 pos = 6; pos != 256 && data.size() < length; pos += 2) { - u16 dref = iblk.r16b(pos); + u16 dref = iblk->r16b(pos); if(!ref_valid(dref)) goto done; auto dblk = m_blockdev.get(cs_to_block(dref)); u32 dpos = data.size(); data.resize(dpos + 256); - memcpy(data.data() + dpos, dblk.rodata(), 256); + dblk->read(0, data.data() + dpos, 256); if(data.size() >= length) goto done; } - ref = iblk.r16b(2); + ref = iblk->r16b(2); if(!ref_valid(ref)) break; iblk = m_blockdev.get(cs_to_block(ref)); @@ -527,27 +527,27 @@ std::pair<err_t, std::vector<u8>> oric_jasmin_impl::file_read(const std::vector< data.resize(length); } - return std::make_pair(ERR_OK, data); + return std::make_pair(std::error_condition(), data); } -err_t oric_jasmin_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) +std::error_condition oric_jasmin_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) { if(path.size() != 1) - return ERR_NOT_FOUND; + return error::not_found; auto [bdir, off, sys] = file_find(path[0]); if(!off) - return ERR_NOT_FOUND; + return error::not_found; if(sys) { if(data.size() != 0x3e00) - return ERR_INVALID; + return error::incorrect_size; for(u32 i=0; i != 0x3e; i++) - m_blockdev.get(i).copy(0, data.data() + i * 256, 256); + m_blockdev.get(i)->write(0, data.data() + i * 256, 256); } else { - u8 *entry = bdir.data() + off; + u8 *entry = bdir->data() + off; u32 cur_ns = get_u16le(entry + 0x10); // Data sectors first u32 need_ns = (data.size() + 255) / 256; @@ -558,7 +558,7 @@ err_t oric_jasmin_impl::file_write(const std::vector<std::string> &path, const s // Enough space? if(cur_ns < need_ns && free_block_count() < need_ns - cur_ns) - return ERR_NO_SPACE; + return error::no_space; u16 load_address = 0; std::vector<u16> tofree; @@ -566,44 +566,44 @@ err_t oric_jasmin_impl::file_write(const std::vector<std::string> &path, const s for(u32 i=0; i < cur_ns; i += 125+1) { auto iblk = m_blockdev.get(cs_to_block(iref)); if(!i) - load_address = iblk.r16l(2); + load_address = iblk->r16l(2); tofree.push_back(iref); for(u32 j=0; j != 125 && i+j+1 != cur_ns; j++) - tofree.push_back(iblk.r16b(6+2*j)); - iref = iblk.r16b(2); + tofree.push_back(iblk->r16b(6+2*j)); + iref = iblk->r16b(2); } free_blocks(tofree); std::vector<u16> blocks = allocate_blocks(need_ns); for(u32 i=0; i < need_ns; i += 125+1) { auto iblk = m_blockdev.get(cs_to_block(blocks[i])); - iblk.fill(0xff); + iblk->fill(0xff); if(!i) { - iblk.w16l(2, load_address); - iblk.w16l(4, data.size()); + iblk->w16l(2, load_address); + iblk->w16l(4, data.size()); } if(i + 126 < need_ns) - iblk.w16b(0, blocks[i+126]); + iblk->w16b(0, blocks[i+126]); else - iblk.w16b(0, 0xff00); + iblk->w16b(0, 0xff00); for(u32 j=0; j != 125 && i+j+1 != need_ns; j++) { u32 dpos = 256 * (j + i/126*125); u32 size = data.size() - dpos; - iblk.w16b(6+j*2, blocks[i+j+1]); + iblk->w16b(6+j*2, blocks[i+j+1]); auto dblk = m_blockdev.get(cs_to_block(blocks[i+j+1])); if(size >= 256) - dblk.copy(0, data.data() + dpos, 256); + dblk->write(0, data.data() + dpos, 256); else { - dblk.copy(0, data.data() + dpos, size); - dblk.fill(size, 0x55, 256-size); + dblk->write(0, data.data() + dpos, size); + dblk->fill(size, 0x55, 256-size); } } } put_u16le(entry + 0x10, need_ns); put_u16be(entry + 0x00, blocks[0]); } - return ERR_OK; + return std::error_condition(); } std::vector<u16> oric_jasmin_impl::allocate_blocks(u32 count) @@ -615,7 +615,7 @@ std::vector<u16> oric_jasmin_impl::allocate_blocks(u32 count) auto fmap = m_blockdev.get(20*17); u32 nf = 0; for(u32 track = 0; track != 2*41 && nf != count; track++) { - u32 map = fmap.r24l(track*3); + u32 map = fmap->r24l(track*3); if(map != 0x800000) { for(u32 sect = 1; sect <= 17 && nf != count; sect++) if(map & (0x20000 >> sect)) { @@ -625,7 +625,7 @@ std::vector<u16> oric_jasmin_impl::allocate_blocks(u32 count) } if(!map) map = 0x800000; - fmap.w24l(track*3, map); + fmap->w24l(track*3, map); } } return blocks; @@ -637,11 +637,11 @@ void oric_jasmin_impl::free_blocks(const std::vector<u16> &blocks) for(u16 ref : blocks) { u32 track = ref >> 8; u32 sect = ref & 0xff; - u32 map = fmap.r24l(track*3); + u32 map = fmap->r24l(track*3); if(map == 0x800000) map = 0; map |= 0x20000 >> sect; - fmap.w24l(track*3, map); + fmap->w24l(track*3, map); } } @@ -650,7 +650,7 @@ u32 oric_jasmin_impl::free_block_count() auto fmap = m_blockdev.get(20*17); u32 nf = 0; for(u32 track = 0; track != 2*41; track++) { - u32 map = fmap.r24l(track*3); + u32 map = fmap->r24l(track*3); if(map != 0x800000) { for(u32 sect = 1; sect <= 17; sect++) if(map & (0x20000 >> sect)) diff --git a/src/lib/formats/fs_prodos.cpp b/src/lib/formats/fs_prodos.cpp index 826aecec91f..f7c77d47ff8 100644 --- a/src/lib/formats/fs_prodos.cpp +++ b/src/lib/formats/fs_prodos.cpp @@ -5,11 +5,14 @@ #include "fs_prodos.h" #include "ap_dsk35.h" +#include "ap2_dsk.h" #include "fsblk.h" +#include "corestr.h" #include "multibyte.h" #include "strformat.h" +#include <map> #include <stdexcept> using namespace fs; @@ -23,22 +26,22 @@ public: virtual ~prodos_impl() = default; virtual meta_data volume_metadata() override; - virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override; - virtual std::pair<err_t, std::vector<u8>> file_rsrc_read(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<u8>> file_rsrc_read(const std::vector<std::string> &path) override; - virtual err_t format(const meta_data &meta) override; + virtual std::error_condition format(const meta_data &meta) override; private: static const u8 boot[512]; static util::arbitrary_datetime prodos_to_dt(u32 date); - std::tuple<fsblk_t::block_t, u32> path_find_step(const std::string &name, u16 block); - std::tuple<fsblk_t::block_t, u32, bool> path_find(const std::vector<std::string> &path); - std::pair<err_t, std::vector<u8>> any_read(u8 type, u16 block, u32 length); + std::tuple<fsblk_t::block_t::ptr, u32> path_find_step(const std::string &name, u16 block); + std::tuple<fsblk_t::block_t::ptr, u32, bool> path_find(const std::vector<std::string> &path); + std::pair<std::error_condition, std::vector<u8>> any_read(u8 type, u16 block, u32 length); }; } @@ -90,8 +93,9 @@ const u8 prodos_impl::boot[512] = { void prodos_image::enumerate_f(floppy_enumerator &fe) const { - fe.add(FLOPPY_APPLE_GCR_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 819200, "prodos_800k", "Apple ProDOS 800K"); - fe.add(FLOPPY_APPLE_GCR_FORMAT, floppy_image::FF_35, floppy_image::SSDD, 409600, "prodos_400k", "Apple ProDOS 400K"); + fe.add(FLOPPY_APPLE_GCR_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 819200, "prodos_800k", "Apple ProDOS 3.5\" 800K"); + fe.add(FLOPPY_APPLE_GCR_FORMAT, floppy_image::FF_35, floppy_image::SSDD, 409600, "prodos_400k", "Apple ProDOS 3.5\" 400K"); + fe.add(FLOPPY_A216S_PRODOS_FORMAT, floppy_image::FF_525, floppy_image::SSSD, 143360, "prodos_140k", "Apple ProDOS 5.25\" 140K"); } std::unique_ptr<filesystem_t> prodos_image::mount(fsblk_t &blockdev) const @@ -124,6 +128,46 @@ char prodos_image::directory_separator() const return '/'; } +// TODO: this list is incomplete +static const std::map<u8, const char *> s_file_types = +{ + { 0x00, "UNK" }, + { 0x01, "BAD" }, + { 0x04, "TXT" }, + { 0x06, "BIN" }, + { 0x0f, "DIR" }, + { 0x19, "ADB" }, + { 0x1a, "AWP" }, + { 0x1b, "ASP" }, + { 0xc9, "FND" }, + { 0xca, "ICN" }, + { 0xef, "PAS" }, + { 0xf0, "CMD" }, + { 0xfa, "INT" }, + { 0xfb, "IVR" }, + { 0xfc, "BAS" }, + { 0xfd, "VAR" }, + { 0xfe, "REL" }, + { 0xff, "SYS" } +}; + +static std::string file_type_to_string(uint8_t type) +{ + auto res = s_file_types.find(type); + if (res != s_file_types.end()) + return res->second; + else + return util::string_format("0x%02x", type); +} + +static bool validate_file_type(std::string str) +{ + if ((str.length() == 3 || str.length() == 4) && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + return str.find_first_not_of("0123456789ABCDEFabcdef", 2) == std::string::npos; + else + return std::any_of(s_file_types.begin(), s_file_types.end(), [&str](const auto &pair) { return util::strequpper(str, pair.second); }); +} + std::vector<meta_description> prodos_image::volume_meta_description() const { std::vector<meta_description> res; @@ -143,6 +187,7 @@ std::vector<meta_description> prodos_image::file_meta_description() const res.emplace_back(meta_description(meta_name::name, "Empty file", false, [](const meta_value &m) { return m.as_string().size() <= 15; }, "File name, up to 15 characters")); res.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "Size of the file in bytes")); res.emplace_back(meta_description(meta_name::rsrc_length, 0, true, nullptr, "Size of the resource fork in bytes")); + res.emplace_back(meta_description(meta_name::file_type, "UNK", false, [](const meta_value &m) { return validate_file_type(m.as_string()); }, "File type, 3 letters or hex code preceded by 0x")); res.emplace_back(meta_description(meta_name::os_version, 5, false, [](const meta_value &m) { return m.as_number() <= 255; }, "Creator OS version")); res.emplace_back(meta_description(meta_name::os_minimum_version, 5, false, [](const meta_value &m) { return m.as_number() <= 255; }, "Minimum OS version")); @@ -165,7 +210,7 @@ std::vector<meta_description> prodos_image::directory_meta_description() const return res; } -err_t prodos_impl::format(const meta_data &meta) +std::error_condition prodos_impl::format(const meta_data &meta) { std::string volume_name = meta.get_string(meta_name::name, "UNTITLED"); u32 blocks = m_blockdev.block_count(); @@ -174,36 +219,36 @@ err_t prodos_impl::format(const meta_data &meta) if(blocks >= 0x10000) blocks = 0xffff; - m_blockdev.get(0).copy(0x000, boot, 0x200); // Standard ProDOS boot sector as written by a 2gs - m_blockdev.get(1).fill(0x00); // No SOS boot sector + m_blockdev.get(0)->write(0x000, boot, 0x200); // Standard ProDOS boot sector as written by a 2gs + m_blockdev.get(1)->fill(0x00); // No SOS boot sector auto kblk1 = m_blockdev.get(2); // key block first block auto kblk2 = m_blockdev.get(3); // key block second block auto kblk3 = m_blockdev.get(4); // key block third block auto kblk4 = m_blockdev.get(5); // key block fourth block - kblk1.w16l(0x00, 0x0000); // Backwards key block pointer (null) - kblk1.w16l(0x02, 0x0003); // Forwards key block pointer - kblk1.w8 (0x04, 0xf0 | volume_name.size()); // Block type (f, key block) and name size - kblk1.wstr(0x05, volume_name); // Volume name, up to 15 characters - kblk1.w32b(0x16, 0x642a250d); // ??? date & time - kblk1.w16b(0x1a, 0x80ff); // ??? - kblk1.w32b(0x1c, 0x642a250d); // Creation date & time - kblk1.w8 (0x20, 0x05); // ProDOS version (2gs) - kblk1.w8 (0x21, 0x00); // ProDOS minimum version - kblk1.w8 (0x22, 0xc3); // Allowed access (destroy, rename, !backup, 3x0, write read) - kblk1.w8 (0x23, 0x27); // Directory entry length (fixed) - kblk1.w8 (0x24, 0x0d); // Entries per block (fixed) - kblk1.w16l(0x25, 0x0000); // Number of file entries in the directory - kblk1.w16l(0x27, 0x0006); // Bitmap block pointer - kblk1.w16l(0x29, blocks); // Number of blocks - - kblk2.w16l(0x00, 0x0002); // Backwards block pointer of the second volume block - kblk2.w16l(0x02, 0x0004); // Forwards block pointer of the second volume block - kblk3.w16l(0x00, 0x0003); // Backwards block pointer of the third volume block - kblk3.w16l(0x02, 0x0005); // Forwards block pointer of the third volume block - kblk4.w16l(0x00, 0x0004); // Backwards block pointer of the fourth volume block - kblk4.w16l(0x02, 0x0000); // Forwards block pointer of the fourth volume block (null) + kblk1->w16l(0x00, 0x0000); // Backwards key block pointer (null) + kblk1->w16l(0x02, 0x0003); // Forwards key block pointer + kblk1->w8 (0x04, 0xf0 | volume_name.size()); // Block type (f, key block) and name size + kblk1->wstr(0x05, volume_name); // Volume name, up to 15 characters + kblk1->w32b(0x16, 0x642a250d); // ??? date & time + kblk1->w16b(0x1a, 0x80ff); // ??? + kblk1->w32b(0x1c, 0x642a250d); // Creation date & time + kblk1->w8 (0x20, 0x05); // ProDOS version (2gs) + kblk1->w8 (0x21, 0x00); // ProDOS minimum version + kblk1->w8 (0x22, 0xc3); // Allowed access (destroy, rename, !backup, 3x0, write read) + kblk1->w8 (0x23, 0x27); // Directory entry length (fixed) + kblk1->w8 (0x24, 0x0d); // Entries per block (fixed) + kblk1->w16l(0x25, 0x0000); // Number of file entries in the directory + kblk1->w16l(0x27, 0x0006); // Bitmap block pointer + kblk1->w16l(0x29, blocks); // Number of blocks + + kblk2->w16l(0x00, 0x0002); // Backwards block pointer of the second volume block + kblk2->w16l(0x02, 0x0004); // Forwards block pointer of the second volume block + kblk3->w16l(0x00, 0x0003); // Backwards block pointer of the third volume block + kblk3->w16l(0x02, 0x0005); // Forwards block pointer of the third volume block + kblk4->w16l(0x00, 0x0004); // Backwards block pointer of the fourth volume block + kblk4->w16l(0x02, 0x0000); // Forwards block pointer of the fourth volume block (null) u32 fmap_block_count = (blocks + 4095) / 4096; u32 first_free_block = 6 + fmap_block_count; @@ -211,7 +256,7 @@ err_t prodos_impl::format(const meta_data &meta) // Mark blocks from first_free_block to blocks-1 (the last one) as free for(u32 i = 0; i != fmap_block_count; i++) { auto fmap = m_blockdev.get(6 + i); - u8 *fdata = fmap.data(); + u8 *fdata = fmap->data(); u32 start = i ? 0 : first_free_block; u32 end = i != fmap_block_count - 1 ? 4095 : (blocks - 1) & 4095; end += 1; @@ -229,7 +274,7 @@ err_t prodos_impl::format(const meta_data &meta) memset(fdata+sb, 0xff, eb-sb-1); } } - return ERR_OK; + return std::error_condition(); } prodos_impl::prodos_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 512) @@ -242,8 +287,8 @@ util::arbitrary_datetime prodos_impl::prodos_to_dt(u32 date) dt.second = 0; dt.minute = ((date >> 16) & 0x3f); dt.hour = ((date >> 24) & 0x1f); - dt.day_of_month = ((date >> 0) & 0x1f); - dt.month = ((date >> 5) & 0x0f) + 1; + dt.day_of_month = ((date >> 0) & 0x1f); // 1-based + dt.month = ((date >> 5) & 0x0f); // 1-based dt.year = ((date >> 9) & 0x7f) + 1900; if (dt.year <= 1949) dt.year += 100; @@ -255,16 +300,16 @@ meta_data prodos_impl::volume_metadata() { meta_data res; auto bdir = m_blockdev.get(2); - int len = bdir.r8(0x04) & 0xf; - res.set(meta_name::name, bdir.rstr(0x05, len)); - res.set(meta_name::os_version, bdir.r8(0x20)); - res.set(meta_name::os_minimum_version, bdir.r8(0x21)); - res.set(meta_name::creation_date, prodos_to_dt(bdir.r32l(0x1c))); - res.set(meta_name::modification_date, prodos_to_dt(bdir.r32l(0x16))); + int len = bdir->r8(0x04) & 0xf; + res.set(meta_name::name, bdir->rstr(0x05, len)); + res.set(meta_name::os_version, bdir->r8(0x20)); + res.set(meta_name::os_minimum_version, bdir->r8(0x21)); + res.set(meta_name::creation_date, prodos_to_dt(bdir->r32l(0x1c))); + res.set(meta_name::modification_date, prodos_to_dt(bdir->r32l(0x16))); return res; } -std::pair<err_t, std::vector<dir_entry>> prodos_impl::directory_contents(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<dir_entry>> prodos_impl::directory_contents(const std::vector<std::string> &path) { u16 block; if(path.empty()) @@ -273,62 +318,66 @@ std::pair<err_t, std::vector<dir_entry>> prodos_impl::directory_contents(const s else { auto [blk, off, dir] = path_find(path); if(!off || !dir) - return std::make_pair(ERR_NOT_FOUND, std::vector<dir_entry>()); - block = blk.r16l(off+0x11); + return std::make_pair(error::not_found, std::vector<dir_entry>()); + block = blk->r16l(off+0x11); } std::vector<dir_entry> res; - u32 off = 39 + 4; do { auto blk = m_blockdev.get(block); - while(off < 511) { - meta_data meta; - u8 type = blk.r8(off); - meta.set(meta_name::name, blk.rstr(off+1, type & 0xf)); - type >>= 4; - - if(type == 5) { - auto rootblk = m_blockdev.get(blk.r16l(off+0x11)); - meta.set(meta_name::length, rootblk.r24l(0x005)); - meta.set(meta_name::rsrc_length, rootblk.r24l(0x105)); - - } else if(type >= 1 && type <= 3) - meta.set(meta_name::length, blk.r24l(off + 0x15)); - - res.emplace_back(dir_entry(type == 0xd ? dir_entry_type::dir : dir_entry_type::file, meta)); - off += 39; + for(u32 off = 4; off < 511; off += 39) { + u8 type = blk->r8(off); + // skip inactive entries and subroutine/volume headers + if(type != 0 && type < 0xe0) { + meta_data meta; + meta.set(meta_name::name, blk->rstr(off+1, type & 0xf)); + type >>= 4; + + if(type == 5) { + auto rootblk = m_blockdev.get(blk->r16l(off+0x11)); + meta.set(meta_name::length, rootblk->r24l(0x005)); + meta.set(meta_name::rsrc_length, rootblk->r24l(0x105)); + + } else if(type >= 1 && type <= 3) { + meta.set(meta_name::length, blk->r24l(off + 0x15)); + meta.set(meta_name::file_type, file_type_to_string(blk->r8(off + 0x10))); + } + + meta.set(meta_name::os_version, blk->r8(off + 0x1c)); + meta.set(meta_name::os_minimum_version, blk->r8(off + 0x1d)); + meta.set(meta_name::creation_date, prodos_to_dt(blk->r32l(off + 0x18))); + meta.set(meta_name::modification_date, prodos_to_dt(blk->r32l(off + 0x21))); + + res.emplace_back(dir_entry(type == 0xd ? dir_entry_type::dir : dir_entry_type::file, meta)); + } } - block = blk.r16l(2); + block = blk->r16l(2); if(block >= m_blockdev.block_count()) break; - off = 4; } while(block); - return std::make_pair(ERR_OK, res); + return std::make_pair(std::error_condition(), res); } -std::tuple<fsblk_t::block_t, u32> prodos_impl::path_find_step(const std::string &name, u16 block) +std::tuple<fsblk_t::block_t::ptr, u32> prodos_impl::path_find_step(const std::string &name, u16 block) { - u32 off = 39 + 4; for(;;) { auto blk = m_blockdev.get(block); - while(off < 511) { - u8 type = blk.r8(off); - if(name == blk.rstr(off+1, type & 0xf)) + for(u32 off = 4; off < 511; off += 39) { + u8 type = blk->r8(off); + if(type != 0 && type < 0xe0 && name == blk->rstr(off+1, type & 0xf)) return std::make_tuple(blk, off); - off += 39; } - block = blk.r16l(2); + block = blk->r16l(2); if(!block || block >= m_blockdev.block_count()) return std::make_tuple(blk, 0U); - off = 4; } } -std::tuple<fsblk_t::block_t, u32, bool> prodos_impl::path_find(const std::vector<std::string> &path) +std::tuple<fsblk_t::block_t::ptr, u32, bool> prodos_impl::path_find(const std::vector<std::string> &path) { if(path.size() == 0) - return std::tuple<fsblk_t::block_t, u32, bool>(fsblk_t::block_t(), 0, false); + return std::tuple<fsblk_t::block_t::ptr, u32, bool>(fsblk_t::block_t::ptr(), 0, false); u16 block = 2; for(u32 pathc = 0;; pathc++) { @@ -337,27 +386,27 @@ std::tuple<fsblk_t::block_t, u32, bool> prodos_impl::path_find(const std::vector return std::make_tuple(blk, off, false); if(pathc + 1 == path.size()) - return std::make_tuple(blk, off, (blk.r8(off) & 0xf0) == 0xd0); + return std::make_tuple(blk, off, (blk->r8(off) & 0xf0) == 0xd0); - if((blk.r8(off) & 0xf0) != 0xd0) + if((blk->r8(off) & 0xf0) != 0xd0) return std::make_tuple(blk, 0U, false); - block = blk.r16l(off + 0x11); + block = blk->r16l(off + 0x11); } } -std::pair<err_t, meta_data> prodos_impl::metadata(const std::vector<std::string> &path) +std::pair<std::error_condition, meta_data> prodos_impl::metadata(const std::vector<std::string> &path) { if(path.size() == 0) - return std::make_pair(ERR_OK, meta_data()); + return std::make_pair(std::error_condition(), meta_data()); auto [blk, off, dir] = path_find(path); if(!off) - return std::make_pair(ERR_NOT_FOUND, meta_data()); + return std::make_pair(error::not_found, meta_data()); - const u8 *entry = blk.rodata() + off; + const u8 *entry = blk->rodata() + off; meta_data res; if(dir) { @@ -371,23 +420,23 @@ std::pair<err_t, meta_data> prodos_impl::metadata(const std::vector<std::string> res.set(meta_name::name, name); if(type == 5) { auto rootblk = m_blockdev.get(get_u16le(entry+0x11)); - res.set(meta_name::length, rootblk.r24l(0x005)); - res.set(meta_name::rsrc_length, rootblk.r24l(0x105)); + res.set(meta_name::length, rootblk->r24l(0x005)); + res.set(meta_name::rsrc_length, rootblk->r24l(0x105)); } else if(type >= 1 && type <= 3) res.set(meta_name::length, get_u24le(entry + 0x15)); else - return std::make_pair(ERR_UNSUPPORTED, meta_data()); + return std::make_pair(error::unsupported, meta_data()); } - return std::make_pair(ERR_OK, res); + return std::make_pair(std::error_condition(), res); } -std::pair<err_t, std::vector<u8>> prodos_impl::any_read(u8 type, u16 block, u32 length) +std::pair<std::error_condition, std::vector<u8>> prodos_impl::any_read(u8 type, u16 block, u32 length) { - std::pair<err_t, std::vector<u8>> data; - data.first = ERR_OK; + std::pair<std::error_condition, std::vector<u8>> data; + data.first = std::error_condition(); data.second.resize((length + 511) & ~511); u32 nb = data.second.size()/512; if(!nb) @@ -397,15 +446,15 @@ std::pair<err_t, std::vector<u8>> prodos_impl::any_read(u8 type, u16 block, u32 u8 *end = dst + data.second.size(); switch(type) { case 1: - memcpy(dst, m_blockdev.get(block).rodata(), 512); + m_blockdev.get(block)->read(0, dst, 512); dst += 512; break; case 2: { auto iblk = m_blockdev.get(block); for(u32 i=0; i != 256 && dst != end; i++) { - u16 blk = iblk.r8(i) | (iblk.r8(i | 0x100) << 8); - memcpy(dst, m_blockdev.get(blk).rodata(), 512); + u16 blk = iblk->r8(i) | (iblk->r8(i | 0x100) << 8); + m_blockdev.get(blk)->read(0, dst, 512); dst += 512; } break; @@ -415,10 +464,10 @@ std::pair<err_t, std::vector<u8>> prodos_impl::any_read(u8 type, u16 block, u32 auto mblk = m_blockdev.get(block); for(u32 j=0; dst != end; j += 256) { u32 idx = j/256; - auto iblk = m_blockdev.get(mblk.r8(idx) | (mblk.r8(idx | 0x100) << 8)); + auto iblk = m_blockdev.get(mblk->r8(idx) | (mblk->r8(idx | 0x100) << 8)); for(u32 i=0; i != 256 && dst != end; i++) { - u16 blk = iblk.r8(i) | (iblk.r8(i | 0x100) << 8); - memcpy(dst, m_blockdev.get(blk).rodata(), 512); + u16 blk = iblk->r8(i) | (iblk->r8(i | 0x100) << 8); + m_blockdev.get(blk)->read(0, dst, 512); dst += 512; } } @@ -426,7 +475,7 @@ std::pair<err_t, std::vector<u8>> prodos_impl::any_read(u8 type, u16 block, u32 } default: - data.first = ERR_UNSUPPORTED; + data.first = error::unsupported; data.second.clear(); return data; } @@ -435,13 +484,13 @@ std::pair<err_t, std::vector<u8>> prodos_impl::any_read(u8 type, u16 block, u32 return data; } -std::pair<err_t, std::vector<u8>> prodos_impl::file_read(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<u8>> prodos_impl::file_read(const std::vector<std::string> &path) { auto [blk, off, dir] = path_find(path); if(!off || dir) - return std::make_pair(ERR_NOT_FOUND, std::vector<u8>()); + return std::make_pair(error::not_found, std::vector<u8>()); - const u8 *entry = blk.rodata() + off; + const u8 *entry = blk->rodata() + off; u8 type = entry[0] >> 4; if(type >= 1 && type <= 3) @@ -449,25 +498,25 @@ std::pair<err_t, std::vector<u8>> prodos_impl::file_read(const std::vector<std:: else if(type == 5) { auto kblk = m_blockdev.get(get_u16le(entry+0x11)); - return any_read(kblk.r8(0x000), kblk.r16l(0x001), kblk.r24l(0x005)); + return any_read(kblk->r8(0x000), kblk->r16l(0x001), kblk->r24l(0x005)); } else - return std::make_pair(ERR_UNSUPPORTED, std::vector<u8>()); + return std::make_pair(error::unsupported, std::vector<u8>()); } -std::pair<err_t, std::vector<u8>> prodos_impl::file_rsrc_read(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<u8>> prodos_impl::file_rsrc_read(const std::vector<std::string> &path) { auto [blk, off, dir] = path_find(path); if(!off || dir) - return std::make_pair(ERR_NOT_FOUND, std::vector<u8>()); + return std::make_pair(error::not_found, std::vector<u8>()); - const u8 *entry = blk.rodata() + off; + const u8 *entry = blk->rodata() + off; u8 type = entry[0] >> 4; if(type == 5) { auto kblk = m_blockdev.get(get_u16le(entry+0x11)); - return any_read(kblk.r8(0x100), kblk.r16l(0x101), kblk.r24l(0x105)); + return any_read(kblk->r8(0x100), kblk->r16l(0x101), kblk->r24l(0x105)); } else - return std::make_pair(ERR_UNSUPPORTED, std::vector<u8>()); + return std::make_pair(error::unsupported, std::vector<u8>()); } diff --git a/src/lib/formats/fs_unformatted.cpp b/src/lib/formats/fs_unformatted.cpp index df618c6485d..995c2b76148 100644 --- a/src/lib/formats/fs_unformatted.cpp +++ b/src/lib/formats/fs_unformatted.cpp @@ -35,11 +35,15 @@ void unformatted_image::enumerate_f(floppy_enumerator &fe) const u32 best_525 = form_factor == floppy_image::FF_525 ? - has_variant(variants, floppy_image::DSHD) ? FSI_525_DSHD : - has_variant(variants, floppy_image::DSQD) ? FSI_525_DSQD : - has_variant(variants, floppy_image::DSDD) ? FSI_525_DSDD : - has_variant(variants, floppy_image::SSQD) ? FSI_525_SSQD : - has_variant(variants, floppy_image::SSDD) ? FSI_525_SSDD : FSI_525_SSSD + has_variant(variants, floppy_image::DSHD) ? FSI_525_DSHD : + has_variant(variants, floppy_image::DSQD16) ? FSI_525_DSQD16 : + has_variant(variants, floppy_image::DSQD) ? FSI_525_DSQD : + has_variant(variants, floppy_image::DSDD16) ? FSI_525_DSDD16 : + has_variant(variants, floppy_image::DSDD) ? FSI_525_DSDD : + has_variant(variants, floppy_image::SSQD16) ? FSI_525_SSQD16 : + has_variant(variants, floppy_image::SSQD) ? FSI_525_SSQD : + has_variant(variants, floppy_image::SSDD16) ? FSI_525_SSDD16 : + has_variant(variants, floppy_image::SSDD) ? FSI_525_SSDD : FSI_525_SSSD : FSI_NONE; u32 best_35 = @@ -61,14 +65,22 @@ void unformatted_image::enumerate_f(floppy_enumerator &fe) const if(all || best_525 == FSI_525_DSHD) fe.add_raw("u525dshd", FSI_525_DSHD, "Unformatted 5\"25 double-sided high-density"); + if(all || best_525 == FSI_525_DSQD16) + fe.add_raw("u525dsqd16", FSI_525_DSQD16, "Unformatted 5\"25 double-sided quad-density 16 hard sectors"); if(all || best_525 == FSI_525_DSQD) fe.add_raw("u525dsqd", FSI_525_DSQD, "Unformatted 5\"25 double-sided quad-density"); + if(all || best_525 == FSI_525_DSDD16) + fe.add_raw("u525dsdd16", FSI_525_DSDD16, "Unformatted 5\"25 double-sided double-density 16 hard sectors"); if(all || best_525 == FSI_525_DSDD) fe.add_raw("u525dsdd", FSI_525_DSDD, "Unformatted 5\"25 double-sided double-density"); if(all) fe.add_raw("u525dssd", FSI_525_DSSD, "Unformatted 5\"25 double-sided single-density"); + if(all || best_525 == FSI_525_SSQD16) + fe.add_raw("u525ssqd16", FSI_525_SSQD16, "Unformatted 5\"25 single-sided quad-density 16 hard sectors"); if(all || best_525 == FSI_525_SSQD) fe.add_raw("u525ssqd", FSI_525_SSQD, "Unformatted 5\"25 single-sided quad-density"); + if(all || best_525 == FSI_525_SSDD16) + fe.add_raw("u525ssdd16", FSI_525_SSDD16, "Unformatted 5\"25 single-sided double-density 16 hard sectors"); if(all || best_525 == FSI_525_SSDD) fe.add_raw("u525ssdd", FSI_525_SSDD, "Unformatted 5\"25 single-sided double-density"); if(all || best_525 == FSI_525_SSSD) @@ -96,13 +108,17 @@ void unformatted_image::format(u32 key, floppy_image *image) case FSI_8_DSSD: image->set_form_variant(floppy_image::FF_8, floppy_image::DSSD); break; case FSI_8_SSSD: image->set_form_variant(floppy_image::FF_8, floppy_image::SSSD); break; - case FSI_525_DSHD: image->set_form_variant(floppy_image::FF_525, floppy_image::DSHD); break; - case FSI_525_DSQD: image->set_form_variant(floppy_image::FF_525, floppy_image::DSQD); break; - case FSI_525_DSDD: image->set_form_variant(floppy_image::FF_525, floppy_image::DSDD); break; - case FSI_525_DSSD: image->set_form_variant(floppy_image::FF_525, floppy_image::DSSD); break; - case FSI_525_SSQD: image->set_form_variant(floppy_image::FF_525, floppy_image::SSQD); break; - case FSI_525_SSDD: image->set_form_variant(floppy_image::FF_525, floppy_image::SSDD); break; - case FSI_525_SSSD: image->set_form_variant(floppy_image::FF_525, floppy_image::SSSD); break; + case FSI_525_DSHD: image->set_form_variant(floppy_image::FF_525, floppy_image::DSHD); break; + case FSI_525_DSQD16: image->set_form_variant(floppy_image::FF_525, floppy_image::DSQD16); break; + case FSI_525_DSQD: image->set_form_variant(floppy_image::FF_525, floppy_image::DSQD); break; + case FSI_525_DSDD16: image->set_form_variant(floppy_image::FF_525, floppy_image::DSDD16); break; + case FSI_525_DSDD: image->set_form_variant(floppy_image::FF_525, floppy_image::DSDD); break; + case FSI_525_DSSD: image->set_form_variant(floppy_image::FF_525, floppy_image::DSSD); break; + case FSI_525_SSQD16: image->set_form_variant(floppy_image::FF_525, floppy_image::SSQD16); break; + case FSI_525_SSQD: image->set_form_variant(floppy_image::FF_525, floppy_image::SSQD); break; + case FSI_525_SSDD16: image->set_form_variant(floppy_image::FF_525, floppy_image::SSDD16); break; + case FSI_525_SSDD: image->set_form_variant(floppy_image::FF_525, floppy_image::SSDD); break; + case FSI_525_SSSD: image->set_form_variant(floppy_image::FF_525, floppy_image::SSSD); break; case FSI_35_DSED: image->set_form_variant(floppy_image::FF_35, floppy_image::DSED); break; case FSI_35_DSHD: image->set_form_variant(floppy_image::FF_35, floppy_image::DSHD); break; diff --git a/src/lib/formats/fs_unformatted.h b/src/lib/formats/fs_unformatted.h index 949bb6aae08..fa51049f384 100644 --- a/src/lib/formats/fs_unformatted.h +++ b/src/lib/formats/fs_unformatted.h @@ -25,10 +25,14 @@ public: FSI_525_SSSD, FSI_525_SSDD, + FSI_525_SSDD16, FSI_525_SSQD, + FSI_525_SSQD16, FSI_525_DSSD, FSI_525_DSDD, + FSI_525_DSDD16, FSI_525_DSQD, + FSI_525_DSQD16, FSI_525_DSHD, FSI_35_SSDD, diff --git a/src/lib/formats/fs_vtech.cpp b/src/lib/formats/fs_vtech.cpp index 27e7f93a056..163b2ae6d53 100644 --- a/src/lib/formats/fs_vtech.cpp +++ b/src/lib/formats/fs_vtech.cpp @@ -19,7 +19,7 @@ namespace fs { const vtech_image VTECH; } // Filesystem has no subdirectories. // // Track 0 sectors 0-14 have the file names. 16 bytes/entry -// offset 0 : File type 'T' (basic) or 'B' (binary) +// offset 0 : File type 'T' (basic), 'B' (binary), or some other letter (application-specific) // offset 1 : 0x3a // offset 2-9: File name // offset a : Track number of first file sector @@ -38,24 +38,24 @@ public: virtual ~vtech_impl() = default; virtual meta_data volume_metadata() override; - virtual err_t volume_metadata_change(const meta_data &info) override; - virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override; - virtual err_t metadata_change(const std::vector<std::string> &path, const meta_data &meta) override; + virtual std::error_condition volume_metadata_change(const meta_data &info) override; + virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override; + virtual std::error_condition metadata_change(const std::vector<std::string> &path, const meta_data &meta) override; - virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; - virtual err_t rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) override; - virtual err_t remove(const std::vector<std::string> &path) override; + virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; + virtual std::error_condition rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) override; + virtual std::error_condition remove(const std::vector<std::string> &path) override; - virtual err_t file_create(const std::vector<std::string> &path, const meta_data &meta) override; + virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override; - virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override; - virtual err_t file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override; + virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override; - virtual err_t format(const meta_data &meta) override; + virtual std::error_condition format(const meta_data &meta) override; private: meta_data file_metadata(const u8 *entry); - std::tuple<fsblk_t::block_t, u32> file_find(std::string_view name); + std::tuple<fsblk_t::block_t::ptr, u32> file_find(std::string_view name); std::vector<std::pair<u8, u8>> allocate_blocks(u32 count); void free_blocks(const std::vector<std::pair<u8, u8>> &blocks); u32 free_block_count(); @@ -114,7 +114,9 @@ std::vector<meta_description> vtech_image::file_meta_description() const res.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return m.as_string().size() <= 8; }, "File name, 8 chars")); res.emplace_back(meta_description(meta_name::loading_address, 0x7ae9, false, [](const meta_value &m) { return m.as_number() < 0x10000; }, "Loading address of the file")); res.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "Size of the file in bytes")); - res.emplace_back(meta_description(meta_name::basic, true, true, nullptr, "Basic file")); + res.emplace_back(meta_description(meta_name::file_type, "T", true, + [](const meta_value &m) { return m.as_string().size() == 1 && m.as_string()[0] >= 'A' && m.as_string()[0] <= 'Z'; }, + "File type (e.g. T = text, B = binary)")); return res; } @@ -122,10 +124,10 @@ vtech_impl::vtech_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 128) { } -err_t vtech_impl::format(const meta_data &meta) +std::error_condition vtech_impl::format(const meta_data &meta) { - m_blockdev.fill(0); - return ERR_OK; + m_blockdev.fill_all(0); + return std::error_condition(); } meta_data vtech_impl::volume_metadata() @@ -133,9 +135,9 @@ meta_data vtech_impl::volume_metadata() return meta_data(); } -err_t vtech_impl::volume_metadata_change(const meta_data &meta) +std::error_condition vtech_impl::volume_metadata_change(const meta_data &meta) { - return ERR_OK; + return std::error_condition(); } meta_data vtech_impl::file_metadata(const u8 *entry) @@ -143,56 +145,56 @@ meta_data vtech_impl::file_metadata(const u8 *entry) meta_data res; res.set(meta_name::name, trim_end_spaces(rstr(entry+2, 8))); - res.set(meta_name::basic, entry[0] == 'T'); + res.set(meta_name::file_type, std::string{ char(entry[0]) }); res.set(meta_name::loading_address, get_u16le(entry + 0xc)); - res.set(meta_name::length, ((get_u16le(entry + 0xe) - get_u16le(entry + 0xc) + 1) & 0xffff)); + res.set(meta_name::length, (get_u16le(entry + 0xe) - get_u16le(entry + 0xc)) & 0xffff); return res; } -std::tuple<fsblk_t::block_t, u32> vtech_impl::file_find(std::string_view name) +std::tuple<fsblk_t::block_t::ptr, u32> vtech_impl::file_find(std::string_view name) { for(int sect = 0; sect != 14; sect++) { auto bdir = m_blockdev.get(sect); for(u32 i = 0; i != 8; i ++) { u32 off = i*16; - u8 type = bdir.r8(off); - if(type != 'T' && type != 'B') + u8 type = bdir->r8(off); + if(type < 'A' || type > 'Z') continue; - if(bdir.r8(off+1) != ':') + if(bdir->r8(off+1) != ':') continue; - if(trim_end_spaces(bdir.rstr(off+2, 8)) == name) { - return std::make_tuple(bdir, i); + if(trim_end_spaces(bdir->rstr(off+2, 8)) == name) { + return std::make_tuple(bdir, off); } } } - return std::make_tuple(fsblk_t::block_t(), 0xffffffff); + return std::make_tuple(fsblk_t::block_t::ptr(), 0xffffffff); } -std::pair<err_t, meta_data> vtech_impl::metadata(const std::vector<std::string> &path) +std::pair<std::error_condition, meta_data> vtech_impl::metadata(const std::vector<std::string> &path) { if(path.size() != 1) - return std::make_pair(ERR_NOT_FOUND, meta_data()); + return std::make_pair(error::not_found, meta_data()); auto [bdir, off] = file_find(path[0]); if(off == 0xffffffff) - return std::make_pair(ERR_NOT_FOUND, meta_data()); + return std::make_pair(error::not_found, meta_data()); - return std::make_pair(ERR_OK, file_metadata(bdir.rodata() + off)); + return std::make_pair(std::error_condition(), file_metadata(bdir->rodata() + off)); } -err_t vtech_impl::metadata_change(const std::vector<std::string> &path, const meta_data &meta) +std::error_condition vtech_impl::metadata_change(const std::vector<std::string> &path, const meta_data &meta) { if(path.size() != 1) - return ERR_NOT_FOUND; + return error::not_found; auto [bdir, off] = file_find(path[0]); - if(!off) - return ERR_NOT_FOUND; + if(off == 0xffffffff) + return error::not_found; - u8 *entry = bdir.data() + off; - if(meta.has(meta_name::basic)) - entry[0x0] = meta.get_flag(meta_name::basic) ? 'T' : 'B'; + u8 *entry = bdir->data() + off; + if(meta.has(meta_name::file_type)) + entry[0x0] = meta.get_string(meta_name::file_type)[0]; if(meta.has(meta_name::name)) { std::string name = meta.get_string(meta_name::name); name.resize(8, ' '); @@ -205,103 +207,103 @@ err_t vtech_impl::metadata_change(const std::vector<std::string> &path, const me put_u16le(entry + 0xe, new_end); } - return ERR_OK; + return std::error_condition(); } -std::pair<err_t, std::vector<dir_entry>> vtech_impl::directory_contents(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<dir_entry>> vtech_impl::directory_contents(const std::vector<std::string> &path) { - std::pair<err_t, std::vector<dir_entry>> res; + std::pair<std::error_condition, std::vector<dir_entry>> res; if(path.size() != 0) { - res.first = ERR_NOT_FOUND; + res.first = error::not_found; return res; } - res.first = ERR_OK; + res.first = std::error_condition(); for(int sect = 0; sect != 14; sect++) { auto bdir = m_blockdev.get(sect); for(u32 i = 0; i != 8; i ++) { u32 off = i*16; - u8 type = bdir.r8(off); - if(type != 'T' && type != 'B') + u8 type = bdir->r8(off); + if(type < 'A' || type > 'Z') continue; - if(bdir.r8(off+1) != ':') + if(bdir->r8(off+1) != ':') continue; - meta_data meta = file_metadata(bdir.rodata()+off); + meta_data meta = file_metadata(bdir->rodata()+off); res.second.emplace_back(dir_entry(dir_entry_type::file, meta)); } } return res; } -err_t vtech_impl::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) +std::error_condition vtech_impl::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) { if(opath.size() != 1 || npath.size() != 1) - return ERR_NOT_FOUND; + return error::not_found; auto [bdir, off] = file_find(opath[0]); - if(!off) - return ERR_NOT_FOUND; + if(off == 0xffffffff) + return error::not_found; std::string name = npath[0]; name.resize(8, ' '); - wstr(bdir.data() + off + 2, name); + wstr(bdir->data() + off + 2, name); - return ERR_OK; + return std::error_condition(); } -err_t vtech_impl::remove(const std::vector<std::string> &path) +std::error_condition vtech_impl::remove(const std::vector<std::string> &path) { - return ERR_NOT_FOUND; + return error::unsupported; } -err_t vtech_impl::file_create(const std::vector<std::string> &path, const meta_data &meta) +std::error_condition vtech_impl::file_create(const std::vector<std::string> &path, const meta_data &meta) { if(path.size() != 0) - return ERR_NOT_FOUND; + return error::not_found; // Find the key for the next unused entry for(int sect = 0; sect != 14; sect++) { auto bdir = m_blockdev.get(sect); for(u32 i = 0; i != 16; i ++) { u32 off = i*16; - u8 type = bdir.r8(off); + u8 type = bdir->r8(off); if(type != 'T' && type != 'B') { std::string fname = meta.get_string(meta_name::name, ""); fname.resize(8, ' '); - bdir.w8 (off+0x0, meta.get_flag(meta_name::basic, true) ? 'T' : 'B'); - bdir.w8 (off+0x1, ':'); - bdir.wstr(off+0x2, fname); - bdir.w8 (off+0xa, 0x00); - bdir.w8 (off+0xb, 0x00); - bdir.w16l(off+0xc, meta.get_number(meta_name::loading_address, 0x7ae9)); - bdir.w16l(off+0xe, bdir.r16l(off+0xc) - 1); // Size 0 initially - return ERR_OK; + bdir->w8 (off+0x0, meta.get_string(meta_name::file_type, "T")[0]); + bdir->w8 (off+0x1, ':'); + bdir->wstr(off+0x2, fname); + bdir->w8 (off+0xa, 0x00); + bdir->w8 (off+0xb, 0x00); + bdir->w16l(off+0xc, meta.get_number(meta_name::loading_address, 0x7ae9)); + bdir->w16l(off+0xe, bdir->r16l(off+0xc)); // Size 0 initially + return std::error_condition(); } } } - return ERR_NO_SPACE; + return error::no_space; } -std::pair<err_t, std::vector<u8>> vtech_impl::file_read(const std::vector<std::string> &path) +std::pair<std::error_condition, std::vector<u8>> vtech_impl::file_read(const std::vector<std::string> &path) { std::vector<u8> data; if(path.size() != 1) - return std::make_pair(ERR_NOT_FOUND, data); + return std::make_pair(error::not_found, data); auto [bdir, off] = file_find(path[0]); if(off == 0xffffffff) - return std::make_pair(ERR_NOT_FOUND, data); + return std::make_pair(error::not_found, data); - const u8 *entry = bdir.rodata() + off; + const u8 *entry = bdir->rodata() + off; u8 track = entry[0xa]; u8 sector = entry[0xb]; - int len = ((get_u16le(entry + 0xe) - get_u16le(entry + 0xc)) & 0xffff) + 1; + int len = (get_u16le(entry + 0xe) - get_u16le(entry + 0xc)) & 0xffff; data.resize(len, 0); int pos = 0; @@ -312,26 +314,26 @@ std::pair<err_t, std::vector<u8>> vtech_impl::file_read(const std::vector<std::s int size = len - pos; if(size > 126) size = 126; - memcpy(data.data() + pos, dblk.data(), size); + dblk->read(0, data.data() + pos, size); pos += size; - track = dblk.r8(126); - sector = dblk.r8(127); + track = dblk->r8(126); + sector = dblk->r8(127); } - return std::make_pair(ERR_OK, data); + return std::make_pair(std::error_condition(), data); } -err_t vtech_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) +std::error_condition vtech_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) { if(path.size() != 1) - return ERR_NOT_FOUND; + return error::not_found; auto [bdir, off] = file_find(path[0]); if(off == 0xffffffff) - return ERR_NOT_FOUND; + return error::not_found; - u8 *entry = bdir.data() + off; + u8 *entry = bdir->data() + off; - u32 cur_len = ((get_u16le(entry + 0xe) - get_u16le(entry + 0xc) + 1) & 0xffff); + u32 cur_len = (get_u16le(entry + 0xe) - get_u16le(entry + 0xc)) & 0xffff; u32 new_len = data.size(); if(new_len > 65535) new_len = 65535; @@ -340,7 +342,7 @@ err_t vtech_impl::file_write(const std::vector<std::string> &path, const std::ve // Enough space? if(cur_ns < need_ns && free_block_count() < need_ns - cur_ns) - return ERR_NO_SPACE; + return error::no_space; u8 track = entry[0xa]; u8 sector = entry[0xb]; @@ -348,8 +350,8 @@ err_t vtech_impl::file_write(const std::vector<std::string> &path, const std::ve for(u32 i = 0; i != cur_ns; i++) { tofree.emplace_back(std::make_pair(track, sector)); auto dblk = m_blockdev.get(track*16 + sector); - track = dblk.r8(126); - sector = dblk.r8(127); + track = dblk->r8(126); + sector = dblk->r8(127); } free_blocks(tofree); @@ -361,16 +363,16 @@ err_t vtech_impl::file_write(const std::vector<std::string> &path, const std::ve if(len > 126) len = 126; else if(len < 126) - dblk.fill(0x00); - memcpy(dblk.data(), data.data() + 126*i, len); + dblk->fill(0x00); + dblk->write(0, data.data() + 126*i, len); if(i < need_ns) { - dblk.w8(126, blocks[i+1].first); - dblk.w8(127, blocks[i+1].second); + dblk->w8(126, blocks[i+1].first); + dblk->w8(127, blocks[i+1].second); } else - dblk.w16l(126, 0); + dblk->w16l(126, 0); } - u16 end_address = (get_u16le(entry + 0xc) + data.size() - 1) & 0xffff; + u16 end_address = (get_u16le(entry + 0xc) + data.size()) & 0xffff; put_u16le(entry + 0xe, end_address); if(need_ns) { entry[0xa] = blocks[0].first; @@ -378,7 +380,7 @@ err_t vtech_impl::file_write(const std::vector<std::string> &path, const std::ve } else put_u16le(entry + 0xa, 0); - return ERR_OK; + return std::error_condition(); } std::vector<std::pair<u8, u8>> vtech_impl::allocate_blocks(u32 count) @@ -392,8 +394,8 @@ std::vector<std::pair<u8, u8>> vtech_impl::allocate_blocks(u32 count) for(u8 sector = 0; sector != 16; sector++) { u32 off = (track-1)*2 + (sector / 8); u32 bit = 1 << (sector & 7); - if(!(fmap.r8(off) & bit)) { - fmap.w8(off, fmap.r8(off) | bit); + if(!(fmap->r8(off) & bit)) { + fmap->w8(off, fmap->r8(off) | bit); blocks.emplace_back(std::make_pair(track, sector)); if(blocks.size() == count) return blocks; @@ -410,7 +412,7 @@ void vtech_impl::free_blocks(const std::vector<std::pair<u8, u8>> &blocks) u8 sector = ref.second; u32 off = (track-1)*2 + (sector / 8); u32 bit = 1 << (sector & 7); - fmap.w8(off, fmap.r8(off) & ~bit); + fmap->w8(off, fmap->r8(off) & ~bit); } } @@ -419,7 +421,7 @@ u32 vtech_impl::free_block_count() auto fmap = m_blockdev.get(15); u32 nf = 0; for(u32 off = 0; off != (40-1)*2; off++) { - u8 m = fmap.r8(off); + u8 m = fmap->r8(off); // Count 1 bits; m = ((m & 0xaa) >> 1) | (m & 0x55); m = ((m & 0xcc) >> 2) | (m & 0x33); diff --git a/src/lib/formats/fsblk.cpp b/src/lib/formats/fsblk.cpp index d766e389bee..5e8198d0ce8 100644 --- a/src/lib/formats/fsblk.cpp +++ b/src/lib/formats/fsblk.cpp @@ -13,154 +13,124 @@ namespace fs { -void refcounted_inner::ref() -{ - m_ref ++; -} - -void refcounted_inner::ref_weak() -{ - m_weak_ref ++; -} - -bool refcounted_inner::unref() -{ - m_ref --; - if(m_ref == 0) { - if(m_weak_ref) - drop_weak_references(); - else - delete this; - return true; - } - return false; -} - -bool refcounted_inner::unref_weak() -{ - m_weak_ref --; - if(m_weak_ref == 0 && m_ref == 0) { - delete this; - return true; - } - return false; -} - - - void fsblk_t::set_block_size(u32 block_size) { m_block_size = block_size; } -u8 *fsblk_t::iblock_t::offset(const char *function, u32 off, u32 size) +const u8 *fsblk_t::block_t::roffs(const char *function, u32 off, u32 size) const { if(off + size > m_size) - throw std::out_of_range(util::string_format("block_t::%s out-of-block access, offset=%d, size=%d, block size=%d", function, off, size, m_size)); - return data() + off; + throw std::out_of_range(util::string_format("block_t::%s out-of-block read access, offset=%d, size=%d, block size=%d", function, off, size, m_size)); + return rodata() + off; } -const u8 *fsblk_t::iblock_t::rooffset(const char *function, u32 off, u32 size) +u8 *fsblk_t::block_t::woffs(const char *function, u32 off, u32 size) { if(off + size > m_size) - throw std::out_of_range(util::string_format("block_t::%s out-of-block read access, offset=%d, size=%d, block size=%d\n", function, off, size, m_size)); - return rodata() + off; + throw std::out_of_range(util::string_format("block_t::%s out-of-block access, offset=%d, size=%d, block size=%d", function, off, size, m_size)); + return data() + off; } -void fsblk_t::block_t::copy(u32 offset, const u8 *src, u32 size) +void fsblk_t::block_t::write(u32 offset, const u8 *src, u32 size) { - memcpy(m_object->offset("copy", offset, size), src, size); + memcpy(woffs("write", offset, size), src, size); } void fsblk_t::block_t::fill(u32 offset, u8 data, u32 size) { - memset(m_object->offset("fill", offset, size), data, size); + memset(woffs("fill", offset, size), data, size); } void fsblk_t::block_t::fill(u8 data) { - memset(m_object->data(), data, m_object->size()); + memset(this->data(), data, size()); } void fsblk_t::block_t::wstr(u32 offset, std::string_view str) { - memcpy(m_object->offset("wstr", offset, str.size()), str.data(), str.size()); + memcpy(woffs("wstr", offset, str.size()), str.data(), str.size()); } void fsblk_t::block_t::w8(u32 offset, u8 data) { - m_object->offset("w8", offset, 1)[0] = data; + woffs("w8", offset, 1)[0] = data; } void fsblk_t::block_t::w16b(u32 offset, u16 data) { - put_u16be(m_object->offset("w16b", offset, 2), data); + put_u16be(woffs("w16b", offset, 2), data); } void fsblk_t::block_t::w24b(u32 offset, u32 data) { - put_u24be(m_object->offset("w24b", offset, 3), data); + put_u24be(woffs("w24b", offset, 3), data); } void fsblk_t::block_t::w32b(u32 offset, u32 data) { - put_u32be(m_object->offset("w32b", offset, 4), data); + put_u32be(woffs("w32b", offset, 4), data); } void fsblk_t::block_t::w16l(u32 offset, u16 data) { - put_u16le(m_object->offset("w16l", offset, 2), data); + put_u16le(woffs("w16l", offset, 2), data); } void fsblk_t::block_t::w24l(u32 offset, u32 data) { - put_u24le(m_object->offset("w24l", offset, 3), data); + put_u24le(woffs("w24l", offset, 3), data); } void fsblk_t::block_t::w32l(u32 offset, u32 data) { - put_u32le(m_object->offset("w32l", offset, 4), data); + put_u32le(woffs("w32l", offset, 4), data); +} + +void fsblk_t::block_t::read(u32 offset, u8 *dst, u32 size) const +{ + memcpy(dst, roffs("read", offset, size), size); } std::string_view fsblk_t::block_t::rstr(u32 offset, u32 size) const { - const u8 *d = m_object->rooffset("rstr", offset, size); + const u8 *d = roffs("rstr", offset, size); return std::string_view(reinterpret_cast<const char *>(d), size); } u8 fsblk_t::block_t::r8(u32 offset) const { - return m_object->offset("r8", offset, 1)[0]; + return roffs("r8", offset, 1)[0]; } u16 fsblk_t::block_t::r16b(u32 offset) const { - return get_u16be(m_object->offset("r16b", offset, 2)); + return get_u16be(roffs("r16b", offset, 2)); } u32 fsblk_t::block_t::r24b(u32 offset) const { - return get_u24be(m_object->offset("r24b", offset, 3)); + return get_u24be(roffs("r24b", offset, 3)); } u32 fsblk_t::block_t::r32b(u32 offset) const { - return get_u32be(m_object->offset("r32b", offset, 4)); + return get_u32be(roffs("r32b", offset, 4)); } u16 fsblk_t::block_t::r16l(u32 offset) const { - return get_u16le(m_object->offset("r16l", offset, 2)); + return get_u16le(roffs("r16l", offset, 2)); } u32 fsblk_t::block_t::r24l(u32 offset) const { - return get_u24le(m_object->offset("r24l", offset, 3)); + return get_u24le(roffs("r24l", offset, 3)); } u32 fsblk_t::block_t::r32l(u32 offset) const { - return get_u32le(m_object->offset("r32l", offset, 4)); + return get_u32le(roffs("r32l", offset, 4)); } @@ -186,69 +156,99 @@ meta_data filesystem_t::volume_metadata() return meta_data(); } -err_t filesystem_t::volume_metadata_change(const meta_data &meta) +std::error_condition filesystem_t::volume_metadata_change(const meta_data &meta) +{ + return error::unsupported; +} + +std::pair<std::error_condition, meta_data> filesystem_t::metadata(const std::vector<std::string> &path) { - return ERR_UNSUPPORTED; + return std::make_pair(error::unsupported, meta_data()); } -std::pair<err_t, meta_data> filesystem_t::metadata(const std::vector<std::string> &path) +std::error_condition filesystem_t::metadata_change(const std::vector<std::string> &path, const meta_data &meta) { - return std::make_pair(ERR_UNSUPPORTED, meta_data()); + return error::unsupported; } -err_t filesystem_t::metadata_change(const std::vector<std::string> &path, const meta_data &meta) +std::pair<std::error_condition, std::vector<dir_entry>> filesystem_t::directory_contents(const std::vector<std::string> &path) { - return ERR_UNSUPPORTED; + return std::make_pair(error::unsupported, std::vector<dir_entry>()); } -std::pair<err_t, std::vector<dir_entry>> filesystem_t::directory_contents(const std::vector<std::string> &path) +std::error_condition filesystem_t::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) { - return std::make_pair(ERR_UNSUPPORTED, std::vector<dir_entry>()); + return error::unsupported; } -err_t filesystem_t::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) +std::error_condition filesystem_t::remove(const std::vector<std::string> &path) { - return ERR_UNSUPPORTED; + return error::unsupported; } -err_t filesystem_t::remove(const std::vector<std::string> &path) +std::error_condition filesystem_t::dir_create(const std::vector<std::string> &path, const meta_data &meta) { - return ERR_UNSUPPORTED; + return error::unsupported; } -err_t filesystem_t::dir_create(const std::vector<std::string> &path, const meta_data &meta) +std::error_condition filesystem_t::file_create(const std::vector<std::string> &path, const meta_data &meta) { - return ERR_UNSUPPORTED; + return error::unsupported; } -err_t filesystem_t::file_create(const std::vector<std::string> &path, const meta_data &meta) +std::pair<std::error_condition, std::vector<u8>> filesystem_t::file_read(const std::vector<std::string> &path) { - return ERR_UNSUPPORTED; + return std::make_pair(error::unsupported, std::vector<u8>()); } -std::pair<err_t, std::vector<u8>> filesystem_t::file_read(const std::vector<std::string> &path) +std::error_condition filesystem_t::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) { - return std::make_pair(ERR_UNSUPPORTED, std::vector<u8>()); + return error::unsupported; } -err_t filesystem_t::file_write(const std::vector<std::string> &path, const std::vector<u8> &data) +std::pair<std::error_condition, std::vector<u8>> filesystem_t::file_rsrc_read(const std::vector<std::string> &path) { - return ERR_UNSUPPORTED; + return std::make_pair(error::unsupported, std::vector<u8>()); } -std::pair<err_t, std::vector<u8>> filesystem_t::file_rsrc_read(const std::vector<std::string> &path) +std::error_condition filesystem_t::file_rsrc_write(const std::vector<std::string> &path, const std::vector<u8> &data) { - return std::make_pair(ERR_UNSUPPORTED, std::vector<u8>()); + return error::unsupported; } -err_t filesystem_t::file_rsrc_write(const std::vector<std::string> &path, const std::vector<u8> &data) +std::error_condition filesystem_t::format(const meta_data &meta) { - return ERR_UNSUPPORTED; + return error::unsupported; } -err_t filesystem_t::format(const meta_data &meta) +std::error_category const &fs_category() noexcept { - return ERR_UNSUPPORTED; + class fs_category_impl : public std::error_category + { + public: + virtual char const *name() const noexcept override { return "fs"; } + + virtual std::string message(int condition) const override + { + using namespace std::literals; + static std::string_view const s_messages[] = { + "No error"sv, + "Unsupported operation"sv, + "File or directory not found"sv, + "No space on volume"sv, + "Invalid block number"sv, + "Invalid filename or path"sv, + "Incorrect file size"sv, + "File already exists"sv, + }; + if ((0 <= condition) && (std::size(s_messages) > condition)) + return std::string(s_messages[condition]); + else + return "Unknown error"s; + } + }; + static fs_category_impl const s_fs_category_instance; + return s_fs_category_instance; } } // namespace fs diff --git a/src/lib/formats/fsblk.h b/src/lib/formats/fsblk.h index 63ed910a507..7f368a7307c 100644 --- a/src/lib/formats/fsblk.h +++ b/src/lib/formats/fsblk.h @@ -10,8 +10,10 @@ #include "fsmeta.h" +#include <memory> #include <string> #include <string_view> +#include <system_error> #include <utility> #include <vector> @@ -22,110 +24,14 @@ using u16 = uint16_t; using u32 = uint32_t; using u64 = uint64_t; -enum err_t { - ERR_OK = 0, - ERR_UNSUPPORTED, - ERR_INVALID, - ERR_NOT_FOUND, - ERR_NOT_EMPTY, - ERR_NO_SPACE, -}; - -template<typename T> class refcounted_outer { -public: - refcounted_outer(bool weak) : m_object(nullptr), m_is_weak_ref(weak) {} - refcounted_outer(T *object, bool weak) : m_object(object), m_is_weak_ref(weak) { - ref(); - } - - refcounted_outer(const refcounted_outer &cref) { - m_object = cref.m_object; - m_is_weak_ref = cref.m_is_weak_ref; - ref(); - } - - refcounted_outer(refcounted_outer &&cref) { - m_object = cref.m_object; - m_is_weak_ref = cref.m_is_weak_ref; - cref.m_object = nullptr; - } - - ~refcounted_outer() { - unref(); - } - - refcounted_outer<T> &operator =(T *dir) { - if(m_object != dir) { - unref(); - m_object = dir; - ref(); - } - return *this; - } - - refcounted_outer<T> &operator =(const refcounted_outer<T> &cref) { - if(m_object != cref.m_object) { - unref(); - m_object = cref.m_object; - ref(); - } - return *this; - } - - refcounted_outer<T> &operator =(refcounted_outer<T> &&cref) { - if(m_object != cref.m_object) { - unref(); - m_object = cref.m_object; - ref(); - } else if(m_is_weak_ref != cref.m_is_weak_ref) { - ref(); - cref.unref(); - m_object = cref.m_object; // In case the object got deleted (when going from strong ref to weak on the last strong) - } - cref.m_object = nullptr; - return *this; - } - - operator bool() const { return m_object != nullptr; } - -protected: - T *m_object; - bool m_is_weak_ref; - -private: - void ref() { - if(m_object) { - if(m_is_weak_ref) - m_object->ref_weak(); - else - m_object->ref(); - } - } - - void unref() { - if(m_object) { - bool del = m_is_weak_ref ? m_object->unref_weak() : m_object->unref(); - if(del) - m_object = nullptr; - } - } -}; - - -class refcounted_inner { -public: - refcounted_inner() : m_ref(0), m_weak_ref(0) {} - virtual ~refcounted_inner() = default; - - void ref(); - void ref_weak(); - bool unref(); - bool unref_weak(); - - virtual void drop_weak_references() = 0; - -public: - u32 m_ref, m_weak_ref; +enum class error : int { + unsupported = 1, + not_found, + no_space, + invalid_block, + invalid_name, + incorrect_size, + already_exists, }; enum class dir_entry_type { @@ -142,51 +48,32 @@ struct dir_entry { }; class fsblk_t { -protected: - class iblock_t : public refcounted_inner { - public: - iblock_t(u32 size) : refcounted_inner(), m_size(size) {} - virtual ~iblock_t() = default; - - u32 size() const { return m_size; } - - virtual const u8 *rodata() = 0; - virtual u8 *data() = 0; - u8 *offset(const char *function, u32 off, u32 size); - const u8 *rooffset(const char *function, u32 off, u32 size); - - protected: - u32 m_size; - }; - - public: - class block_t : public refcounted_outer<iblock_t> { + class block_t { public: - block_t(bool weak = false) : refcounted_outer<iblock_t>(weak) {} - block_t(iblock_t *block, bool weak = true) : refcounted_outer(block, weak) {} - virtual ~block_t() = default; - - block_t strong() { return block_t(m_object, false); } - block_t weak() { return block_t(m_object, true); } + using ptr = std::shared_ptr<block_t>; - u32 size() const { return m_object->size(); } + block_t(u32 size) : m_size(size) {} + virtual ~block_t() = default; - const u8 *rodata() const { return m_object->rodata(); } - u8 *data() { return m_object->data(); } + u32 size() const { return m_size; } - void copy(u32 offset, const u8 *src, u32 size); - void fill( u8 data); - void fill(u32 offset, u8 data, u32 size); - void wstr(u32 offset, std::string_view str); - void w8( u32 offset, u8 data); - void w16b(u32 offset, u16 data); - void w24b(u32 offset, u32 data); - void w32b(u32 offset, u32 data); - void w16l(u32 offset, u16 data); - void w24l(u32 offset, u32 data); - void w32l(u32 offset, u32 data); + virtual const u8 *rodata() const = 0; + virtual u8 *data() = 0; + void write(u32 offset, const u8 *src, u32 size); + void fill( u8 data); + void fill( u32 offset, u8 data, u32 size); + void wstr( u32 offset, std::string_view str); + void w8( u32 offset, u8 data); + void w16b( u32 offset, u16 data); + void w24b( u32 offset, u32 data); + void w32b( u32 offset, u32 data); + void w16l( u32 offset, u16 data); + void w24l( u32 offset, u32 data); + void w32l( u32 offset, u32 data); + + void read(u32 offset, u8 *dst, u32 size) const; std::string_view rstr(u32 offset, u32 size) const; u8 r8( u32 offset) const; u16 r16b(u32 offset) const; @@ -195,6 +82,13 @@ public: u16 r16l(u32 offset) const; u32 r24l(u32 offset) const; u32 r32l(u32 offset) const; + + protected: + u32 m_size; + + private: + const u8 *roffs(const char *function, u32 off, u32 size) const; + u8 *woffs(const char *function, u32 off, u32 size); }; fsblk_t() : m_block_size(0) {} @@ -202,8 +96,8 @@ public: virtual void set_block_size(u32 block_size); virtual u32 block_count() const = 0; - virtual block_t get(u32 id) = 0; - virtual void fill(u8 data) = 0; + virtual block_t::ptr get(u32 id) = 0; + virtual void fill_all(u8 data) = 0; protected: u32 m_block_size; @@ -218,44 +112,44 @@ public: virtual meta_data volume_metadata(); // Change the metadata for the volume - virtual err_t volume_metadata_change(const meta_data &meta); + virtual std::error_condition volume_metadata_change(const meta_data &meta); // Get the metadata for a file or a directory. Empty path targets the root directory - virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path); + virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path); // Change the metadata for a file or a directory. Empty path targets the root directory - virtual err_t metadata_change(const std::vector<std::string> &path, const meta_data &meta); + virtual std::error_condition metadata_change(const std::vector<std::string> &path, const meta_data &meta); // Get the contents of a directory, empty path targets the root directory - virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path); + virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path); // Rename a file or a directory. In contrast to metadata_change, this can move the object // between directories - virtual err_t rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath); + virtual std::error_condition rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath); // Remove a file or a directory. Directories must be empty (e.g. it's not recursive) - virtual err_t remove(const std::vector<std::string> &path); + virtual std::error_condition remove(const std::vector<std::string> &path); // Create a directory, path designates where the directory must be, directory name is in meta - virtual err_t dir_create(const std::vector<std::string> &path, const meta_data &meta); + virtual std::error_condition dir_create(const std::vector<std::string> &path, const meta_data &meta); // Create an empty file, path designates where the file must be, file name is in meta - virtual err_t file_create(const std::vector<std::string> &path, const meta_data &meta); + virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta); // Read the contents of a file - virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path); + virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path); // Replace the contents of a file, the file must already exist - virtual err_t file_write(const std::vector<std::string> &path, const std::vector<u8> &data); + virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data); // Read the resource fork of a file on systems that handle those - virtual std::pair<err_t, std::vector<u8>> file_rsrc_read(const std::vector<std::string> &path); + virtual std::pair<std::error_condition, std::vector<u8>> file_rsrc_read(const std::vector<std::string> &path); // Replace the resource fork of a file, the file must already exist - virtual err_t file_rsrc_write(const std::vector<std::string> &path, const std::vector<u8> &data); + virtual std::error_condition file_rsrc_write(const std::vector<std::string> &path, const std::vector<u8> &data); // Format an image, provide the volume metadata - virtual err_t format(const meta_data &meta); + virtual std::error_condition format(const meta_data &meta); static void wstr(u8 *p, std::string_view str); @@ -271,6 +165,17 @@ protected: fsblk_t &m_blockdev; }; +// error category for filesystem errors +std::error_category const &fs_category() noexcept; +inline std::error_condition make_error_condition(error err) noexcept { return std::error_condition(int(err), fs_category()); } + } // namespace fs + +namespace std { + +template <> struct is_error_condition_enum<fs::error> : public std::true_type { }; + +} // namespace std + #endif diff --git a/src/lib/formats/fsblk_vec.cpp b/src/lib/formats/fsblk_vec.cpp index 36f11cc9d89..a04d60c263c 100644 --- a/src/lib/formats/fsblk_vec.cpp +++ b/src/lib/formats/fsblk_vec.cpp @@ -12,7 +12,7 @@ namespace fs { -const u8 *fsblk_vec_t::blk_t::rodata() +const u8 *fsblk_vec_t::blk_t::rodata() const { return m_data; } @@ -22,23 +22,19 @@ u8 *fsblk_vec_t::blk_t::data() return m_data; } -void fsblk_vec_t::blk_t::drop_weak_references() -{ -} - u32 fsblk_vec_t::block_count() const { return m_data.size() / m_block_size; } -fsblk_t::block_t fsblk_vec_t::get(u32 id) +fsblk_vec_t::block_t::ptr fsblk_vec_t::get(u32 id) { if(id >= block_count()) - throw std::out_of_range(util::string_format("Block number overflow: requiring block %d on device of size %d (%d bytes, block size %d)\n", id, block_count(), m_data.size(), m_block_size)); - return block_t(new blk_t(m_data.data() + m_block_size*id, m_block_size)); + throw std::out_of_range(util::string_format("Block number overflow: requiring block %d on device of size %d (%d bytes, block size %d)", id, block_count(), m_data.size(), m_block_size)); + return std::make_shared<blk_t>(m_data.data() + m_block_size*id, m_block_size); } -void fsblk_vec_t::fill(u8 data) +void fsblk_vec_t::fill_all(u8 data) { std::fill(m_data.begin(), m_data.end(), data); } diff --git a/src/lib/formats/fsblk_vec.h b/src/lib/formats/fsblk_vec.h index 49f7ed3013e..b029bc55c5e 100644 --- a/src/lib/formats/fsblk_vec.h +++ b/src/lib/formats/fsblk_vec.h @@ -9,14 +9,13 @@ namespace fs { class fsblk_vec_t : public fsblk_t { private: - class blk_t : public iblock_t { + class blk_t : public block_t { public: - blk_t(u8 *data, u32 size) : iblock_t(size), m_data(data) {} + blk_t(u8 *data, u32 size) : block_t(size), m_data(data) {} virtual ~blk_t() = default; - virtual const u8 *rodata() override; + virtual const u8 *rodata() const override; virtual u8 *data() override; - virtual void drop_weak_references() override; private: u8 *m_data; @@ -27,8 +26,8 @@ public: virtual ~fsblk_vec_t() = default; virtual u32 block_count() const override; - virtual block_t get(u32 id) override; - virtual void fill(u8 data) override; + virtual block_t::ptr get(u32 id) override; + virtual void fill_all(u8 data) override; private: std::vector<u8> &m_data; diff --git a/src/lib/formats/fsd_dsk.cpp b/src/lib/formats/fsd_dsk.cpp index e4909ba37f3..b386dc026c6 100644 --- a/src/lib/formats/fsd_dsk.cpp +++ b/src/lib/formats/fsd_dsk.cpp @@ -91,9 +91,11 @@ bool fsd_format::supports_save() const noexcept int fsd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t h[3]; - - size_t actual; - io.read_at(0, h, 3, actual); + auto const [err, actual] = read_at(io, 0, h, 3); + if (err || (3 != actual)) { + LOG_FORMATS("fsd: read error\n"); + return 0; + } if (memcmp(h, "FSD", 3) == 0) { return FIFID_SIGN; } @@ -111,9 +113,9 @@ bool fsd_format::load(util::random_read &io, uint32_t form_factor, const std::ve uint64_t size; if(io.length(size)) return false; - std::vector<uint8_t> img(size); - size_t actual; - io.read_at(0, &img[0], size, actual); + auto const [err, img, actual] = read_at(io, 0, size); + if(err || (actual != size)) + return false; uint64_t pos; std::string title; diff --git a/src/lib/formats/fsmeta.cpp b/src/lib/formats/fsmeta.cpp index f3e91e706a0..3c731f21939 100644 --- a/src/lib/formats/fsmeta.cpp +++ b/src/lib/formats/fsmeta.cpp @@ -14,7 +14,6 @@ namespace fs { const char *meta_data::entry_name(meta_name name) { switch(name) { - case meta_name::basic: return "basic"; case meta_name::creation_date: return "creation_date"; case meta_name::length: return "length"; case meta_name::loading_address: return "loading_address"; diff --git a/src/lib/formats/fsmeta.h b/src/lib/formats/fsmeta.h index 0f6baf20133..ff0b505ab8b 100644 --- a/src/lib/formats/fsmeta.h +++ b/src/lib/formats/fsmeta.h @@ -21,7 +21,6 @@ namespace fs { enum class meta_name { - basic, creation_date, length, loading_address, diff --git a/src/lib/formats/g64_dsk.cpp b/src/lib/formats/g64_dsk.cpp index 568cc8f5027..177438dc9a6 100644 --- a/src/lib/formats/g64_dsk.cpp +++ b/src/lib/formats/g64_dsk.cpp @@ -36,9 +36,10 @@ const uint32_t g64_format::c1541_cell_size[] = int g64_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { char h[8]; + auto const [err, actual] = read_at(io, 0, h, 8); + if (err || (8 != actual)) + return 0; - size_t actual; - io.read_at(0, h, 8, actual); if (!memcmp(h, G64_FORMAT_HEADER, 8)) return FIFID_SIGN; @@ -51,9 +52,9 @@ bool g64_format::load(util::random_read &io, uint32_t form_factor, const std::ve if (io.length(size)) return false; - std::vector<uint8_t> img(size); - size_t actual; - io.read_at(0, &img[0], size, actual); + auto const [err, img, actual] = read_at(io, 0, size); + if (err || (actual != size)) + return false; if (img[POS_VERSION]) { @@ -124,7 +125,6 @@ bool g64_format::save(util::random_read_write &io, const std::vector<uint32_t> & { uint8_t const zerofill[] = { 0x00, 0x00, 0x00, 0x00 }; std::vector<uint8_t> const prefill(TRACK_LENGTH, 0xff); - size_t actual; int tracks, heads; image.get_actual_geometry(tracks, heads); @@ -132,7 +132,7 @@ bool g64_format::save(util::random_read_write &io, const std::vector<uint32_t> & // write header uint8_t header[] = { 'G', 'C', 'R', '-', '1', '5', '4', '1', 0x00, static_cast<uint8_t>(tracks), TRACK_LENGTH & 0xff, TRACK_LENGTH >> 8 }; - io.write_at(POS_SIGNATURE, header, sizeof(header), actual); + write_at(io, POS_SIGNATURE, header, sizeof(header)); // FIXME: check for errors // write tracks for (int head = 0; head < heads; head++) { @@ -145,8 +145,8 @@ bool g64_format::save(util::random_read_write &io, const std::vector<uint32_t> & uint32_t const spos = tpos + (tracks * 4); uint32_t const dpos = POS_TRACK_OFFSET + (tracks * 4 * 2) + (tracks_written * TRACK_LENGTH); - io.write_at(tpos, zerofill, 4, actual); - io.write_at(spos, zerofill, 4, actual); + write_at(io, tpos, zerofill, 4); // FIXME: check for errors + write_at(io, spos, zerofill, 4); // FIXME: check for errors if (image.get_buffer(track, head).size() <= 1) continue; @@ -178,11 +178,11 @@ bool g64_format::save(util::random_read_write &io, const std::vector<uint32_t> & put_u32le(speed_offset, speed_zone); put_u16le(track_length, packed.size()); - io.write_at(tpos, track_offset, 4, actual); - io.write_at(spos, speed_offset, 4, actual); - io.write_at(dpos, prefill.data(), TRACK_LENGTH, actual); - io.write_at(dpos, track_length, 2, actual); - io.write_at(dpos + 2, packed.data(), packed.size(), actual); + write_at(io, tpos, track_offset, 4); // FIXME: check for errors + write_at(io, spos, speed_offset, 4); // FIXME: check for errors + write_at(io, dpos, prefill.data(), TRACK_LENGTH); // FIXME: check for errors + write_at(io, dpos, track_length, 2); // FIXME: check for errors + write_at(io, dpos + 2, packed.data(), packed.size()); // FIXME: check for errors tracks_written++; } diff --git a/src/lib/formats/gtp_cas.cpp b/src/lib/formats/gtp_cas.cpp index 85caa73fd02..dc996aa89db 100644 --- a/src/lib/formats/gtp_cas.cpp +++ b/src/lib/formats/gtp_cas.cpp @@ -127,7 +127,7 @@ static int gtp_cas_to_wav_size( const uint8_t *casdata, int caslen ) { return size; } -static int gtp_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) { +static int gtp_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { int i,size,n; size = 0; n = 0; diff --git a/src/lib/formats/h17disk.cpp b/src/lib/formats/h17disk.cpp new file mode 100644 index 00000000000..9fb211ddb0d --- /dev/null +++ b/src/lib/formats/h17disk.cpp @@ -0,0 +1,253 @@ +// license:BSD-3-Clause +// copyright-holders:Mark Garlanger +/********************************************************************* + +Heath H17D disk image format (version 2.0.0) + + Format for Heath hard-sectored 5.25" disk images. + + See https://heathkit.garlanger.com/diskformats/ for more information + + TODO - implement writing to H17D image + +*********************************************************************/ + +#include "h17disk.h" + +#include "imageutl.h" + +#include "ioprocs.h" + +#include <cstring> + + +namespace { + +constexpr int TRACK_SIZE = 50'000; +constexpr int BITCELL_SIZE = 4000; + +constexpr int SECTOR_METADATA_SIZE = 16; + +constexpr int SECTOR_DATA_SIZE = 256; +constexpr int SECTORS_PER_TRACK = 10; + +struct format { + int head_count; + int track_count; + uint32_t variant; +}; + +const format formats[] = { + { 1, 40, floppy_image::SSSD10 }, // H-17-1 + { 2, 40, floppy_image::DSSD10 }, + { 1, 80, floppy_image::SSQD10 }, + { 2, 80, floppy_image::DSQD10 }, // H-17-4 + {} +}; + +struct block_header { + uint32_t block_name; + uint32_t length; +}; + +enum { + DskF = 0x466b7344, //!< "DskF", Disk Format + Parm = 0x6b726150, //!< "Parm", Parameters + Date = 0x65746144, //!< "Date", Date + Imgr = 0x72676d49, //!< "Imgr", Imager + + Prog = 0x676f7250, //!< "Prog", Program (creation) + Padd = 0x64646150, //!< "Padd", Padding + H8DB = 0x42443848, //!< "H8DB", H8D data block + SecM = 0x4d636553, //!< "SecM", Sector Metadata + Labl = 0x6c62614c, //!< "Labl", Label + Comm = 0x6d6d6f43, //!< "Comm", Comment +}; + +std::pair<std::uint64_t, std::size_t> find_block(util::random_read &io, uint32_t block_id) +{ + LOG_FORMATS("find_block: 0x%x\n", block_id); + + // start of file + int pos = 0; + block_header header = { 0, 0 }; + + do + { + pos += header.length + 8; + auto const [err, actual] = read_at(io, pos, (void *) &header, 8); + if (err || actual !=8) + { + return std::make_pair(0, 0); + } + header.length = swapendian_int32(header.length); + } + while (header.block_name != block_id); + + // update position to point to data portion of the block + return std::make_pair(pos + 8, header.length); +} + +format find_format(util::random_read &io) +{ + auto const [pos, length] = find_block(io, DskF); + if ((pos == 0) || (length < 2) || (length > 3)) + { + LOG_FORMATS("Can't find valid DskF block %d/%d\n", pos, length); + + return {}; + } + + uint8_t buf[3]; + + auto const [err, actual] = read_at(io, pos, buf, length); + if (err || (actual != length)) + { + LOG_FORMATS("read error\n"); + + return {}; + } + + int head_count = buf[0]; + int track_count = buf[1]; + + for (int i = 0; formats[i].head_count; i++) + { + if ((formats[i].head_count == head_count) && (formats[i].track_count == track_count)) + { + LOG_FORMATS("find_format format found: %d - variant: 0x%x\n", i, formats[i].variant); + + return formats[i]; + } + } + + LOG_FORMATS("Invalid disk format - heads: %d, tracks: %d\n", head_count, track_count); + return {}; +} + +} // anonymous namespace + + +heath_h17d_format::heath_h17d_format() : floppy_image_format_t() +{ +} + +int heath_h17d_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const +{ + uint8_t h[4]; + auto const [err, actual] = read_at(io, 0, h, 4); + + if (err || (actual != 4)) + { + return 0; + } + + // Verify "H17D" Signature. + if ((h[0] == 0x48) && (h[1] == 0x31) && (h[2] == 0x37) && (h[3] == 0x44)) + { + return FIFID_SIGN; + } + + return 0; +} + +bool heath_h17d_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const +{ + const format fmt = find_format(io); + + if (!fmt.head_count) + { + LOG_FORMATS("invalid format\n"); + + return false; + } + + image.set_variant(fmt.variant); + + std::vector<uint32_t> buf; + + auto const [secm_pos, secm_length] = find_block(io, SecM); + + uint8_t sector_meta_data[SECTOR_METADATA_SIZE]; + uint8_t sector_data[SECTOR_DATA_SIZE]; + + for (int head = 0; head < fmt.head_count; head++) + { + for (int track = 0; track < fmt.track_count; track++) + { + for (int sector = 0; sector < SECTORS_PER_TRACK; sector++) + { + int sect_meta_pos = (sector + (track * fmt.head_count + head) * SECTORS_PER_TRACK) * SECTOR_METADATA_SIZE + secm_pos; + + auto const [err, actual] = read_at(io, sect_meta_pos, sector_meta_data, SECTOR_METADATA_SIZE); + + if (err || (actual != SECTOR_METADATA_SIZE)) + { + LOG_FORMATS("unable to read sect meta data %d/%d/%d\n", head, track, sector); + + return false; + } + int data_offset = sector_meta_data[0] << 24 | sector_meta_data[1] << 16 | sector_meta_data[2] << 8 | sector_meta_data[3]; + + auto const [err2, actual2] = read_at(io, data_offset, sector_data, SECTOR_DATA_SIZE); + + if (err2 || (actual2 != SECTOR_DATA_SIZE)) + { + LOG_FORMATS("unable to read sect data %d/%d/%d\n", head, track, sector); + + return false; + } + + // Inital 15 zero bytes + for (int i = 0; i < 15; i++) + { + fm_reverse_byte_w(buf, 0); + } + + // header (sync byte, volume, track, sector, checksum) + for (int i = 0; i < 5; i++) + { + fm_reverse_byte_w(buf, sector_meta_data[5 + i]); + } + + // 12 zero bytes + for (int i = 0; i < 12; i++) + { + fm_reverse_byte_w(buf, 0); + } + + // data sync byte + fm_reverse_byte_w(buf, sector_meta_data[10]); + + // sector data + for (int i = 0; i < 256; i++) + { + fm_reverse_byte_w(buf, sector_data[i]); + } + + // sector data checksum + fm_reverse_byte_w(buf, sector_meta_data[11]); + + // trailing zero's until the next sector hole usually ~ 30 characters. + while (buf.size() < TRACK_SIZE / SECTORS_PER_TRACK * (sector + 1)) + { + fm_reverse_byte_w(buf, 0); + } + } + + generate_track_from_levels(track, head, buf, 0, image); + buf.clear(); + } + } + + return true; +} + +void heath_h17d_format::fm_reverse_byte_w(std::vector<uint32_t> &buffer, uint8_t val) const +{ + constexpr unsigned char lookup[16] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf }; + + fm_w(buffer, 8, lookup[val & 0x0f] << 4 | lookup[val >> 4], BITCELL_SIZE); +} + +const heath_h17d_format FLOPPY_H17D_FORMAT; diff --git a/src/lib/formats/h17disk.h b/src/lib/formats/h17disk.h new file mode 100644 index 00000000000..19ac0bac052 --- /dev/null +++ b/src/lib/formats/h17disk.h @@ -0,0 +1,38 @@ +// license:BSD-3-Clause +// copyright-holders:Mark Garlanger +/********************************************************************* + +Heath h17disk disk image format + +The Heath hard-sectored disk format for the H8 and H89 systems with the +H17 controller on the H8 and the H-88-1 controller on the H89. + +*********************************************************************/ +#ifndef MAME_FORMATS_H17DISK_H +#define MAME_FORMATS_H17DISK_H + +#pragma once + +#include "flopimg.h" + +class heath_h17d_format : public floppy_image_format_t +{ +public: + heath_h17d_format(); + + int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override; + bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override; + + const char *name() const noexcept override { return "h17disk"; } + const char *description() const noexcept override { return "Heath H17D disk image"; } + const char *extensions() const noexcept override { return "h17,h17d,h17disk"; } + bool supports_save() const noexcept override { return false; } + +protected: + + void fm_reverse_byte_w(std::vector<uint32_t> &buffer, uint8_t val) const; +}; + +extern const heath_h17d_format FLOPPY_H17D_FORMAT; + +#endif // MAME_FORMATS_H17DISK_H diff --git a/src/lib/formats/h8_cas.cpp b/src/lib/formats/h8_cas.cpp index c0d5d6dd925..526e411d5ae 100644 --- a/src/lib/formats/h8_cas.cpp +++ b/src/lib/formats/h8_cas.cpp @@ -1,147 +1,96 @@ // license:BSD-3-Clause -// copyright-holders:Robbbert +// copyright-holders:Robbbert,Mark Garlanger /******************************************************************** -Support for Heathkit H8 H8T cassette images +Support for Heathkit H8/H88 H8T cassette images Standard Kansas City format (300 baud) +TODO - investigate 1200 buad support, H8 should support it, but H88 does not. + We output a leader, followed by the contents of the H8T file. ********************************************************************/ #include "h8_cas.h" -#define WAVEENTRY_LOW -32768 -#define WAVEENTRY_HIGH 32767 - -#define H8_WAV_FREQUENCY 9600 - -// image size -static int h8_image_size; // FIXME: global variable prevents multiple instances - -static int h8_put_samples(int16_t *buffer, int sample_pos, int count, int level) -{ - if (buffer) - { - for (int i=0; i<count; i++) - buffer[sample_pos + i] = level; - } - - return count; -} - -static int h8_output_bit(int16_t *buffer, int sample_pos, bool bit) -{ - int samples = 0; - - for (uint8_t i = 0; i < 4; i++) - { - if (bit) - { - samples += h8_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW); - samples += h8_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH); - samples += h8_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW); - samples += h8_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH); - } - else - { - samples += h8_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_LOW); - samples += h8_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_HIGH); - } - } - - return samples; -} - -static int h8_output_byte(int16_t *buffer, int sample_pos, uint8_t byte) -{ - int samples = 0; - uint8_t i; +#include "coretmpl.h" // BIT - // start bit - samples += h8_output_bit (buffer, sample_pos + samples, 0); - // data bits - for (i = 0; i<8; i++) - samples += h8_output_bit (buffer, sample_pos + samples, (byte >> i) & 1); +namespace { - // stop bits - for (i = 0; i<2; i++) - samples += h8_output_bit (buffer, sample_pos + samples, 1); - - return samples; -} +constexpr double ONE_FREQ = 1200.0; +constexpr double ONE_FREQ_VARIANCE = 300.0; +constexpr double ZERO_FREQ = 2400.0; +constexpr double ZERO_FREQ_VARIANCE = 600.0; -static int h8_handle_cassette(int16_t *buffer, const uint8_t *bytes) +const cassette_image::Modulation heath_h8t_modulation = { - uint32_t sample_count = 0; - uint32_t byte_count = 0; - uint32_t i; - - - // leader - for (i=0; i<2000; i++) - sample_count += h8_output_bit(buffer, sample_count, 1); - - // data - for (i=byte_count; i<h8_image_size; i++) - sample_count += h8_output_byte(buffer, sample_count, bytes[i]); - - return sample_count; -} - - -/******************************************************************* - Generate samples for the tape image -********************************************************************/ + cassette_image::MODULATION_SINEWAVE, + ONE_FREQ - ONE_FREQ_VARIANCE, ONE_FREQ, ONE_FREQ + ONE_FREQ_VARIANCE, + ZERO_FREQ - ZERO_FREQ_VARIANCE, ZERO_FREQ, ZERO_FREQ + ZERO_FREQ_VARIANCE +}; -static int h8_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +cassette_image::error heath_h8t_identify(cassette_image *cassette, cassette_image::Options *opts) { - return h8_handle_cassette(buffer, bytes); + return cassette->modulation_identify(heath_h8t_modulation, opts); } -/******************************************************************* - Calculate the number of samples needed for this tape image -********************************************************************/ -static int h8_cassette_calculate_size_in_samples(const uint8_t *bytes, int length) +cassette_image::error heath_h8t_load(cassette_image *cassette) { - h8_image_size = length; + cassette_image::error err = cassette_image::error::SUCCESS; + uint64_t image_size = cassette->image_size(); + double time_index = 0.0; + double time_displacement; + + auto const MODULATE = + [&cassette, &err, &time_index, &time_displacement] (unsigned value) + { + for (int i = 0; (i < (value ? 8 : 4)); i++) + { + err = cassette->put_modulated_data_bit(0, time_index, value, heath_h8t_modulation, &time_displacement); + if (cassette_image::error::SUCCESS == err) + time_index += time_displacement; + else + return; + } + }; + + // leader - 1 second + while ((cassette_image::error::SUCCESS == err) && (time_index < 1.0)) + MODULATE(1); + + for (uint64_t image_pos = 0; (cassette_image::error::SUCCESS == err) && (image_pos < image_size); image_pos++) + { + uint8_t data = cassette->image_read_byte(image_pos); - return h8_handle_cassette(nullptr, bytes); -} + // start bit + MODULATE(0); -static const cassette_image::LegacyWaveFiller h8_legacy_fill_wave = -{ - h8_cassette_fill_wave, // fill_wave - -1, // chunk_size - 0, // chunk_samples - h8_cassette_calculate_size_in_samples, // chunk_sample_calc - H8_WAV_FREQUENCY, // sample_frequency - 0, // header_samples - 0 // trailer_samples -}; + // data bits + for (int bit = 0; (cassette_image::error::SUCCESS == err) && (bit < 8); bit++) + MODULATE(util::BIT(data, bit)); -static cassette_image::error h8_cassette_identify(cassette_image *cassette, cassette_image::Options *opts) -{ - return cassette->legacy_identify(opts, &h8_legacy_fill_wave); -} + // stop bit + if (cassette_image::error::SUCCESS == err) + MODULATE(1); + } -static cassette_image::error h8_cassette_load(cassette_image *cassette) -{ - return cassette->legacy_construct(&h8_legacy_fill_wave); + return err; } -static const cassette_image::Format h8_cassette_image_format = +const cassette_image::Format heath_h8t_format = { "h8t", - h8_cassette_identify, - h8_cassette_load, + heath_h8t_identify, + heath_h8t_load, nullptr }; -CASSETTE_FORMATLIST_START(h8_cassette_formats) - CASSETTE_FORMAT(h8_cassette_image_format) +} + +CASSETTE_FORMATLIST_START( h8_cassette_formats ) + CASSETTE_FORMAT( heath_h8t_format ) CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/h8_cas.h b/src/lib/formats/h8_cas.h index 8ad9f82006a..9c7ea02104a 100644 --- a/src/lib/formats/h8_cas.h +++ b/src/lib/formats/h8_cas.h @@ -1,10 +1,10 @@ // license:BSD-3-Clause -// copyright-holders:Robbbert +// copyright-holders:Robbbert,Mark Garlanger /********************************************************************* h8_cas.h - Format code for Heathkit H8 H8T cassette images + Format code for Heathkit H8/H88 H8T cassette images *********************************************************************/ #ifndef MAME_FORMATS_H8_CAS_H diff --git a/src/lib/formats/hect_tap.cpp b/src/lib/formats/hect_tap.cpp index 867d95cf4b0..cef1a730875 100644 --- a/src/lib/formats/hect_tap.cpp +++ b/src/lib/formats/hect_tap.cpp @@ -121,7 +121,7 @@ static int hector_handle_tap(int16_t *buffer, const uint8_t *casdata) if (data_pos>1) previous_block = casdata[data_pos-1]; - /* Handle block lenght on tape data */ + /* Handle block length on tape data */ block_size = casdata[data_pos] ; if (block_size==0) block_size=256; @@ -173,8 +173,8 @@ static int hector_handle_forth_tap(int16_t *buffer, const uint8_t *casdata) /* Starting a block with 768 cycle of synchro*/ sample_count += hector_tap_synchro( buffer, sample_count, 768 ); - /* Handle block lenght on tape data */ - block_size = 822 ; /* Fixed size for the forth*/ + /* Handle block length on tape data */ + block_size = 822 ; /* Fixed size for the forth */ /*block_count=0;*/ @@ -203,7 +203,7 @@ static int hector_handle_forth_tap(int16_t *buffer, const uint8_t *casdata) /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int hector_tap_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int hector_tap_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { return hector_handle_tap( buffer, bytes ); } @@ -222,7 +222,7 @@ static int hector_tap_forth_to_wav_size(const uint8_t *casdata, int caslen) /******************************************************************* Generate samples for the tape image FORTH ********************************************************************/ -static int hector_tap_forth_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int hector_tap_forth_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { return hector_handle_forth_tap( buffer, bytes ); //forth removed here !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! } diff --git a/src/lib/formats/hp300_dsk.cpp b/src/lib/formats/hp300_dsk.cpp index de33bf6d310..99c3c20f283 100644 --- a/src/lib/formats/hp300_dsk.cpp +++ b/src/lib/formats/hp300_dsk.cpp @@ -30,11 +30,11 @@ const char *hp300_format::extensions() const noexcept } const hp300_format::format hp300_format::formats[] = { + { floppy_image::FF_35, floppy_image::SSDD, floppy_image::MFM, 2000, 16, 66, 1, 256, {}, 0, {}, 32, 22, 46 }, // FORMAT 4 // HP 9121S, 9121D or 9133A { floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM, 2000, 16, 77, 2, 256, {}, 1, {}, 50, 22, 54 }, // FORMAT 0,1 { floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM, 2000, 9, 77, 2, 512, {}, 1, {}, 50, 22, 89 }, // FORMAT 2 { floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM, 2000, 5, 77, 2, 1024, {}, 1, {}, 50, 22, 108 }, // FORMAT 3 - { floppy_image::FF_35, floppy_image::DSSD, floppy_image::MFM, 2000, 16, 70, 1, 256, {}, 1, {}, 32, 22, 46 }, // FORMAT 4 { floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM, 2000, 9, 80, 2, 512, {}, 1, {}, 146, 22, 81 }, // FORMAT 16 // HP9122C/D, 9123D, 9133D/H/L or 9153A/B { floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM, 2000, 32, 77, 2, 256, {}, 1, {}, 50, 22, 59 }, // FORMAT 0,1,4 diff --git a/src/lib/formats/hpi_dsk.cpp b/src/lib/formats/hpi_dsk.cpp index 84836207222..952ec0a2709 100644 --- a/src/lib/formats/hpi_dsk.cpp +++ b/src/lib/formats/hpi_dsk.cpp @@ -162,9 +162,10 @@ bool hpi_format::load(util::random_read &io, uint32_t form_factor, const std::ve image.set_variant(heads == 2 ? floppy_image::DSDD : floppy_image::SSDD); // Suck in the whole image - std::vector<uint8_t> image_data(size); - size_t actual; - io.read_at(0, image_data.data(), size, actual); + auto const [err, image_data, actual] = read_at(io, 0, size); + if (err || (actual != size)) { + return false; + } // Get interleave factor from image unsigned il = (unsigned)image_data[ IL_OFFSET ] * 256 + image_data[ IL_OFFSET + 1 ]; @@ -209,8 +210,7 @@ bool hpi_format::save(util::random_read_write &io, const std::vector<uint32_t> & while (get_next_sector(bitstream , pos , track_no , head_no , sector_no , sector_data)) { if (track_no == cyl && head_no == head && sector_no < HPI_SECTORS) { unsigned offset_in_image = chs_to_lba(cyl, head, sector_no, heads) * HPI_SECTOR_SIZE; - size_t actual; - io.write_at(offset_in_image, sector_data, HPI_SECTOR_SIZE, actual); + /*auto const [err, actual] =*/ write_at(io, offset_in_image, sector_data, HPI_SECTOR_SIZE); // FIXME: check for errors } } } diff --git a/src/lib/formats/hti_tape.cpp b/src/lib/formats/hti_tape.cpp index f4a5727d591..7d6119860f1 100644 --- a/src/lib/formats/hti_tape.cpp +++ b/src/lib/formats/hti_tape.cpp @@ -11,6 +11,8 @@ #include "ioprocs.h" #include "multibyte.h" +#include <tuple> + static constexpr uint32_t OLD_FILE_MAGIC = 0x5441434f; // Magic value at start of old-format image file: "TACO" static constexpr uint32_t FILE_MAGIC_DELTA = 0x48544930; // Magic value at start of delta-modulation image file: "HTI0" @@ -54,10 +56,12 @@ bool hti_format_t::load_tape(util::random_read &io) return false; } - size_t actual; - uint8_t tmp[ 4 ]; + uint8_t tmp[4]; + auto const [err, actual] = read(io, tmp, 4); + if (err || (4 != actual)) { + return false; + } - io.read(tmp, 4, actual); auto magic = get_u32be(tmp); if (((m_img_format == HTI_DELTA_MOD_16_BITS || m_img_format == HTI_DELTA_MOD_17_BITS) && magic != FILE_MAGIC_DELTA && magic != OLD_FILE_MAGIC) || (m_img_format == HTI_MANCHESTER_MOD && magic != FILE_MAGIC_MANCHESTER)) { @@ -65,7 +69,7 @@ bool hti_format_t::load_tape(util::random_read &io) } for (unsigned i = 0; i < no_of_tracks(); i++) { - tape_track_t& track = m_tracks[ i ]; + tape_track_t &track = m_tracks[i]; if (!load_track(io, track, magic == OLD_FILE_MAGIC)) { clear_tape(); return false; @@ -77,17 +81,16 @@ bool hti_format_t::load_tape(util::random_read &io) void hti_format_t::save_tape(util::random_read_write &io) { - io.seek(0, SEEK_SET); + io.seek(0, SEEK_SET); // FIXME: check for errors - size_t actual; - uint8_t tmp[ 4 ]; + uint8_t tmp[4]; put_u32be(tmp, m_img_format == HTI_MANCHESTER_MOD ? FILE_MAGIC_MANCHESTER : FILE_MAGIC_DELTA); - io.write(tmp, 4, actual); + write(io, tmp, 4); // FIXME: check for errors for (unsigned i = 0; i < no_of_tracks(); i++) { - const tape_track_t& track = m_tracks[ i ]; - tape_pos_t next_pos = (tape_pos_t)-1; + const tape_track_t &track = m_tracks[i]; + tape_pos_t next_pos = tape_pos_t(-1); unsigned n_words = 0; tape_track_t::const_iterator it_start; for (tape_track_t::const_iterator it = track.cbegin(); it != track.cend(); ++it) { @@ -102,20 +105,20 @@ void hti_format_t::save_tape(util::random_read_write &io) dump_sequence(io, it_start, n_words); // End of track put_u32le(tmp, (uint32_t)-1); - io.write(tmp, 4, actual); + write(io, tmp, 4); // FIXME: check for errors } } void hti_format_t::clear_tape() { - for (tape_track_t& track : m_tracks) { + for (tape_track_t &track : m_tracks) { track.clear(); } } hti_format_t::tape_pos_t hti_format_t::word_length(tape_word_t w) const { - unsigned zeros , ones; + unsigned zeros, ones; // pop count of w ones = (w & 0x5555) + ((w >> 1) & 0x5555); @@ -128,7 +131,7 @@ hti_format_t::tape_pos_t hti_format_t::word_length(tape_word_t w) const return zeros * bit_length(false) + ones * bit_length(true); } -hti_format_t::tape_pos_t hti_format_t::farthest_end(const track_iterator_t& it , bool forward) const +hti_format_t::tape_pos_t hti_format_t::farthest_end(const track_iterator_t &it, bool forward) const { if (forward) { return word_end_pos(it); @@ -137,7 +140,7 @@ hti_format_t::tape_pos_t hti_format_t::farthest_end(const track_iterator_t& it , } } -bool hti_format_t::pos_offset(tape_pos_t& pos , bool forward , tape_pos_t offset) +bool hti_format_t::pos_offset(tape_pos_t &pos, bool forward, tape_pos_t offset) { if (offset == 0) { return true; @@ -161,7 +164,7 @@ bool hti_format_t::pos_offset(tape_pos_t& pos , bool forward , tape_pos_t offset } } -hti_format_t::tape_pos_t hti_format_t::next_hole(tape_pos_t pos , bool forward) +hti_format_t::tape_pos_t hti_format_t::next_hole(tape_pos_t pos, bool forward) { if (forward) { for (tape_pos_t hole : tape_holes) { @@ -172,9 +175,9 @@ hti_format_t::tape_pos_t hti_format_t::next_hole(tape_pos_t pos , bool forward) // No more holes: will hit end of tape return NULL_TAPE_POS; } else { - for (int i = (sizeof(tape_holes) / sizeof(tape_holes[ 0 ])) - 1; i >= 0; i--) { - if (tape_holes[ i ] < pos) { - return tape_holes[ i ]; + for (int i = (sizeof(tape_holes) / sizeof(tape_holes[0])) - 1; i >= 0; i--) { + if (tape_holes[i] < pos) { + return tape_holes[i]; } } // No more holes: will hit start of tape @@ -182,16 +185,16 @@ hti_format_t::tape_pos_t hti_format_t::next_hole(tape_pos_t pos , bool forward) } } -void hti_format_t::write_word(unsigned track_no , tape_pos_t start , tape_word_t word , tape_pos_t& length , bool forward) +void hti_format_t::write_word(unsigned track_no, tape_pos_t start, tape_word_t word, tape_pos_t &length, bool forward) { - tape_track_t& track = m_tracks[ track_no ]; + tape_track_t &track = m_tracks[track_no]; track_iterator_t it_low = track.lower_bound(start); - adjust_it(track , it_low , start); + adjust_it(track, it_low, start); length = word_length(word); tape_pos_t end_pos = start + length; track_iterator_t it_high = track.lower_bound(end_pos); - track.erase(it_low , it_high); + track.erase(it_low, it_high); // A 0 word is inserted after the word being written, if space allows. // This is meant to avoid fragmentation of the slack space at the end of a record @@ -203,24 +206,24 @@ void hti_format_t::write_word(unsigned track_no , tape_pos_t start , tape_word_t it_high--; } - track.insert(it_high , std::make_pair(start, word)); + track.insert(it_high, std::make_pair(start, word)); } -void hti_format_t::write_gap(unsigned track_no , tape_pos_t a , tape_pos_t b) +void hti_format_t::write_gap(unsigned track_no, tape_pos_t a, tape_pos_t b) { - ensure_a_lt_b(a , b); - tape_track_t& track = m_tracks[ track_no ]; + ensure_a_lt_b(a, b); + tape_track_t &track = m_tracks[track_no]; track_iterator_t it_low = track.lower_bound(a); - adjust_it(track , it_low , a); + adjust_it(track, it_low, a); track_iterator_t it_high = track.lower_bound(b); track.erase(it_low, it_high); } -bool hti_format_t::just_gap(unsigned track_no , tape_pos_t a , tape_pos_t b) +bool hti_format_t::just_gap(unsigned track_no, tape_pos_t a, tape_pos_t b) { - ensure_a_lt_b(a , b); - tape_track_t& track = m_tracks[ track_no ]; + ensure_a_lt_b(a, b); + tape_track_t &track = m_tracks[track_no]; track_iterator_t it_low = track.lower_bound(a); track_iterator_t it_high = track.lower_bound(b); @@ -229,9 +232,9 @@ bool hti_format_t::just_gap(unsigned track_no , tape_pos_t a , tape_pos_t b) return it_low == it_high; } -bool hti_format_t::next_data(unsigned track_no , tape_pos_t pos , bool forward , bool inclusive , track_iterator_t& it) +bool hti_format_t::next_data(unsigned track_no, tape_pos_t pos, bool forward, bool inclusive, track_iterator_t &it) { - tape_track_t& track = m_tracks[ track_no ]; + tape_track_t &track = m_tracks[track_no]; it = track.lower_bound(pos); if (forward) { if (inclusive) { @@ -251,9 +254,9 @@ bool hti_format_t::next_data(unsigned track_no , tape_pos_t pos , bool forward , } } -hti_format_t::adv_res_t hti_format_t::adv_it(unsigned track_no , bool forward , track_iterator_t& it) +hti_format_t::adv_res_t hti_format_t::adv_it(unsigned track_no, bool forward, track_iterator_t &it) { - tape_track_t& track = m_tracks[ track_no ]; + tape_track_t &track = m_tracks[track_no]; if (forward) { tape_pos_t prev_pos = word_end_pos(it); ++it; @@ -275,7 +278,7 @@ hti_format_t::adv_res_t hti_format_t::adv_it(unsigned track_no , bool forward , } } -bool hti_format_t::sync_with_record(unsigned track_no , track_iterator_t& it , unsigned& bit_idx) +bool hti_format_t::sync_with_record(unsigned track_no, track_iterator_t &it, unsigned &bit_idx) { while ((it->second & (1U << bit_idx)) == 0) { if (bit_idx) { @@ -296,7 +299,7 @@ bool hti_format_t::sync_with_record(unsigned track_no , track_iterator_t& it , u return true; } -hti_format_t::adv_res_t hti_format_t::next_word(unsigned track_no , track_iterator_t& it , unsigned& bit_idx , tape_word_t& word) +hti_format_t::adv_res_t hti_format_t::next_word(unsigned track_no, track_iterator_t &it, unsigned &bit_idx, tape_word_t &word) { if (bit_idx == 15) { auto res = adv_it(track_no, true, it); @@ -319,13 +322,13 @@ hti_format_t::adv_res_t hti_format_t::next_word(unsigned track_no , track_iterat } } -bool hti_format_t::next_gap(unsigned track_no , tape_pos_t& pos , bool forward , tape_pos_t min_gap) +bool hti_format_t::next_gap(unsigned track_no, tape_pos_t &pos, bool forward, tape_pos_t min_gap) { tape_track_t::iterator it; // First align with next data - next_data(track_no , pos , forward , true , it); + next_data(track_no, pos, forward, true, it); // Then scan for 1st gap - tape_track_t& track = m_tracks[ track_no ]; + tape_track_t &track = m_tracks[track_no]; bool done = false; track_iterator_t prev_it; unsigned n_gaps = 1; @@ -346,7 +349,7 @@ bool hti_format_t::next_gap(unsigned track_no , tape_pos_t& pos , bool forward , adv_res_t adv_res; do { prev_it = it; - adv_res = adv_it(track_no , forward , it); + adv_res = adv_it(track_no, forward, it); } while (adv_res == ADV_CONT_DATA); pos = word_end_pos(prev_it); } @@ -366,32 +369,36 @@ bool hti_format_t::next_gap(unsigned track_no , tape_pos_t& pos , bool forward , adv_res_t adv_res; do { prev_it = it; - adv_res = adv_it(track_no , forward , it); + adv_res = adv_it(track_no, forward, it); } while (adv_res == ADV_CONT_DATA); pos = prev_it->first; } } // Set "pos" where minimum gap size is met - pos_offset(pos , forward , min_gap); + pos_offset(pos, forward, min_gap); return n_gaps == 0; } -bool hti_format_t::load_track(util::random_read &io , tape_track_t& track , bool old_format) +bool hti_format_t::load_track(util::random_read &io, tape_track_t &track, bool old_format) { - size_t actual; - uint8_t tmp[ 4 ]; - uint32_t tmp32; tape_pos_t delta_pos = 0; tape_pos_t last_word_end = 0; track.clear(); while (1) { - // Read no. of words to follow - io.read(tmp, 4, actual); + std::error_condition err; + size_t actual; + uint8_t tmp[4]; + uint32_t tmp32; + // Read no. of words to follow + std::tie(err, actual) = read(io, tmp, 4); + if (err || (4 != actual)) { + return false; + } tmp32 = get_u32le(tmp); // Track ends @@ -402,8 +409,10 @@ bool hti_format_t::load_track(util::random_read &io , tape_track_t& track , bool unsigned n_words = tmp32; // Read tape position of block - io.read(tmp, 4, actual); - + std::tie(err, actual) = read(io, tmp, 4); + if (err || (4 != actual)) { + return false; + } tmp32 = get_u32le(tmp); tape_pos_t pos = (tape_pos_t)tmp32 + delta_pos; @@ -414,11 +423,14 @@ bool hti_format_t::load_track(util::random_read &io , tape_track_t& track , bool for (unsigned i = 0; i < n_words; i++) { uint16_t tmp16; - io.read(tmp, 2, actual); + std::tie(err, actual) = read(io, tmp, 2); + if (err || (2 != actual)) { + return false; + } tmp16 = get_u16le(tmp); if (!old_format) { - track.insert(std::make_pair(pos , tmp16)); + track.insert(std::make_pair(pos, tmp16)); pos += word_length(tmp16); } else if (m_img_format == HTI_DELTA_MOD_16_BITS) { // Convert HP9845 & HP85 old format @@ -471,29 +483,28 @@ bool hti_format_t::load_track(util::random_read &io , tape_track_t& track , bool } } -void hti_format_t::dump_sequence(util::random_read_write &io , tape_track_t::const_iterator it_start , unsigned n_words) +void hti_format_t::dump_sequence(util::random_read_write &io, tape_track_t::const_iterator it_start, unsigned n_words) { if (n_words) { - size_t actual; - uint8_t tmp[ 8 ]; - put_u32le(&tmp[ 0 ], n_words); - put_u32le(&tmp[ 4 ], it_start->first); - io.write(tmp, 8, actual); + uint8_t tmp[8]; + put_u32le(&tmp[0], n_words); + put_u32le(&tmp[4], it_start->first); + write(io, tmp, 8); // FIXME: check for errors for (unsigned i = 0; i < n_words; i++) { put_u16le(tmp, it_start->second); - io.write(tmp, 2, actual); + write(io, tmp, 2); // FIXME: check for errors ++it_start; } } } -hti_format_t::tape_pos_t hti_format_t::word_end_pos(const track_iterator_t& it) const +hti_format_t::tape_pos_t hti_format_t::word_end_pos(const track_iterator_t &it) const { return it->first + word_length(it->second); } -void hti_format_t::adjust_it(tape_track_t& track , track_iterator_t& it , tape_pos_t pos) const +void hti_format_t::adjust_it(tape_track_t &track, track_iterator_t &it, tape_pos_t pos) const { if (it != track.begin()) { --it; @@ -503,7 +514,7 @@ void hti_format_t::adjust_it(tape_track_t& track , track_iterator_t& it , tape_p } } -void hti_format_t::ensure_a_lt_b(tape_pos_t& a , tape_pos_t& b) +void hti_format_t::ensure_a_lt_b(tape_pos_t &a, tape_pos_t &b) { if (a > b) { // Ensure A always comes before B diff --git a/src/lib/formats/hti_tape.h b/src/lib/formats/hti_tape.h index 373c971d174..d343133583f 100644 --- a/src/lib/formats/hti_tape.h +++ b/src/lib/formats/hti_tape.h @@ -22,8 +22,6 @@ class hti_format_t { public: - hti_format_t(); - // Tape position, 1 unit = 1 inch / (968 * 1024) typedef int32_t tape_pos_t; @@ -55,7 +53,6 @@ public: // Iterator to access words on tape typedef tape_track_t::iterator track_iterator_t; - // Set image format enum image_format_t { // Delta modulation, 16 bits per word, 2 tracks per cartridge // HP 9845 & HP 85 @@ -68,6 +65,16 @@ public: HTI_MANCHESTER_MOD }; + enum adv_res_t + { + ADV_NO_MORE_DATA, + ADV_CONT_DATA, + ADV_DISCONT_DATA + }; + + hti_format_t(); + + // Set image format void set_image_format(image_format_t fmt) { m_img_format = fmt; } // Return number of tracks @@ -86,55 +93,48 @@ public: // Return physical length of a 16-bit word on tape tape_pos_t word_length(tape_word_t w) const; - tape_pos_t farthest_end(const track_iterator_t& it , bool forward) const; + tape_pos_t farthest_end(const track_iterator_t &it, bool forward) const; - static bool pos_offset(tape_pos_t& pos , bool forward , tape_pos_t offset); + static bool pos_offset(tape_pos_t &pos, bool forward, tape_pos_t offset); // Position of next hole tape will reach in a given direction - static tape_pos_t next_hole(tape_pos_t pos , bool forward); + static tape_pos_t next_hole(tape_pos_t pos, bool forward); // Write a data word on tape - void write_word(unsigned track_no , tape_pos_t start , tape_word_t word , tape_pos_t& length , bool forward = true); + void write_word(unsigned track_no, tape_pos_t start, tape_word_t word, tape_pos_t &length, bool forward = true); // Write a gap on tape - void write_gap(unsigned track_no , tape_pos_t a , tape_pos_t b); + void write_gap(unsigned track_no, tape_pos_t a, tape_pos_t b); // Check that a section of tape has no data (it's just gap) - bool just_gap(unsigned track_no , tape_pos_t a , tape_pos_t b); + bool just_gap(unsigned track_no, tape_pos_t a, tape_pos_t b); // Return position of next data word in a given direction - bool next_data(unsigned track_no , tape_pos_t pos , bool forward , bool inclusive , track_iterator_t& it); - - enum adv_res_t - { - ADV_NO_MORE_DATA, - ADV_CONT_DATA, - ADV_DISCONT_DATA - }; + bool next_data(unsigned track_no, tape_pos_t pos, bool forward, bool inclusive, track_iterator_t &it); // Advance an iterator to next word of data - adv_res_t adv_it(unsigned track_no , bool forward , track_iterator_t& it); + adv_res_t adv_it(unsigned track_no, bool forward, track_iterator_t &it); // Sync with the preamble of a record - bool sync_with_record(unsigned track_no , track_iterator_t& it , unsigned& bit_idx); + bool sync_with_record(unsigned track_no, track_iterator_t &it, unsigned &bit_idx); // Get a data word from record, after syncing - adv_res_t next_word(unsigned track_no , track_iterator_t& it , unsigned& bit_idx , tape_word_t& word); + adv_res_t next_word(unsigned track_no, track_iterator_t &it, unsigned &bit_idx, tape_word_t &word); // Scan for beginning of next gap in a given direction - bool next_gap(unsigned track_no , tape_pos_t& pos , bool forward , tape_pos_t min_gap); + bool next_gap(unsigned track_no, tape_pos_t &pos, bool forward, tape_pos_t min_gap); private: // Content of tape tracks - tape_track_t m_tracks[ 2 ]; + tape_track_t m_tracks[2]; // Image format image_format_t m_img_format; - bool load_track(util::random_read &io , tape_track_t& track , bool old_format); - static void dump_sequence(util::random_read_write &io , tape_track_t::const_iterator it_start , unsigned n_words); + bool load_track(util::random_read &io, tape_track_t &track, bool old_format); + static void dump_sequence(util::random_read_write &io, tape_track_t::const_iterator it_start, unsigned n_words); - tape_pos_t word_end_pos(const track_iterator_t& it) const; - void adjust_it(tape_track_t& track , track_iterator_t& it , tape_pos_t pos) const; - static void ensure_a_lt_b(tape_pos_t& a , tape_pos_t& b); + tape_pos_t word_end_pos(const track_iterator_t &it) const; + void adjust_it(tape_track_t &track, track_iterator_t &it, tape_pos_t pos) const; + static void ensure_a_lt_b(tape_pos_t &a, tape_pos_t &b); }; #endif // MAME_FORMATS_HTI_TAPE_H diff --git a/src/lib/formats/hxchfe_dsk.cpp b/src/lib/formats/hxchfe_dsk.cpp index a8cf09feaf2..da8d660bb33 100644 --- a/src/lib/formats/hxchfe_dsk.cpp +++ b/src/lib/formats/hxchfe_dsk.cpp @@ -106,6 +106,8 @@ #include "osdcore.h" // osd_printf_* +#include <tuple> + #define HFE_FORMAT_HEADER "HXCPICFE" @@ -139,10 +141,11 @@ bool hfe_format::supports_save() const noexcept int hfe_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t header[8]; - - size_t actual; - io.read_at(0, &header, sizeof(header), actual); - if ( memcmp( header, HFE_FORMAT_HEADER, 8 ) ==0) { + auto const [err, actual] = read_at(io, 0, &header, sizeof(header)); + if (err || (sizeof(header) != actual)) { + return 0; + } + if (!memcmp(header, HFE_FORMAT_HEADER, 8)) { return FIFID_SIGN; } return 0; @@ -150,16 +153,15 @@ int hfe_format::identify(util::random_read &io, uint32_t form_factor, const std: bool hfe_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { + std::error_condition err; size_t actual; - uint8_t header[HEADER_LENGTH]; - uint8_t track_table[TRACK_TABLE_LENGTH]; - header_info info; int drivecyl, driveheads; image.get_maximal_geometry(drivecyl, driveheads); // read header - io.read_at(0, header, HEADER_LENGTH, actual); + uint8_t header[HEADER_LENGTH]; + std::tie(err, actual) = read_at(io, 0, header, HEADER_LENGTH); // FIXME: check for errors and premature EOF // get values // Format revision must be 0 @@ -169,6 +171,7 @@ bool hfe_format::load(util::random_read &io, uint32_t form_factor, const std::ve return false; } + header_info info; info.m_cylinders = header[9] & 0xff; info.m_heads = header[10] & 0xff; @@ -194,7 +197,7 @@ bool hfe_format::load(util::random_read &io, uint32_t form_factor, const std::ve return false; } - info.m_track_encoding = (encoding_t)(header[11] & 0xff); + info.m_track_encoding = encoding_t(header[11] & 0xff); if (info.m_track_encoding > EMU_FM_ENCODING) { @@ -231,7 +234,8 @@ bool hfe_format::load(util::random_read &io, uint32_t form_factor, const std::ve // read track lookup table (multiple of 512) int table_offset = get_u16le(&header[18]); - io.read_at(table_offset<<9, track_table, TRACK_TABLE_LENGTH, actual); + uint8_t track_table[TRACK_TABLE_LENGTH]; + std::tie(err, actual) = read_at(io, table_offset<<9, track_table, TRACK_TABLE_LENGTH); // FIXME: check for errors and premature EOF for (int i=0; i < info.m_cylinders; i++) { @@ -244,10 +248,9 @@ bool hfe_format::load(util::random_read &io, uint32_t form_factor, const std::ve for(int cyl=0; cyl < info.m_cylinders; cyl++) { // actual data read - // The HFE format defines an interleave of the two sides per cylinder - // at every 256 bytes + // The HFE format defines an interleave of the two sides per cylinder at every 256 bytes cylinder_buffer.resize(info.m_cyl_length[cyl]); - io.read_at(info.m_cyl_offset[cyl]<<9, &cylinder_buffer[0], info.m_cyl_length[cyl], actual); + std::tie(err, actual) = read_at(io, info.m_cyl_offset[cyl]<<9, &cylinder_buffer[0], info.m_cyl_length[cyl]); // FIXME: check for errors and premature EOF generate_track_from_hfe_bitstream(cyl, 0, samplelength, &cylinder_buffer[0], info.m_cyl_length[cyl], image); if (info.m_heads == 2) diff --git a/src/lib/formats/hxcmfm_dsk.cpp b/src/lib/formats/hxcmfm_dsk.cpp index 0390b55bca0..d9fda047dea 100644 --- a/src/lib/formats/hxcmfm_dsk.cpp +++ b/src/lib/formats/hxcmfm_dsk.cpp @@ -6,6 +6,7 @@ #include "ioprocs.h" #include <cstring> +#include <tuple> #define MFM_FORMAT_HEADER "HXCMFM" @@ -63,10 +64,11 @@ bool mfm_format::supports_save() const noexcept int mfm_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t header[7]; - - size_t actual; - io.read_at(0, &header, sizeof(header), actual); - if ( memcmp( header, MFM_FORMAT_HEADER, 6 ) ==0) { + auto const [err, actual] = read_at(io, 0, &header, sizeof(header)); // FIXME: does this really need to read 7 bytes? only 6 are checked. also check for premature EOF + if (err) { + return 0; + } + if (!memcmp(header, MFM_FORMAT_HEADER, 6)) { return FIFID_SIGN; } return 0; @@ -74,12 +76,13 @@ int mfm_format::identify(util::random_read &io, uint32_t form_factor, const std: bool mfm_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { + std::error_condition err; size_t actual; MFMIMG header; MFMTRACKIMG trackdesc; // read header - io.read_at(0, &header, sizeof(header), actual); + std::tie(err, actual) = read_at(io, 0, &header, sizeof(header)); // FIXME: check for errors and premature EOF int drivecyl, driveheads; image.get_maximal_geometry(drivecyl, driveheads); @@ -91,12 +94,12 @@ bool mfm_format::load(util::random_read &io, uint32_t form_factor, const std::ve for(int side=0; side < header.number_of_side; side++) { if (!skip_odd || track%2 == 0) { // read location of - io.read_at((header.mfmtracklistoffset)+( counter *sizeof(trackdesc)), &trackdesc, sizeof(trackdesc), actual); + std::tie(err, actual) = read_at(io, header.mfmtracklistoffset + (counter * sizeof(trackdesc)), &trackdesc, sizeof(trackdesc)); // FIXME: check for errors and premature EOF trackbuf.resize(trackdesc.mfmtracksize); // actual data read - io.read_at(trackdesc.mfmtrackoffset, &trackbuf[0], trackdesc.mfmtracksize, actual); + std::tie(err, actual) = read_at(io, trackdesc.mfmtrackoffset, &trackbuf[0], trackdesc.mfmtracksize); // FIXME: check for errors and premature EOF if (skip_odd) { generate_track_from_bitstream(track/2, side, &trackbuf[0], trackdesc.mfmtracksize*8, image); @@ -117,7 +120,6 @@ bool mfm_format::load(util::random_read &io, uint32_t form_factor, const std::ve bool mfm_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const { // TODO: HD support - size_t actual; MFMIMG header; int track_count, head_count; image.get_actual_geometry(track_count, head_count); @@ -130,7 +132,7 @@ bool mfm_format::save(util::random_read_write &io, const std::vector<uint32_t> & header.floppyiftype = 4; header.mfmtracklistoffset = sizeof(MFMIMG); - io.write_at(0, &header, sizeof(MFMIMG), actual); + write_at(io, 0, &header, sizeof(MFMIMG)); // FIXME: check for errors int tpos = sizeof(MFMIMG); int dpos = tpos + track_count*head_count*sizeof(MFMTRACKIMG); @@ -149,8 +151,8 @@ bool mfm_format::save(util::random_read_write &io, const std::vector<uint32_t> & trackdesc.mfmtracksize = packed.size(); trackdesc.mfmtrackoffset = dpos; - io.write_at(tpos, &trackdesc, sizeof(MFMTRACKIMG), actual); - io.write_at(dpos, packed.data(), packed.size(), actual); + write_at(io, tpos, &trackdesc, sizeof(MFMTRACKIMG)); // FIXME: check for errors + write_at(io, dpos, packed.data(), packed.size()); // FIXME: check for errors tpos += sizeof(MFMTRACKIMG); dpos += packed.size(); diff --git a/src/lib/formats/ibmxdf_dsk.cpp b/src/lib/formats/ibmxdf_dsk.cpp index 5fb1f3da411..c9704361f65 100644 --- a/src/lib/formats/ibmxdf_dsk.cpp +++ b/src/lib/formats/ibmxdf_dsk.cpp @@ -186,8 +186,8 @@ bool ibmxdf_format::load(util::random_read &io, uint32_t form_factor, const std: const format &f = formats[type]; - for(int track=0; track < f.track_count; track++) - for(int head=0; head < f.head_count; head++) { + for(int track = 0; track < f.track_count; track++) { + for(int head = 0; head < f.head_count; head++) { uint8_t sectdata[23 * 2 * 512]; // XXX magic desc_s sectors[40]; floppy_image_format_t::desc_e *desc; @@ -213,10 +213,10 @@ bool ibmxdf_format::load(util::random_read &io, uint32_t form_factor, const std: build_sector_description(tf, sectdata, sectors, track, head); int const track_size = compute_track_size(f) * 2; // read both sides at once - size_t actual; - io.read_at(get_image_offset(f, head, track), sectdata, track_size, actual); + /*auto const [err, actual] =*/ read_at(io, get_image_offset(f, head, track), sectdata, track_size); // FIXME: check for errors and premature EOF generate_track(desc, track, head, sectors, tf.sector_count, total_size, image); } + } image.set_variant(f.variant); diff --git a/src/lib/formats/idpart_dsk.cpp b/src/lib/formats/idpart_dsk.cpp new file mode 100644 index 00000000000..9d4c641fa41 --- /dev/null +++ b/src/lib/formats/idpart_dsk.cpp @@ -0,0 +1,53 @@ +// license:BSD-3-Clause +// copyright-holders:Miodrag Milanovic +/********************************************************************* + + formats/idpart_dsk.cpp + + Iskra Delta Partner format + +*********************************************************************/ + +#include "formats/idpart_dsk.h" + +idpart_format::idpart_format() : upd765_format(formats) +{ +} + +const char *idpart_format::name() const noexcept +{ + return "idpart"; +} + +const char *idpart_format::description() const noexcept +{ + return "Iskra Delta Partner disk image"; +} + +const char *idpart_format::extensions() const noexcept +{ + return "img"; +} + +// Unverified gap sizes. +const idpart_format::format idpart_format::formats[] = { + { + floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM, + 2000, + 18, 73, 2, + 256, {}, + 1, {}, + 80, 50, 22 + }, + { + floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM, + 2000, + 18, 77, 2, + 256, {}, + 1, {}, + 80, 50, 22 + }, + {} +}; + +const idpart_format FLOPPY_IDPART_FORMAT; diff --git a/src/lib/formats/idpart_dsk.h b/src/lib/formats/idpart_dsk.h new file mode 100644 index 00000000000..6f5977a5cfa --- /dev/null +++ b/src/lib/formats/idpart_dsk.h @@ -0,0 +1,32 @@ +// license:BSD-3-Clause +// copyright-holders:Miodrag Milanovic +/********************************************************************* + + formats/idpart_dsk.h + + Iskra Delta Partner format + +*********************************************************************/ +#ifndef MAME_FORMATS_IDPART_DSK_H +#define MAME_FORMATS_IDPART_DSK_H + +#pragma once + +#include "upd765_dsk.h" + +class idpart_format : public upd765_format +{ +public: + idpart_format(); + + virtual const char *name() const noexcept override; + virtual const char *description() const noexcept override; + virtual const char *extensions() const noexcept override; + +private: + static const format formats[]; +}; + +extern const idpart_format FLOPPY_IDPART_FORMAT; + +#endif // MAME_FORMATS_IDPART_DSK_H diff --git a/src/lib/formats/imd_dsk.cpp b/src/lib/formats/imd_dsk.cpp index fde013f3113..907440dc922 100644 --- a/src/lib/formats/imd_dsk.cpp +++ b/src/lib/formats/imd_dsk.cpp @@ -425,9 +425,10 @@ void imd_format::fixnum(char *start, char *end) const int imd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { char h[4]; + auto const [err, actual] = read_at(io, 0, h, 4); + if(err || (4 != actual)) + return 0; - size_t actual; - io.read_at(0, h, 4, actual); if(!memcmp(h, "IMD ", 4)) return FIFID_SIGN; @@ -451,9 +452,9 @@ bool imd_format::load(util::random_read &io, uint32_t form_factor, const std::ve uint64_t size; if(io.length(size)) return false; - std::vector<uint8_t> img(size); - size_t actual; - io.read_at(0, &img[0], size, actual); + auto const [err, img, actual] = read_at(io, 0, size); + if(err || (actual != size)) + return false; uint64_t pos, savepos; for(pos=0; pos < size && img[pos] != 0x1a; pos++) { } diff --git a/src/lib/formats/img_dsk.cpp b/src/lib/formats/img_dsk.cpp index fb6f1a3bfb3..66e097c281e 100644 --- a/src/lib/formats/img_dsk.cpp +++ b/src/lib/formats/img_dsk.cpp @@ -72,9 +72,10 @@ bool img_format::load(util::random_read &io, uint32_t form_factor, const std::ve image.set_variant(is_dd ? floppy_image::SSDD : floppy_image::SSSD); // Suck in the whole image - std::vector<uint8_t> image_data(size); - size_t actual; - io.read_at(0, image_data.data(), size, actual); + auto const [err, image_data, actual] = read_at(io, 0, size); + if (err || (actual != size)) { + return false; + } for (unsigned cyl = 0; cyl < TRACKS; cyl++) { if (is_dd) { @@ -134,7 +135,7 @@ bool img_format::load(util::random_read &io, uint32_t form_factor, const std::ve sects[ sector ].sector = real_sector; sects[ sector ].bad_crc = false; sects[ sector ].deleted = false; - sects[ sector ].data = image_data.data() + offset_in_image; + sects[ sector ].data = &image_data[offset_in_image]; } build_pc_track_fm(cyl, 0, image, 83333, SECTORS_FM, sects, 33, 46, 32, 11); } @@ -155,8 +156,7 @@ bool img_format::save(util::random_read_write &io, const std::vector<uint32_t> & while (get_next_sector(bitstream , pos , track_no , sector_no , sector_data)) { if (track_no == cyl && sector_no >= 1 && sector_no <= SECTORS) { unsigned offset_in_image = (cyl * SECTORS + sector_no - 1) * SECTOR_SIZE; - size_t actual; - io.write_at(offset_in_image, sector_data, SECTOR_SIZE, actual); + /*auto const [err, actual] =*/ write_at(io, offset_in_image, sector_data, SECTOR_SIZE); // FIXME: check for errors } } } else { @@ -164,8 +164,7 @@ bool img_format::save(util::random_read_write &io, const std::vector<uint32_t> & auto sects = extract_sectors_from_bitstream_fm_pc(bitstream); for (unsigned s = 1; s <= SECTORS_FM; s++) { unsigned offset_in_image = (cyl * SECTORS_FM + s - 1) * SECTOR_SIZE; - size_t actual; - io.write_at(offset_in_image, sects[ s ].data(), SECTOR_SIZE, actual); + /*auto const [err, actual] =*/ write_at(io, offset_in_image, sects[ s ].data(), SECTOR_SIZE); // FIXME: check for errors and premature EOF } } } diff --git a/src/lib/formats/ipf_dsk.cpp b/src/lib/formats/ipf_dsk.cpp index 072dd96c99e..b47eca030f2 100644 --- a/src/lib/formats/ipf_dsk.cpp +++ b/src/lib/formats/ipf_dsk.cpp @@ -8,6 +8,72 @@ #include <cstring> +struct ipf_format::ipf_decode { + struct track_info { + uint32_t cylinder = 0, head = 0, type = 0; + uint32_t sigtype = 0, process = 0, reserved[3] = { 0, 0, 0 }; + uint32_t size_bytes = 0, size_cells = 0; + uint32_t index_bytes = 0, index_cells = 0; + uint32_t datasize_cells = 0, gapsize_cells = 0; + uint32_t block_count = 0, weak_bits = 0; + + uint32_t data_size_bits = 0; + + bool info_set = false; + + const uint8_t *data = nullptr; + uint32_t data_size = 0; + }; + + std::vector<track_info> tinfos; + uint32_t tcount = 0; + + uint32_t type = 0, release = 0, revision = 0; + uint32_t encoder_type = 0, encoder_revision = 0, origin = 0; + uint32_t min_cylinder = 0, max_cylinder = 0, min_head = 0, max_head = 0; + uint32_t credit_day = 0, credit_time = 0; + uint32_t platform[4] = {}, extra[5] = {}; + + uint32_t crc32r(const uint8_t *data, uint32_t size); + + bool parse_info(const uint8_t *info); + bool parse_imge(const uint8_t *imge); + bool parse_data(const uint8_t *data, uint32_t &pos, uint32_t max_extra_size); + + bool scan_one_tag(uint8_t *data, size_t size, uint32_t &pos, uint8_t *&tag, uint32_t &tsize); + bool scan_all_tags(uint8_t *data, size_t size); + static uint32_t rb(const uint8_t *&p, int count); + + track_info *get_index(uint32_t idx); + + void track_write_raw(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t cells, bool &context); + void track_write_mfm(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t start_offset, uint32_t patlen, uint32_t cells, bool &context); + void track_write_weak(std::vector<uint32_t>::iterator &tpos, uint32_t cells); + bool generate_block_data(const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, std::vector<uint32_t>::iterator tlimit, bool &context); + + bool gap_description_to_reserved_size(const uint8_t *&data, const uint8_t *dlimit, uint32_t &res_size); + bool generate_gap_from_description(const uint8_t *&data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, uint32_t size, bool pre, bool &context); + bool generate_block_gap_0(uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, std::vector<uint32_t>::iterator &tpos, bool &context); + bool generate_block_gap_1(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context); + bool generate_block_gap_2(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context); + bool generate_block_gap_3(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context); + bool generate_block_gap(uint32_t gap_type, uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, bool &context); + + bool generate_block(const track_info &t, uint32_t idx, uint32_t ipos, std::vector<uint32_t> &track, uint32_t &pos, uint32_t &dpos, uint32_t &gpos, uint32_t &spos, bool &context); + uint32_t block_compute_real_size(const track_info &t); + + void timing_set(std::vector<uint32_t> &track, uint32_t start, uint32_t end, uint32_t time); + bool generate_timings(const track_info &t, std::vector<uint32_t> &track, const std::vector<uint32_t> &data_pos, const std::vector<uint32_t> &gap_pos); + + void rotate(std::vector<uint32_t> &track, uint32_t offset, uint32_t size); + void mark_track_splice(std::vector<uint32_t> &track, uint32_t offset, uint32_t size); + bool generate_track(track_info &t, floppy_image &image); + bool generate_tracks(floppy_image &image); + + bool parse(uint8_t *data, size_t size, floppy_image &image); +}; + + const ipf_format FLOPPY_IPF_FORMAT; const char *ipf_format::name() const noexcept @@ -34,8 +100,9 @@ int ipf_format::identify(util::random_read &io, uint32_t form_factor, const std: { static const uint8_t refh[12] = { 0x43, 0x41, 0x50, 0x53, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0xd5, 0x73, 0xba }; uint8_t h[12]; - size_t actual; - io.read_at(0, h, 12, actual); + auto const [err, actual] = read_at(io, 0, h, 12); + if(err || (12 != actual)) + return 0; if(!memcmp(h, refh, 12)) return FIFID_SIGN; @@ -46,13 +113,13 @@ int ipf_format::identify(util::random_read &io, uint32_t form_factor, const std: bool ipf_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { uint64_t size; - if (io.length(size)) + if(io.length(size)) + return false; + auto const [err, data, actual] = read_at(io, 0, size); + if(err || (actual != size)) return false; - std::vector<uint8_t> data(size); - size_t actual; - io.read_at(0, &data[0], size, actual); ipf_decode dec; - return dec.parse(data, image); + return dec.parse(data.get(), size, image); } @@ -79,12 +146,12 @@ uint32_t ipf_format::ipf_decode::crc32r(const uint8_t *data, uint32_t size) return ~crc; } -bool ipf_format::ipf_decode::parse(std::vector<uint8_t> &data, floppy_image &image) +bool ipf_format::ipf_decode::parse(uint8_t *data, size_t size, floppy_image &image) { image.set_variant(floppy_image::DSDD); // Not handling anything else yet tcount = 84*2+1; // Usual max tinfos.resize(tcount); - bool res = scan_all_tags(data); + bool res = scan_all_tags(data, size); if(res) res = generate_tracks(image); tinfos.clear(); @@ -177,13 +244,13 @@ bool ipf_format::ipf_decode::parse_data(const uint8_t *data, uint32_t &pos, uint return true; } -bool ipf_format::ipf_decode::scan_one_tag(std::vector<uint8_t> &data, uint32_t &pos, uint8_t *&tag, uint32_t &tsize) +bool ipf_format::ipf_decode::scan_one_tag(uint8_t *data, size_t size, uint32_t &pos, uint8_t *&tag, uint32_t &tsize) { - if(data.size()-pos < 12) + if(size-pos < 12) return false; tag = &data[pos]; tsize = get_u32be(tag+4); - if(data.size()-pos < tsize) + if(size-pos < tsize) return false; uint32_t crc = get_u32be(tag+8); tag[8] = tag[9] = tag[10] = tag[11] = 0; @@ -193,15 +260,14 @@ bool ipf_format::ipf_decode::scan_one_tag(std::vector<uint8_t> &data, uint32_t & return true; } -bool ipf_format::ipf_decode::scan_all_tags(std::vector<uint8_t> &data) +bool ipf_format::ipf_decode::scan_all_tags(uint8_t *data, size_t size) { uint32_t pos = 0; - uint32_t size = data.size(); while(pos != size) { uint8_t *tag; uint32_t tsize; - if(!scan_one_tag(data, pos, tag, tsize)) + if(!scan_one_tag(data, size, pos, tag, tsize)) return false; switch(get_u32be(tag)) { diff --git a/src/lib/formats/ipf_dsk.h b/src/lib/formats/ipf_dsk.h index 9e63b2bff38..a1c36f02f8d 100644 --- a/src/lib/formats/ipf_dsk.h +++ b/src/lib/formats/ipf_dsk.h @@ -21,71 +21,7 @@ public: virtual bool supports_save() const noexcept override; private: - struct ipf_decode { - struct track_info { - uint32_t cylinder = 0, head = 0, type = 0; - uint32_t sigtype = 0, process = 0, reserved[3] = { 0, 0, 0 }; - uint32_t size_bytes = 0, size_cells = 0; - uint32_t index_bytes = 0, index_cells = 0; - uint32_t datasize_cells = 0, gapsize_cells = 0; - uint32_t block_count = 0, weak_bits = 0; - - uint32_t data_size_bits = 0; - - bool info_set = false; - - const uint8_t *data = nullptr; - uint32_t data_size = 0; - }; - - std::vector<track_info> tinfos; - uint32_t tcount = 0; - - uint32_t type = 0, release = 0, revision = 0; - uint32_t encoder_type = 0, encoder_revision = 0, origin = 0; - uint32_t min_cylinder = 0, max_cylinder = 0, min_head = 0, max_head = 0; - uint32_t credit_day = 0, credit_time = 0; - uint32_t platform[4] = {}, extra[5] = {}; - - uint32_t crc32r(const uint8_t *data, uint32_t size); - - bool parse_info(const uint8_t *info); - bool parse_imge(const uint8_t *imge); - bool parse_data(const uint8_t *data, uint32_t &pos, uint32_t max_extra_size); - - bool scan_one_tag(std::vector<uint8_t> &data, uint32_t &pos, uint8_t *&tag, uint32_t &tsize); - bool scan_all_tags(std::vector<uint8_t> &data); - static uint32_t r32(const uint8_t *p); - static uint32_t rb(const uint8_t *&p, int count); - - track_info *get_index(uint32_t idx); - - void track_write_raw(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t cells, bool &context); - void track_write_mfm(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t start_offset, uint32_t patlen, uint32_t cells, bool &context); - void track_write_weak(std::vector<uint32_t>::iterator &tpos, uint32_t cells); - bool generate_block_data(const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, std::vector<uint32_t>::iterator tlimit, bool &context); - - bool gap_description_to_reserved_size(const uint8_t *&data, const uint8_t *dlimit, uint32_t &res_size); - bool generate_gap_from_description(const uint8_t *&data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, uint32_t size, bool pre, bool &context); - bool generate_block_gap_0(uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, std::vector<uint32_t>::iterator &tpos, bool &context); - bool generate_block_gap_1(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context); - bool generate_block_gap_2(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context); - bool generate_block_gap_3(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context); - bool generate_block_gap(uint32_t gap_type, uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, bool &context); - - bool generate_block(const track_info &t, uint32_t idx, uint32_t ipos, std::vector<uint32_t> &track, uint32_t &pos, uint32_t &dpos, uint32_t &gpos, uint32_t &spos, bool &context); - uint32_t block_compute_real_size(const track_info &t); - - void timing_set(std::vector<uint32_t> &track, uint32_t start, uint32_t end, uint32_t time); - bool generate_timings(const track_info &t, std::vector<uint32_t> &track, const std::vector<uint32_t> &data_pos, const std::vector<uint32_t> &gap_pos); - - void rotate(std::vector<uint32_t> &track, uint32_t offset, uint32_t size); - void mark_track_splice(std::vector<uint32_t> &track, uint32_t offset, uint32_t size); - bool generate_track(track_info &t, floppy_image &image); - bool generate_tracks(floppy_image &image); - - bool parse(std::vector<uint8_t> &data, floppy_image &image); - }; + struct ipf_decode; }; extern const ipf_format FLOPPY_IPF_FORMAT; diff --git a/src/lib/formats/jfd_dsk.cpp b/src/lib/formats/jfd_dsk.cpp index 813c7192ca9..46e933cedf1 100644 --- a/src/lib/formats/jfd_dsk.cpp +++ b/src/lib/formats/jfd_dsk.cpp @@ -197,8 +197,9 @@ int jfd_format::identify(util::random_read &io, uint32_t form_factor, const std: return 0; std::vector<uint8_t> img(size); - size_t actual; - io.read_at(0, &img[0], size, actual); + auto const [ioerr, actual] = read_at(io, 0, &img[0], size); + if (ioerr || (actual != size)) + return 0; int err; std::vector<uint8_t> gz_ptr(4); @@ -222,7 +223,7 @@ int jfd_format::identify(util::random_read &io, uint32_t form_factor, const std: err = inflateEnd(&d_stream); if (err != Z_OK) return 0; - img = gz_ptr; + img = std::move(gz_ptr); } if (!memcmp(&img[0], JFD_HEADER, sizeof(JFD_HEADER))) { @@ -239,8 +240,9 @@ bool jfd_format::load(util::random_read &io, uint32_t form_factor, const std::ve return false; std::vector<uint8_t> img(size); - size_t actual; - io.read_at(0, &img[0], size, actual); + auto const [ioerr, actual] = read_at(io, 0, &img[0], size); + if (ioerr || (actual != size)) + return false; int err; std::vector<uint8_t> gz_ptr; @@ -274,7 +276,7 @@ bool jfd_format::load(util::random_read &io, uint32_t form_factor, const std::ve return false; } size = inflate_size; - img = gz_ptr; + img = std::move(gz_ptr); } osd_printf_verbose("jfd_dsk: loading %s\n", &img[48]); diff --git a/src/lib/formats/juku_dsk.cpp b/src/lib/formats/juku_dsk.cpp index 05fdc9b8484..46d448a2c61 100644 --- a/src/lib/formats/juku_dsk.cpp +++ b/src/lib/formats/juku_dsk.cpp @@ -1,8 +1,8 @@ // license: BSD-3-Clause -// copyright-holders: Dirk Best +// copyright-holders: Dirk Best, Märt Põder /*************************************************************************** - Juku E5101 + Juku E5101/E5104 Disk image format @@ -31,11 +31,11 @@ const char *juku_format::extensions() const noexcept const juku_format::format juku_format::formats[] = { - { // 800k 5 1/4 inch double density single sided - gaps unverified (CP/M) - floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM, + { // 386k 5.25" double density single sided - gaps unverified (CP/M) + floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM, 2000, 10, 80, 1, 512, {}, 1, {}, 32, 22, 35 }, - { // 800k 5 1/4 inch double density double sided - gaps unverified (CP/M) + { // 786k 5.25" double density "outout" double sided - gaps unverified (CP/M) floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM, 2000, 10, 80, 2, 512, {}, 1, {}, 32, 22, 35 }, diff --git a/src/lib/formats/juku_dsk.h b/src/lib/formats/juku_dsk.h index 414c351ac76..c78363ece14 100644 --- a/src/lib/formats/juku_dsk.h +++ b/src/lib/formats/juku_dsk.h @@ -1,8 +1,8 @@ // license: BSD-3-Clause -// copyright-holders: Dirk Best +// copyright-holders: Dirk Best, Märt Põder /*************************************************************************** - Juku E5101 + Juku E5101/E5104 Disk image format diff --git a/src/lib/formats/jvc_dsk.cpp b/src/lib/formats/jvc_dsk.cpp index ce0083a9deb..fcc5d97a5b5 100644 --- a/src/lib/formats/jvc_dsk.cpp +++ b/src/lib/formats/jvc_dsk.cpp @@ -141,11 +141,14 @@ bool jvc_format::parse_header(util::random_read &io, int &header_size, int &trac uint8_t header[5]; // if we know that this is a header of a bad size, we can fail immediately; otherwise read the header - size_t actual; - if (header_size >= sizeof(header)) + if (header_size >= sizeof(header)) // TODO: wouldn't this make more sense with > than >=? The first case in the following switch statement is unreachable as-is. return false; if (header_size > 0) - io.read_at(0, header, header_size, actual); + { + auto const [err, actual] = read_at(io, 0, header, header_size); + if (err || (actual != header_size)) + return false; + } // default values heads = 1; @@ -241,8 +244,7 @@ bool jvc_format::load(util::random_read &io, uint32_t form_factor, const std::ve sectors[interleave[i]].bad_crc = false; sectors[interleave[i]].data = §or_data[sector_offset]; - size_t actual; - io.read_at(file_offset, sectors[interleave[i]].data, sector_size, actual); + /*auto const [err, actual] =*/ read_at(io, file_offset, sectors[interleave[i]].data, sector_size); // FIXME: check for errors and premature EOF sector_offset += sector_size; file_offset += sector_size; @@ -268,8 +270,7 @@ bool jvc_format::save(util::random_read_write &io, const std::vector<uint32_t> & uint8_t header[2]; header[0] = 18; header[1] = 2; - size_t actual; - io.write_at(file_offset, header, sizeof(header), actual); + /*auto const [err, actual] =*/ write_at(io, file_offset, header, sizeof(header)); // FIXME: check for errors file_offset += sizeof(header); } @@ -289,8 +290,7 @@ bool jvc_format::save(util::random_read_write &io, const std::vector<uint32_t> & return false; } - size_t actual; - io.write_at(file_offset, sectors[1 + i].data(), 256, actual); + /*auto const [err, actual] =*/ write_at(io, file_offset, sectors[1 + i].data(), 256); // FIXME: check for errors file_offset += 256; } } diff --git a/src/lib/formats/kc_cas.cpp b/src/lib/formats/kc_cas.cpp index 035d18fb4cd..34c52786b9c 100644 --- a/src/lib/formats/kc_cas.cpp +++ b/src/lib/formats/kc_cas.cpp @@ -232,7 +232,7 @@ static int kc_handle_sss(int16_t *buffer, const uint8_t *casdata) /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int kc_kcc_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int kc_kcc_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { return kc_handle_kcc(buffer, bytes); } @@ -243,7 +243,7 @@ static int kc_kcc_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) ********************************************************************/ static int kc_kcc_to_wav_size(const uint8_t *casdata, int caslen) { - kc_image_size = caslen ; + kc_image_size = caslen; return kc_handle_kcc( nullptr, casdata ); } @@ -284,7 +284,7 @@ static const cassette_image::Format kc_kcc_format = /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int kc_tap_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int kc_tap_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { return kc_handle_tap(buffer, bytes); } @@ -295,7 +295,7 @@ static int kc_tap_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) ********************************************************************/ static int kc_tap_to_wav_size(const uint8_t *casdata, int caslen) { - kc_image_size = caslen ; + kc_image_size = caslen; return kc_handle_tap( nullptr, casdata ); } @@ -336,7 +336,7 @@ static const cassette_image::Format kc_tap_format = /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int kc_sss_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int kc_sss_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { return kc_handle_sss(buffer, bytes); } @@ -347,7 +347,7 @@ static int kc_sss_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) ********************************************************************/ static int kc_sss_to_wav_size(const uint8_t *casdata, int caslen) { - kc_image_size = caslen ; + kc_image_size = caslen; return kc_handle_sss( nullptr, casdata ); } diff --git a/src/lib/formats/kim1_cas.cpp b/src/lib/formats/kim1_cas.cpp index 933e8cc3818..de2ee022632 100644 --- a/src/lib/formats/kim1_cas.cpp +++ b/src/lib/formats/kim1_cas.cpp @@ -145,7 +145,7 @@ static int kim1_handle_kim(int16_t *buffer, const uint8_t *casdata) /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int kim1_kim_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int kim1_kim_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { return kim1_handle_kim( buffer, bytes ); } diff --git a/src/lib/formats/lviv_lvt.cpp b/src/lib/formats/lviv_lvt.cpp index c2ffe37d9c9..e37508ab529 100644 --- a/src/lib/formats/lviv_lvt.cpp +++ b/src/lib/formats/lviv_lvt.cpp @@ -71,7 +71,7 @@ static int lviv_cassette_calculate_size_in_samples(const uint8_t *bytes, int len /*************************************************************************************/ -static int lviv_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int lviv_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { int16_t * p = buffer; diff --git a/src/lib/formats/lw30_dsk.cpp b/src/lib/formats/lw30_dsk.cpp index dd5eef5da5f..dd1536755d5 100644 --- a/src/lib/formats/lw30_dsk.cpp +++ b/src/lib/formats/lw30_dsk.cpp @@ -138,9 +138,11 @@ static constexpr int raw_track_size = 2/*0xaa*/ + 48/*0xaa*/ + SECTORS_PER_TRACK int lw30_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint64_t size = 0; - io.length(size); + if(io.length(size)) + return 0; + if(size == TRACKS_PER_DISK * SECTORS_PER_TRACK * SECTOR_SIZE) - return 50; // identified by size + return FIFID_SIZE; // identified by size return 0; } @@ -150,9 +152,8 @@ bool lw30_format::load(util::random_read &io, uint32_t form_factor, const std::v uint8_t trackdata[SECTORS_PER_TRACK * SECTOR_SIZE], rawdata[CELLS_PER_REV / 8]; memset(rawdata, 0xaa, sizeof(rawdata)); for(int track = 0; track < TRACKS_PER_DISK; track++) { - size_t actual{}; - io.read_at(track * SECTORS_PER_TRACK * SECTOR_SIZE, trackdata, SECTORS_PER_TRACK * SECTOR_SIZE, actual); - if(actual != SECTORS_PER_TRACK * SECTOR_SIZE) + auto const [err, actual] = read_at(io, track * SECTORS_PER_TRACK * SECTOR_SIZE, trackdata, SECTORS_PER_TRACK * SECTOR_SIZE); + if(err || (actual != SECTORS_PER_TRACK * SECTOR_SIZE)) return false; size_t i = 0; for(int x = 0; x < 2 + 48; x++) diff --git a/src/lib/formats/m20_dsk.cpp b/src/lib/formats/m20_dsk.cpp index 10c6cf51cf1..02905ecfa0d 100644 --- a/src/lib/formats/m20_dsk.cpp +++ b/src/lib/formats/m20_dsk.cpp @@ -62,8 +62,7 @@ bool m20_format::load(util::random_read &io, uint32_t form_factor, const std::ve bool mfm = track || head; desc_pc_sector sects[16]; uint8_t sectdata[16*256]; - size_t actual; - io.read_at(16*256*(track*2+head), sectdata, 16*256, actual); + /*auto const [err, actual] =*/ read_at(io, 16*256*(track*2+head), sectdata, 16*256); // FIXME: check for errors and premature EOF for (int i = 0; i < 16; i++) { int j = i/2 + (i & 1 ? 0 : 8); sects[i].track = track; @@ -92,17 +91,16 @@ bool m20_format::save(util::random_read_write &io, const std::vector<uint32_t> & int track_count, head_count; track_count = 35; head_count = 2; //FIXME: use image.get_actual_geometry(track_count, head_count) instead - // initial fm track + // initial FM track auto bitstream = generate_bitstream_from_track(0, 0, 4000, image); auto sectors = extract_sectors_from_bitstream_fm_pc(bitstream); for (int i = 0; i < 16; i++) { - size_t actual; - io.write_at(file_offset, sectors[i + 1].data(), 128, actual); + /*auto const [err, actual] =*/ write_at(io, file_offset, sectors[i + 1].data(), 128); // FIXME: check for errors file_offset += 256; //128; } - // rest are mfm tracks + // rest are MFM tracks for (int track = 0; track < track_count; track++) { for (int head = 0; head < head_count; head++) { // skip track 0, head 0 @@ -115,8 +113,7 @@ bool m20_format::save(util::random_read_write &io, const std::vector<uint32_t> & sectors = extract_sectors_from_bitstream_mfm_pc(bitstream); for (int i = 0; i < 16; i++) { - size_t actual; - io.write_at(file_offset, sectors[i + 1].data(), 256, actual); + /*auto const [err, actual] =*/ write_at(io, file_offset, sectors[i + 1].data(), 256); // FIXME: check for errors file_offset += 256; } } diff --git a/src/lib/formats/mbee_cas.cpp b/src/lib/formats/mbee_cas.cpp index cd11f7bb041..f8c42602b2b 100644 --- a/src/lib/formats/mbee_cas.cpp +++ b/src/lib/formats/mbee_cas.cpp @@ -209,7 +209,7 @@ static int mbee_handle_tap(int16_t *buffer, const uint8_t *bytes) Generate samples for the tape image ********************************************************************/ -static int mbee_tap_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int mbee_tap_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { return mbee_handle_tap(buffer, bytes); } diff --git a/src/lib/formats/mdos_dsk.cpp b/src/lib/formats/mdos_dsk.cpp index 765ce1bc63c..a14df309db4 100644 --- a/src/lib/formats/mdos_dsk.cpp +++ b/src/lib/formats/mdos_dsk.cpp @@ -55,6 +55,8 @@ #include "ioprocs.h" #include "multibyte.h" +#include <tuple> + mdos_format::mdos_format() : wd177x_format(formats) { @@ -77,10 +79,10 @@ const char *mdos_format::extensions() const noexcept int mdos_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { - int type = find_size(io, form_factor, variants); + int const type = find_size(io, form_factor, variants); if (type != -1) - return FIFID_SIZE; + return FIFID_SIZE | FIFID_STRUCT; return 0; } @@ -115,18 +117,22 @@ int mdos_format::parse_date_field(const uint8_t *str) int mdos_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { + std::error_condition err; size_t actual; + uint64_t size; if (io.length(size)) return -1; + // Look at the disk ID sector. disk_id_sector info; - // Look at the disk id sector. - io.read_at(0, &info, sizeof(struct disk_id_sector), actual); + std::tie(err, actual) = read_at(io, 0, &info, sizeof(disk_id_sector)); + if (err || (sizeof(disk_id_sector) != actual)) + return -1; LOG_FORMATS("MDOS floppy dsk: size %d bytes, %d total sectors, %d remaining bytes, expected form factor %x\n", (uint32_t)size, (uint32_t)size / 128, (uint32_t)size % 128, form_factor); - // The 'unused' area is not necessarily zero filled and is ignoded + // The 'unused' area is not necessarily zero filled and is ignored // in the identification of a MDOS format image. // Expect an ASCII id, version, revision, and 'user name' strings. @@ -146,9 +152,9 @@ int mdos_format::find_size(util::random_read &io, uint32_t form_factor, const st return -1; // The date should be the numeric day, month and year. - int month = parse_date_field(info.date); - int day = parse_date_field(info.date + 2); - int year = parse_date_field(info.date + 4); + int const month = parse_date_field(info.date); + int const day = parse_date_field(info.date + 2); + int const year = parse_date_field(info.date + 4); LOG_FORMATS(" day %d, month %d, year %d\n", day, month, year); if (day < 1 || day > 32 || month < 1 || month > 12 || year < 0) @@ -182,8 +188,12 @@ int mdos_format::find_size(util::random_read &io, uint32_t form_factor, const st // the extent of the disk are free or available. uint8_t cluster_allocation[128], cluster_available[128]; - io.read_at(1 * 128, &cluster_allocation, sizeof(cluster_allocation), actual); - io.read_at(2 * 128, &cluster_available, sizeof(cluster_available), actual); + std::tie(err, actual) = read_at(io, 1 * 128, &cluster_allocation, sizeof(cluster_allocation)); + if (err || (sizeof(cluster_allocation) != actual)) + return -1; + std::tie(err, actual) = read_at(io, 2 * 128, &cluster_available, sizeof(cluster_available)); + if (err || (sizeof(cluster_available) != actual)) + return -1; for (int cluster = 0; cluster < sizeof(cluster_allocation) * 8; cluster++) { if (cluster * 4 * 128 + 4 * 128 > size) { @@ -202,7 +212,7 @@ int mdos_format::find_size(util::random_read &io, uint32_t form_factor, const st } } - for (int i=0; formats[i].form_factor; i++) { + for (int i = 0; formats[i].form_factor; i++) { const format &f = formats[i]; LOG_FORMATS(" checking format %d with form factor %02x, %d sectors, %d heads\n", i, f.form_factor, f.sector_count, f.head_count); diff --git a/src/lib/formats/mfi_dsk.cpp b/src/lib/formats/mfi_dsk.cpp index e6700f8f18f..eb1e632ec03 100644 --- a/src/lib/formats/mfi_dsk.cpp +++ b/src/lib/formats/mfi_dsk.cpp @@ -8,6 +8,7 @@ #include <cstring> #include <functional> +#include <tuple> /* @@ -41,8 +42,10 @@ - 2, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing - 3, MG_E -> End of zone - Tracks data is aligned so that the index pulse is at the start, - whether the disk is hard-sectored or not. + Tracks data is aligned so that the index pulse is at the start for soft- + sectored disks. For hard-sectored disks, the sector hole for the first + sector is at the start and the index hole is half a sector from the end + of the track. The position is the angular position in units of 1/200,000,000th of a turn. A size in such units, not coincidentally at all, is also @@ -58,7 +61,9 @@ if you try to rewrite a physical disk with the data. Some preservation formats encode that information, it is guessed for others. The write track function of fdcs should set it. The - representation is the angular position relative to the index. + representation is the angular position relative to the index, for + soft-sectored disks, and the first sector hole for hard-sectored + disks. The media type is divided in two parts. The first half indicate the physical form factor, i.e. all medias with that @@ -99,38 +104,46 @@ bool mfi_format::supports_save() const noexcept int mfi_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { header h; + auto const [err, actual] = read_at(io, 0, &h, sizeof(header)); + if (err || (sizeof(header) != actual)) + return 0; - size_t actual; - io.read_at(0, &h, sizeof(header), actual); - if((memcmp( h.sign, sign, 16) == 0 || memcmp( h.sign, sign_old, 16) == 0) && + if((!memcmp(h.sign, sign, 16) || !memcmp(h.sign, sign_old, 16)) && (h.cyl_count & CYLINDER_MASK) <= 84 && (h.cyl_count >> RESOLUTION_SHIFT) < 3 && h.head_count <= 2 && (!form_factor || !h.form_factor || h.form_factor == form_factor)) return FIFID_SIGN|FIFID_STRUCT; + return 0; } bool mfi_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { + std::error_condition err; size_t actual; + header h; - entry entries[84*2*4]; - io.read_at(0, &h, sizeof(header), actual); - int resolution = h.cyl_count >> RESOLUTION_SHIFT; + std::tie(err, actual) = read_at(io, 0, &h, sizeof(header)); + if(err || (sizeof(header) != actual)) + return false; + int const resolution = h.cyl_count >> RESOLUTION_SHIFT; h.cyl_count &= CYLINDER_MASK; - io.read_at(sizeof(header), &entries, (h.cyl_count << resolution)*h.head_count*sizeof(entry), actual); - image.set_form_variant(h.form_factor, h.variant); if(!h.cyl_count) return true; + entry entries[84*2*4]; + std::tie(err, actual) = read_at(io, sizeof(header), &entries, (h.cyl_count << resolution)*h.head_count*sizeof(entry)); + if(err || (((h.cyl_count << resolution)*h.head_count*sizeof(entry)) != actual)) + return false; + std::function<void (const std::vector<uint32_t> &src, std::vector<uint32_t> &track)> converter; - if(!memcmp( h.sign, sign, 16)) { - converter = [](const std::vector<uint32_t> &src, std::vector<uint32_t> &track) -> void { + if(!memcmp(h.sign, sign, 16)) { + converter = [] (const std::vector<uint32_t> &src, std::vector<uint32_t> &track) { uint32_t ctime = 0; for(uint32_t mg : src) { ctime += mg & TIME_MASK; @@ -139,7 +152,7 @@ bool mfi_format::load(util::random_read &io, uint32_t form_factor, const std::ve }; } else { - converter = [](const std::vector<uint32_t> &src, std::vector<uint32_t> &track) -> void { + converter = [] (const std::vector<uint32_t> &src, std::vector<uint32_t> &track) { unsigned int cell_count = src.size(); uint32_t mg = src[0] & MG_MASK; uint32_t wmg = src[cell_count - 1] & MG_MASK; @@ -184,7 +197,7 @@ bool mfi_format::load(util::random_read &io, uint32_t form_factor, const std::ve compressed.resize(ent->compressed_size); uncompressed.resize(cell_count); - io.read_at(ent->offset, &compressed[0], ent->compressed_size, actual); + std::tie(err, actual) = read_at(io, ent->offset, &compressed[0], ent->compressed_size); // FIXME: check for errors and premature EOF uLongf size = ent->uncompressed_size; if(uncompress((Bytef *)uncompressed.data(), &size, &compressed[0], ent->compressed_size) != Z_OK) { @@ -204,7 +217,6 @@ bool mfi_format::load(util::random_read &io, uint32_t form_factor, const std::ve bool mfi_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const { - size_t actual; int tracks, heads; image.get_actual_geometry(tracks, heads); int resolution = image.get_resolution(); @@ -224,7 +236,7 @@ bool mfi_format::save(util::random_read_write &io, const std::vector<uint32_t> & h.form_factor = image.get_form_factor(); h.variant = image.get_variant(); - io.write_at(0, &h, sizeof(header), actual); + write_at(io, 0, &h, sizeof(header)); // FIXME: check for errors memset(entries, 0, sizeof(entries)); @@ -258,11 +270,11 @@ bool mfi_format::save(util::random_read_write &io, const std::vector<uint32_t> & entries[epos].write_splice = image.get_write_splice_position(track >> 2, head, track & 3); epos++; - io.write_at(pos, postcomp.get(), csize, actual); + write_at(io, pos, postcomp.get(), csize); // FIXME: check for errors pos += csize; } - io.write_at(sizeof(header), entries, (tracks << resolution)*heads*sizeof(entry), actual); + write_at(io, sizeof(header), entries, (tracks << resolution)*heads*sizeof(entry)); // FIXME: check for errors return true; } diff --git a/src/lib/formats/mfi_dsk.h b/src/lib/formats/mfi_dsk.h index 89201c6db8a..7241bcfab32 100644 --- a/src/lib/formats/mfi_dsk.h +++ b/src/lib/formats/mfi_dsk.h @@ -45,13 +45,13 @@ private: static const char sign[16]; struct header { - char sign[16]; - unsigned int cyl_count, head_count; - unsigned int form_factor, variant; + uint8_t sign[16]; + uint32_t cyl_count, head_count; + uint32_t form_factor, variant; }; struct entry { - unsigned int offset, compressed_size, uncompressed_size, write_splice; + uint32_t offset, compressed_size, uncompressed_size, write_splice; }; }; diff --git a/src/lib/formats/mz_cas.cpp b/src/lib/formats/mz_cas.cpp index 6bc82a43bd1..ddc35b62663 100644 --- a/src/lib/formats/mz_cas.cpp +++ b/src/lib/formats/mz_cas.cpp @@ -69,7 +69,7 @@ static int fill_wave_b(int16_t *buffer, int offs, int byte) return count; } -static int fill_wave(int16_t *buffer, int length, uint8_t *code) +static int fill_wave(int16_t *buffer, int length, const uint8_t *code, int) { static int16_t *beg; static uint16_t csum = 0; diff --git a/src/lib/formats/nabupc_dsk.cpp b/src/lib/formats/nabupc_dsk.cpp index 79d67a5e25f..00205c1e560 100644 --- a/src/lib/formats/nabupc_dsk.cpp +++ b/src/lib/formats/nabupc_dsk.cpp @@ -18,6 +18,7 @@ #include "ioprocs.h" #include "strformat.h" + const nabupc_format::format nabupc_format::formats[] = { { // 200k 40 track single sided double density (nabu) @@ -167,8 +168,7 @@ bool nabupc_format::load(util::random_read &io, uint32_t form_factor, const std: for (int head = 0; head < f.head_count; head++) { desc_pc_sector sects[sector_count]; uint8_t sectdata[sector_count * sector_size]; - size_t actual; - io.read_at((track * f.head_count + head) * sector_count * sector_size, sectdata, sector_count * sector_size, actual); + /*auto const [err, actual] =*/ read_at(io, (track * f.head_count + head) * sector_count * sector_size, sectdata, sector_count * sector_size); // FIXME: check for errors and premature EOF for (int i = 0; i < sector_count; i++) { sects[i].track = track; sects[i].head = head; @@ -196,8 +196,7 @@ bool nabupc_format::save(util::random_read_write &io, const std::vector<uint32_t auto bitstream = generate_bitstream_from_track(track, head, 2000, image); auto sectors = extract_sectors_from_bitstream_mfm_pc(bitstream); for (int i = 0; i < sector_count; i++) { - size_t actual; - io.write_at(file_offset, sectors[i + 1].data(), sector_size, actual); + /*auto const [err, actual] =*/ write_at(io, file_offset, sectors[i + 1].data(), sector_size); // FIXME: check for errors file_offset += sector_size; } } diff --git a/src/lib/formats/nfd_dsk.cpp b/src/lib/formats/nfd_dsk.cpp index 773b35d8aef..a51b461e144 100644 --- a/src/lib/formats/nfd_dsk.cpp +++ b/src/lib/formats/nfd_dsk.cpp @@ -108,10 +108,11 @@ const char *nfd_format::extensions() const noexcept int nfd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t h[16]; - size_t actual; - io.read_at(0, h, 16, actual); + auto const [err, actual] = read_at(io, 0, h, 16); // TODO: does it really need 16 bytes? it only looks at 14. + if (err || (16 != actual)) + return 0; - if (strncmp((const char *)h, "T98FDDIMAGE.R0", 14) == 0 || strncmp((const char *)h, "T98FDDIMAGE.R1", 14) == 0) + if (!memcmp(h, "T98FDDIMAGE.R0", 14) || !memcmp(h, "T98FDDIMAGE.R1", 14)) return FIFID_SIGN; return 0; @@ -119,13 +120,12 @@ int nfd_format::identify(util::random_read &io, uint32_t form_factor, const std: bool nfd_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { - size_t actual; uint64_t size; if (io.length(size)) return false; uint8_t h[0x120], hsec[0x10]; - io.read_at(0, h, 0x120, actual); - int format_version = !strncmp((const char *)h, "T98FDDIMAGE.R0", 14) ? 0 : 1; + read_at(io, 0, h, 0x120); // FIXME: check for errors and premature EOF + int format_version = !memcmp(h, "T98FDDIMAGE.R0", 14) ? 0 : 1; // sector map (the 164th entry is only used by rev.1 format, loops with track < 163 are correct for rev.0) uint8_t disk_type = 0; @@ -149,7 +149,7 @@ bool nfd_format::load(util::random_read &io, uint32_t form_factor, const std::ve { int curr_track_size = 0; // read sector map absolute location - io.read_at(pos, hsec, 4, actual); + read_at(io, pos, hsec, 4); // FIXME: check for errors and premature EOF pos += 4; uint32_t secmap_addr = little_endianize_int32(*(uint32_t *)(hsec)); @@ -158,14 +158,14 @@ bool nfd_format::load(util::random_read &io, uint32_t form_factor, const std::ve // read actual sector map for the sectors of this track // for rev.1 format the first 0x10 are a track summary: // first WORD is # of sectors, second WORD is # of special data sectors - io.read_at(secmap_addr, hsec, 0x10, actual); + read_at(io, secmap_addr, hsec, 0x10); // FIXME: check for errors and premature EOF secmap_addr += 0x10; num_secs[track] = little_endianize_int16(*(uint16_t *)(hsec)); num_specials[track] = little_endianize_int16(*(uint16_t *)(hsec + 0x2)); for (int sect = 0; sect < num_secs[track]; sect++) { - io.read_at(secmap_addr, hsec, 0x10, actual); + read_at(io, secmap_addr, hsec, 0x10); // FIXME: check for errors and premature EOF if (track == 0 && sect == 0) disk_type = hsec[0xb]; // can this change across the disk? I don't think so... @@ -184,7 +184,7 @@ bool nfd_format::load(util::random_read &io, uint32_t form_factor, const std::ve { for (int sect = 0; sect < num_specials[track]; sect++) { - io.read_at(secmap_addr, hsec, 0x10, actual); + read_at(io, secmap_addr, hsec, 0x10); // FIXME: check for errors and premature EOF secmap_addr += 0x10; curr_track_size += (hsec[9] + 1) * little_endianize_int32(*(uint32_t *)(hsec + 0x0a)); } @@ -207,7 +207,7 @@ bool nfd_format::load(util::random_read &io, uint32_t form_factor, const std::ve { // read sector map for this sector // for rev.0 format each sector uses 0x10 bytes - io.read_at(pos, hsec, 0x10, actual); + read_at(io, pos, hsec, 0x10); // FIXME: check for errors and premature EOF if (track == 0 && sect == 0) disk_type = hsec[0xa]; // can this change across the disk? I don't think so... @@ -254,7 +254,7 @@ bool nfd_format::load(util::random_read &io, uint32_t form_factor, const std::ve for (int track = 0; track < 163 && pos < size; track++) { - io.read_at(pos, sect_data, track_sizes[track], actual); + read_at(io, pos, sect_data, track_sizes[track]); // FIXME: check for errors and premature EOF for (int i = 0; i < num_secs[track]; i++) { @@ -277,7 +277,7 @@ bool nfd_format::load(util::random_read &io, uint32_t form_factor, const std::ve if (mfm[track * 26]) build_pc_track_mfm(track / 2, track % 2, image, cell_count, num_secs[track], sects, calc_default_pc_gap3_size(form_factor, (128 << sec_sizes[track * 26]))); else - build_pc_track_fm(track / 2, track % 2, image, cell_count, num_secs[track], sects, calc_default_pc_gap3_size(form_factor, (128 << sec_sizes[track * 26]))); + build_pc_track_fm(track / 2, track % 2, image, cell_count / 2, num_secs[track], sects, calc_default_pc_gap3_size(form_factor, (128 << sec_sizes[track * 26]))); } return true; diff --git a/src/lib/formats/orao_cas.cpp b/src/lib/formats/orao_cas.cpp index a580fdef077..8cc77e3bf6f 100644 --- a/src/lib/formats/orao_cas.cpp +++ b/src/lib/formats/orao_cas.cpp @@ -62,7 +62,7 @@ static int orao_cas_to_wav_size( const uint8_t *casdata, int caslen ) { return size; } -static int orao_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) { +static int orao_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { int i,j,size,k; uint8_t b; size = 0; diff --git a/src/lib/formats/oric_dsk.cpp b/src/lib/formats/oric_dsk.cpp index f4d9b78faa9..15bb5ce1455 100644 --- a/src/lib/formats/oric_dsk.cpp +++ b/src/lib/formats/oric_dsk.cpp @@ -12,6 +12,7 @@ #include "ioprocs.h" #include "multibyte.h" +#include "osdcore.h" #include <cstring> @@ -39,8 +40,7 @@ bool oric_dsk_format::supports_save() const noexcept int oric_dsk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t h[256]; - size_t actual; - io.read_at(0, h, 256, actual); + /*auto const [err, actual] =*/ read_at(io, 0, h, 256); // FIXME: check for errors and premature EOF if(memcmp(h, "MFM_DISK", 8)) return 0; @@ -60,19 +60,29 @@ int oric_dsk_format::identify(util::random_read &io, uint32_t form_factor, const bool oric_dsk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { - size_t actual; uint8_t h[256]; uint8_t t[6250+3]; t[6250] = t[6251] = t[6252] = 0; - io.read_at(0, h, 256, actual); + read_at(io, 0, h, 256); // FIXME: check for errors and premature EOF int sides = get_u32le(&h[ 8]); int tracks = get_u32le(&h[12]); + int max_tracks, max_sides; + image.get_maximal_geometry(max_tracks, max_sides); + if (tracks > max_tracks) { + osd_printf_error("oric_dsk: Floppy disk has too many tracks for this drive (floppy tracks=%d, drive tracks=%d).\n", tracks, max_tracks); + return false; + } + if (sides > max_sides) { + osd_printf_warning("oric_dsk: Floppy disk has excess of heads for this drive that will be discarded (floppy heads=%d, drive heads=%d).\n", sides, max_sides); + sides = max_sides; + } + for(int side=0; side<sides; side++) for(int track=0; track<tracks; track++) { - io.read_at(256+6400*(tracks*side + track), t, 6250, actual); + read_at(io, 256+6400*(tracks*side + track), t, 6250); // FIXME: check for errors and premature EOF std::vector<uint32_t> stream; int sector_size = 128; for(int i=0; i<6250; i++) { @@ -157,9 +167,9 @@ bool oric_jasmin_format::load(util::random_read &io, uint32_t form_factor, const int const heads = size == 41*17*256 ? 1 : 2; - std::vector<uint8_t> data(size); - size_t actual; - io.read_at(0, data.data(), size, actual); + auto const [err, data, actual] = read_at(io, 0, size); + if(err || (actual != size)) + return false; for(int head = 0; head != heads; head++) for(int track = 0; track != 41; track++) { @@ -171,7 +181,7 @@ bool oric_jasmin_format::load(util::random_read &io, uint32_t form_factor, const sdesc[s].sector = sector + 1; sdesc[s].size = 1; sdesc[s].actual_size = 256; - sdesc[s].data = data.data() + 256 * (sector + track*17 + head*17*41); + sdesc[s].data = &data[256 * (sector + track*17 + head*17*41)]; sdesc[s].deleted = false; sdesc[s].bad_crc = false; } @@ -203,8 +213,7 @@ bool oric_jasmin_format::save(util::random_read_write &io, const std::vector<uin auto sectors = extract_sectors_from_bitstream_mfm_pc(generate_bitstream_from_track(track, head, 2000, image)); for(unsigned int sector = 0; sector != 17; sector ++) { uint8_t const *const data = (sector+1 < sectors.size() && !sectors[sector+1].empty()) ? sectors[sector+1].data() : zero; - size_t actual; - io.write_at(256 * (sector + track*17 + head*17*41), data, 256, actual); + /*auto const [err, actual] =*/ write_at(io, 256 * (sector + track*17 + head*17*41), data, 256); // FIXME: check for errors } } return true; diff --git a/src/lib/formats/oric_tap.cpp b/src/lib/formats/oric_tap.cpp index 7f900808f21..a8b11a5ee7a 100644 --- a/src/lib/formats/oric_tap.cpp +++ b/src/lib/formats/oric_tap.cpp @@ -345,139 +345,135 @@ static int oric_cassette_calculate_size_in_samples(const uint8_t *bytes, int len } /* length is length of sample buffer to fill! */ -static int oric_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int oric_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { unsigned char header[9]; - uint8_t *data_ptr; - int16_t *p; - int i; - uint8_t data; - - p = buffer; - + int16_t *p = buffer; /* header and trailer act as pauses */ /* the trailer is required so that the via sees the last bit of the last byte */ - if (bytes == CODE_HEADER) { - for (i = 0; i < ORIC_WAVESAMPLES_HEADER; i++) + if (bytes == CODE_HEADER) + { + for (int i = 0; i < ORIC_WAVESAMPLES_HEADER; i++) *(p++) = WAVEENTRY_NULL; } - else if (bytes == CODE_TRAILER) { - for (i = 0; i < ORIC_WAVESAMPLES_TRAILER; i++) + else if (bytes == CODE_TRAILER) + { + for (int i = 0; i < ORIC_WAVESAMPLES_TRAILER; i++) *(p++) = WAVEENTRY_NULL; } else -{ - /* the length is the number of samples left in the buffer and NOT the number of bytes for the input file */ - length = length - ORIC_WAVESAMPLES_TRAILER; - - oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE; - data_ptr = bytes; - - while ((data_ptr<(bytes + oric.tap_size)) && (p < (buffer+length)) ) { - data = data_ptr[0]; - data_ptr++; + /* the length is the number of samples left in the buffer and NOT the number of bytes for the input file */ + length = length - ORIC_WAVESAMPLES_TRAILER; - switch (oric.cassette_state) + oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE; + const uint8_t *data_ptr = bytes; + + while ((data_ptr < (bytes + oric.tap_size)) && (p < (buffer+length)) ) { - case ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE: - { - if (data==ORIC_SYNC_BYTE) - { - LOG_FORMATS("found sync byte!\n"); - /* found first sync byte */ - oric.cassette_state = ORIC_CASSETTE_GOT_SYNC_BYTE; - } - } - break; + const uint8_t data = data_ptr[0]; + data_ptr++; - case ORIC_CASSETTE_GOT_SYNC_BYTE: + switch (oric.cassette_state) { - if (data!=ORIC_SYNC_BYTE) + case ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE: { - /* 0.25 second pause */ - p = oric_fill_pause(p, oric_seconds_to_samples(0.25)); - - LOG_FORMATS("found end of sync bytes!\n"); - /* found end of sync bytes */ - for (i=0; i<ORIC_LEADER_LENGTH; i++) + if (data == ORIC_SYNC_BYTE) { - p = oric_output_byte(p,0x016); + LOG_FORMATS("found sync byte!\n"); + /* found first sync byte */ + oric.cassette_state = ORIC_CASSETTE_GOT_SYNC_BYTE; } + } + break; - if (data==0x024) + case ORIC_CASSETTE_GOT_SYNC_BYTE: + { + if (data != ORIC_SYNC_BYTE) { - //LOG_FORMATS("reading header!\n"); - p = oric_output_byte(p,data); - oric.cassette_state = ORIC_CASSETTE_READ_HEADER; - oric.data_count = 0; - oric.data_length = 9; + /* 0.25 second pause */ + p = oric_fill_pause(p, oric_seconds_to_samples(0.25)); + + LOG_FORMATS("found end of sync bytes!\n"); + /* found end of sync bytes */ + for (int i = 0; i < ORIC_LEADER_LENGTH; i++) + { + p = oric_output_byte(p,0x016); + } + + if (data == 0x024) + { + //LOG_FORMATS("reading header!\n"); + p = oric_output_byte(p, data); + oric.cassette_state = ORIC_CASSETTE_READ_HEADER; + oric.data_count = 0; + oric.data_length = 9; + } } } - } - break; - - case ORIC_CASSETTE_READ_HEADER: - { - header[oric.data_count] = data; - p = oric_output_byte(p, data); - oric.data_count++; + break; - if (oric.data_count==oric.data_length) + case ORIC_CASSETTE_READ_HEADER: { - //LOG_FORMATS("finished reading header!\n"); - oric.cassette_state = ORIC_CASSETTE_READ_FILENAME; - } - } - break; + header[oric.data_count] = data; + p = oric_output_byte(p, data); + oric.data_count++; - case ORIC_CASSETTE_READ_FILENAME: - { - p = oric_output_byte(p, data); + if (oric.data_count==oric.data_length) + { + //LOG_FORMATS("finished reading header!\n"); + oric.cassette_state = ORIC_CASSETTE_READ_FILENAME; + } + } + break; - /* got end of filename? */ - if (data==0) + case ORIC_CASSETTE_READ_FILENAME: { - uint16_t end, start; - LOG_FORMATS("got end of filename\n"); + p = oric_output_byte(p, data); - /* oric includes a small delay, but I don't see - it being 1 bits */ - for (i=0; i<100; i++) + /* got end of filename? */ + if (data == 0) { - p = oric_output_bit(p,1); - } + uint16_t end, start; + LOG_FORMATS("got end of filename\n"); - oric.cassette_state = ORIC_CASSETTE_WRITE_DATA; - oric.data_count = 0; + /* oric includes a small delay, but I don't see + it being 1 bits */ + for (int i = 0; i < 100; i++) + { + p = oric_output_bit(p,1); + } - end = get_u16be(&header[4]); - start = get_u16be(&header[6]); - LOG(("start (from header): %02x\n",start)); - LOG(("end (from header): %02x\n",end)); - oric.data_length = end - start + 1; - } - } - break; + oric.cassette_state = ORIC_CASSETTE_WRITE_DATA; + oric.data_count = 0; - case ORIC_CASSETTE_WRITE_DATA: - { - p = oric_output_byte(p, data); - oric.data_count++; + end = get_u16be(&header[4]); + start = get_u16be(&header[6]); + LOG(("start (from header): %02x\n",start)); + LOG(("end (from header): %02x\n",end)); + oric.data_length = end - start + 1; + } + } + break; - if (oric.data_count==oric.data_length) + case ORIC_CASSETTE_WRITE_DATA: { - LOG_FORMATS("finished writing data!\n"); - oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE; + p = oric_output_byte(p, data); + oric.data_count++; + + if (oric.data_count==oric.data_length) + { + LOG_FORMATS("finished writing data!\n"); + oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE; + } } - } - break; + break; + } } } -} return p - buffer; } diff --git a/src/lib/formats/os9_dsk.cpp b/src/lib/formats/os9_dsk.cpp index f74cd7fb3a6..ca79ced2145 100644 --- a/src/lib/formats/os9_dsk.cpp +++ b/src/lib/formats/os9_dsk.cpp @@ -88,8 +88,7 @@ int os9_format::find_size(util::random_read &io, uint32_t form_factor, const std return -1; uint8_t os9_header[0x60]; - size_t actual; - io.read_at(0, os9_header, sizeof(os9_header), actual); + /*auto const [err, actual] =*/ read_at(io, 0, os9_header, sizeof(os9_header)); // FIXME: check for errors and premature EOF int os9_total_sectors = get_u24be(&os9_header[0x00]); int os9_heads = util::BIT(os9_header[0x10], 0) ? 2 : 1; diff --git a/src/lib/formats/p6001_cas.cpp b/src/lib/formats/p6001_cas.cpp index c4689eb9be4..cdb0b922680 100644 --- a/src/lib/formats/p6001_cas.cpp +++ b/src/lib/formats/p6001_cas.cpp @@ -53,7 +53,7 @@ static int pc6001_cas_to_wav_size (const uint8_t *casdata, int caslen) /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int pc6001_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int pc6001_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { return pc6001_handle_cas(buffer,bytes); } diff --git a/src/lib/formats/pasti_dsk.cpp b/src/lib/formats/pasti_dsk.cpp index 1390072b97c..ae82ee7939b 100644 --- a/src/lib/formats/pasti_dsk.cpp +++ b/src/lib/formats/pasti_dsk.cpp @@ -45,11 +45,10 @@ bool pasti_format::supports_save() const noexcept int pasti_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { uint8_t h[16]; - size_t actual; - io.read_at(0, h, 16, actual); + /*auto const [err, actual] =*/ read_at(io, 0, h, 16); // FIXME: check for errors and premature EOF if(!memcmp(h, "RSY\0\3\0", 6) && - (1 || (h[10] >= 80 && h[10] <= 82) || (h[10] >= 160 && h[10] <= 164))) + (1 || (h[10] >= 80 && h[10] <= 82) || (h[10] >= 160 && h[10] <= 164))) // TODO: why is this check disabled? return FIFID_SIGN; return 0; @@ -67,9 +66,8 @@ static void hexdump(const uint8_t *d, int s) bool pasti_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { - size_t actual; uint8_t fh[16]; - io.read_at(0, fh, 16, actual); + read_at(io, 0, fh, 16); // FIXME: check for errors and premature EOF std::vector<uint8_t> raw_track; @@ -84,7 +82,7 @@ bool pasti_format::load(util::random_read &io, uint32_t form_factor, const std:: for(int track=0; track < tracks; track++) { for(int head=0; head < heads; head++) { uint8_t th[16]; - io.read_at(pos, th, 16, actual); + read_at(io, pos, th, 16); // FIXME: check for errors and premature EOF int entry_len = get_u32le(&th[0]); int fuzz_len = get_u32le(&th[4]); int sect = get_u16le(&th[8]); @@ -95,7 +93,7 @@ bool pasti_format::load(util::random_read &io, uint32_t form_factor, const std:: raw_track.resize(entry_len-16); - io.read_at(pos+16, &raw_track[0], entry_len-16, actual); + read_at(io, pos+16, &raw_track[0], entry_len-16); // FIXME: check for errors and premature EOF uint8_t *fuzz = fuzz_len ? &raw_track[16*sect] : nullptr; uint8_t *bdata = fuzz ? fuzz+fuzz_len : &raw_track[16*sect]; diff --git a/src/lib/formats/pc98_dsk.cpp b/src/lib/formats/pc98_dsk.cpp index 534a5216450..36dac83e2af 100644 --- a/src/lib/formats/pc98_dsk.cpp +++ b/src/lib/formats/pc98_dsk.cpp @@ -83,6 +83,10 @@ const pc98_format::format pc98_format::formats[] = { floppy_image::FF_525, floppy_image::DSHD, floppy_image::MFM, 1200, 8, 77, 2, 1024, {}, 1, {}, 80, 50, 22, 84 }, + { /* 1MB 5 1/4 inch 256bps n88 basic disk type */ + floppy_image::FF_525, floppy_image::DSHD, floppy_image::MFM, + 1200, 26, 77, 2, 256, {}, 1, {}, 80, 50, 21, 77 + }, {} }; diff --git a/src/lib/formats/pc98fdi_dsk.cpp b/src/lib/formats/pc98fdi_dsk.cpp index b331ad8b521..10822ab316d 100644 --- a/src/lib/formats/pc98fdi_dsk.cpp +++ b/src/lib/formats/pc98fdi_dsk.cpp @@ -11,8 +11,9 @@ #include "pc98fdi_dsk.h" #include "ioprocs.h" +#include "multibyte.h" -#include "osdcomm.h" // little_endianize_int32 +#include <tuple> pc98fdi_format::pc98fdi_format() @@ -41,15 +42,16 @@ int pc98fdi_format::identify(util::random_read &io, uint32_t form_factor, const return 0; uint8_t h[32]; - size_t actual; - io.read_at(0, h, 32, actual); - - uint32_t const hsize = little_endianize_int32(*(uint32_t *) (h + 0x8)); - uint32_t const psize = little_endianize_int32(*(uint32_t *) (h + 0xc)); - uint32_t const ssize = little_endianize_int32(*(uint32_t *) (h + 0x10)); - uint32_t const scnt = little_endianize_int32(*(uint32_t *) (h + 0x14)); - uint32_t const sides = little_endianize_int32(*(uint32_t *) (h + 0x18)); - uint32_t const ntrk = little_endianize_int32(*(uint32_t *) (h + 0x1c)); + auto const [err, actual] = read_at(io, 0, h, 32); + if(err || (32 != actual)) + return 0; + + uint32_t const hsize = get_u32le(h + 0x8); + uint32_t const psize = get_u32le(h + 0xc); + uint32_t const ssize = get_u32le(h + 0x10); + uint32_t const scnt = get_u32le(h + 0x14); + uint32_t const sides = get_u32le(h + 0x18); + uint32_t const ntrk = get_u32le(h + 0x1c); if(size == hsize + psize && psize == ssize*scnt*sides*ntrk) return FIFID_STRUCT; @@ -58,16 +60,19 @@ int pc98fdi_format::identify(util::random_read &io, uint32_t form_factor, const bool pc98fdi_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { + std::error_condition err; size_t actual; uint8_t h[32]; - io.read_at(0, h, 32, actual); + std::tie(err, actual) = read_at(io, 0, h, 32); + if(err || (32 != actual)) + return false; - uint32_t const hsize = little_endianize_int32(*(uint32_t *)(h+0x8)); - uint32_t const sector_size = little_endianize_int32(*(uint32_t *)(h+0x10)); - uint32_t const sector_count = little_endianize_int32(*(uint32_t *)(h+0x14)); - uint32_t const head_count = little_endianize_int32(*(uint32_t *)(h+0x18)); - uint32_t const track_count = little_endianize_int32(*(uint32_t *)(h+0x1c)); + uint32_t const hsize = get_u32le(h + 0x8); + uint32_t const sector_size = get_u32le(h + 0x10); + uint32_t const sector_count = get_u32le(h + 0x14); + uint32_t const head_count = get_u32le(h + 0x18); + uint32_t const track_count = get_u32le(h + 0x1c); int const cell_count = form_factor == floppy_image::FF_35 ? 200000 : 166666; @@ -78,9 +83,9 @@ bool pc98fdi_format::load(util::random_read &io, uint32_t form_factor, const std desc_pc_sector sects[256]; uint8_t sect_data[65536]; - for(int track=0; track < track_count; track++) + for(int track=0; track < track_count; track++) { for(int head=0; head < head_count; head++) { - io.read_at(hsize + sector_size*sector_count*(track*head_count + head), sect_data, sector_size*sector_count, actual); + std::tie(err, actual) = read_at(io, hsize + sector_size*sector_count*(track*head_count + head), sect_data, sector_size*sector_count); // FIXME: check for errors and premature EOF for(int i=0; i<sector_count; i++) { sects[i].track = track; @@ -95,6 +100,7 @@ bool pc98fdi_format::load(util::random_read &io, uint32_t form_factor, const std build_pc_track_mfm(track, head, image, cell_count, sector_count, sects, calc_default_pc_gap3_size(form_factor, sector_size)); } + } return true; } diff --git a/src/lib/formats/phc25_cas.cpp b/src/lib/formats/phc25_cas.cpp index 8294df07628..e6dab7e88e1 100644 --- a/src/lib/formats/phc25_cas.cpp +++ b/src/lib/formats/phc25_cas.cpp @@ -129,7 +129,7 @@ static int phc25_handle_cassette(int16_t *buffer, const uint8_t *bytes) Generate samples for the tape image ********************************************************************/ -static int phc25_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int phc25_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { return phc25_handle_cassette(buffer, bytes); } diff --git a/src/lib/formats/pmd_cas.cpp b/src/lib/formats/pmd_cas.cpp index ef4cef7c00a..224a3b0af5b 100644 --- a/src/lib/formats/pmd_cas.cpp +++ b/src/lib/formats/pmd_cas.cpp @@ -168,7 +168,7 @@ static int pmd85_handle_cassette(int16_t *buffer, const uint8_t *bytes) Generate samples for the tape image ********************************************************************/ -static int pmd85_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int pmd85_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { return pmd85_handle_cassette(buffer, bytes); } diff --git a/src/lib/formats/poly_dsk.cpp b/src/lib/formats/poly_dsk.cpp index 4a264e293a1..8b8f48780f8 100644 --- a/src/lib/formats/poly_dsk.cpp +++ b/src/lib/formats/poly_dsk.cpp @@ -50,9 +50,8 @@ int poly_cpm_format::identify(util::random_read &io, uint32_t form_factor, const { // check for Poly CP/M boot sector uint8_t boot[16]; - size_t actual; - io.read_at(0, boot, 16, actual); - if (memcmp(boot, "\x86\xc3\xb7\x00\x00\x8e\x10\xc0\xbf\x00\x01\xbf\xe0\x60\x00\x00", 16) == 0) + auto const [err, actual] = read_at(io, 0, boot, 16); + if (!err && (16 == actual) && !memcmp(boot, "\x86\xc3\xb7\x00\x00\x8e\x10\xc0\xbf\x00\x01\xbf\xe0\x60\x00\x00", 16)) { return FIFID_SIZE|FIFID_SIGN; } @@ -114,8 +113,7 @@ bool poly_cpm_format::load(util::random_read &io, uint32_t form_factor, const st sects[i].deleted = false; sects[i].bad_crc = false; sects[i].data = §_data[sdatapos]; - size_t actual; - io.read(sects[i].data, bps, actual); + /*auto const [err, actual] =*/ read(io, sects[i].data, bps); // FIXME: check for errors and premature EOF sdatapos += bps; } // gap sizes unverified diff --git a/src/lib/formats/primoptp.cpp b/src/lib/formats/primoptp.cpp index ccadcb6d70d..81eaf65b65a 100644 --- a/src/lib/formats/primoptp.cpp +++ b/src/lib/formats/primoptp.cpp @@ -138,12 +138,12 @@ static int primo_cassette_calculate_size_in_samples(const uint8_t *bytes, int le return size_in_samples; } -static int primo_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int primo_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { int i = 0, j = 0, k; int16_t *p = buffer; - uint8_t *b = bytes; + const uint8_t *b = bytes; uint32_t file_size = 0; uint16_t block_size = 0; diff --git a/src/lib/formats/rk_cas.cpp b/src/lib/formats/rk_cas.cpp index 4747ccd50ed..25bbb79eea3 100644 --- a/src/lib/formats/rk_cas.cpp +++ b/src/lib/formats/rk_cas.cpp @@ -77,7 +77,7 @@ static int gam_cas_to_wav_size( const uint8_t *casdata, int caslen ) { return (RK_HEADER_LEN * 8 * 2 + caslen * 8 * 2) * RK_SIZE_20; } -static int rk20_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) { +static int rk20_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { int i; int16_t * p = buffer; @@ -93,7 +93,7 @@ static int rk20_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) { return p - buffer; } -static int rk22_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) { +static int rk22_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { int i; int16_t * p = buffer; @@ -109,7 +109,7 @@ static int rk22_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) { return p - buffer; } -static int rk60_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) { +static int rk60_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { int i; int16_t * p = buffer; @@ -125,7 +125,7 @@ static int rk60_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) { return p - buffer; } -static int gam_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) { +static int gam_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { int i; int16_t * p = buffer; diff --git a/src/lib/formats/rx50_dsk.cpp b/src/lib/formats/rx50_dsk.cpp index cefc2725209..853caf0dbc1 100644 --- a/src/lib/formats/rx50_dsk.cpp +++ b/src/lib/formats/rx50_dsk.cpp @@ -139,7 +139,7 @@ int rx50img_format::identify(util::random_read &io, uint32_t form_factor, const return 0; } - // /* Sectors are numbered 1 to 10 */ +// Sectors are numbered 1 to 10 bool rx50img_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { uint8_t track_count, head_count, sector_count; @@ -158,8 +158,7 @@ bool rx50img_format::load(util::random_read &io, uint32_t form_factor, const std int track_size = sector_count*512; for(int track=0; track < track_count; track++) { for(int head=0; head < head_count; head++) { - size_t actual; - io.read_at((track*head_count + head)*track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ read_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors and premature EOF generate_track(rx50_10_desc, track, head, sectors, sector_count, 102064, image); // 98480 } } @@ -205,8 +204,7 @@ bool rx50img_format::save(util::random_read_write &io, const std::vector<uint32_ for(int track=0; track < track_count; track++) { for(int head=0; head < head_count; head++) { get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata); - size_t actual; - io.write_at((track*head_count + head)*track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ write_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors } } return true; diff --git a/src/lib/formats/sap_dsk.cpp b/src/lib/formats/sap_dsk.cpp new file mode 100644 index 00000000000..ef51cb8b772 --- /dev/null +++ b/src/lib/formats/sap_dsk.cpp @@ -0,0 +1,169 @@ +// license:BSD-3-Clause +// copyright-holders:AJR + +#include "sap_dsk.h" + +#include "ioprocs.h" +#include "multibyte.h" + +static constexpr unsigned HEADER_LENGTH = 66; +static constexpr uint8_t MAGIC_XOR = 0xb3; + +const sap_dsk_format FLOPPY_SAP_FORMAT; + +inline uint16_t sap_dsk_format::accumulate_crc(uint16_t crc, uint8_t data) noexcept +{ + // Almost CRC16-CCITT, but not quite... + for (int shift : {0, 4}) + crc = ((crc >> 4) & 0xfff) ^ 0x1081 * ((crc ^ (data >> shift)) & 0xf); + return crc; +} + +const char *sap_dsk_format::name() const noexcept +{ + return "sap"; +} + +const char *sap_dsk_format::description() const noexcept +{ + return "Thomson SAP disk image (Systeme d'Archivage Pukall)"; +} + +const char *sap_dsk_format::extensions() const noexcept +{ + return "sap"; +} + +bool sap_dsk_format::supports_save() const noexcept +{ + return false; +} + +int sap_dsk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const +{ + uint8_t buffer[HEADER_LENGTH]; + auto [err, actual] = read_at(io, 0, buffer, HEADER_LENGTH); + if (err || actual != HEADER_LENGTH) + return 0; + + // Check ID string + if (memcmp(&buffer[1], "SYSTEME D'ARCHIVAGE PUKALL S.A.P. (c) Alexandre PUKALL Avril 1998", HEADER_LENGTH - 1) != 0) + return 0; + + // Check disk format + if (form_factor == floppy_image::FF_35) + { + // 3.5" disks must have 80 double-density tracks + if ((buffer[0] & 0x7f) != 0x01) + return 0; + } + else if (form_factor == floppy_image::FF_525) + { + // 5.25" disks must have 40 tracks + if ((buffer[0] & 0x7e) != 0x02) + return 0; + } + else if (form_factor != floppy_image::FF_UNKNOWN) + return 0; + + uint32_t expected_size = HEADER_LENGTH + uint32_t((buffer[0] & 0x01) ? 256 + 6 : 128 + 6) * ((buffer[0] & 0x02) ? 40 : 80) * 16 * ((buffer[0] & 0x80) ? 2 : 1); + uint64_t actual_size; + if (!io.length(actual_size) && expected_size == actual_size) + return FIFID_SIGN | FIFID_SIZE; + else + return FIFID_SIGN; +} + +bool sap_dsk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const +{ + uint8_t disk_type; + if (auto [err, actual] = read_at(io, 0, &disk_type, 1); err || actual != 1) + return false; + + int track_count = (disk_type & 0x02) ? 40 : 80; + int head_count = (disk_type & 0x80) ? 2 : 1; + int max_tracks, max_heads; + image.get_maximal_geometry(max_tracks, max_heads); + if (max_heads < head_count || max_tracks < track_count) + return false; + + switch (disk_type & 0x03) + { + case 0x01: + image.set_form_variant(floppy_image::FF_35, (disk_type & 0x80) ? floppy_image::DSDD : floppy_image::SSDD); + break; + + case 0x02: + image.set_form_variant(floppy_image::FF_525, (disk_type & 0x80) ? floppy_image::DSSD : floppy_image::SSSD); + break; + + case 0x03: + image.set_form_variant(floppy_image::FF_525, (disk_type & 0x80) ? floppy_image::DSDD : floppy_image::SSDD); + break; + + default: + return false; + } + + uint8_t sector_buf[16 * 1024]; + uint8_t sector_header[4]; + uint8_t sector_crc[2]; + desc_pc_sector sectors[16]; + + size_t read_offset = HEADER_LENGTH; + for (int head = 0; head < head_count; head++) + { + for (int track = 0; track < track_count; track++) + { + uint8_t *bufptr = §or_buf[0]; + uint8_t track_size = 16; + int sector_count = 0; + bool is_mfm = disk_type & 0x01; + while (track_size != 0) + { + if (auto [err, actual] = read_at(io, read_offset, §or_header[0], 4); err || actual != 4) + return false; + if (sector_count == 0 && sector_header[0] == 0x01) + is_mfm = false; + uint8_t sector_size = (sector_header[0] & 0x02) ? 4 >> (sector_header[0] & 0x01) : 1; + if (track_size < sector_size) + return false; + uint16_t sector_octets = sector_size * (is_mfm ? 256 : 128); + if (auto [err, actual] = read_at(io, read_offset + 4, bufptr, sector_octets); err || actual != sector_octets) + return false; + if (auto [err, actual] = read_at(io, read_offset + 4 + sector_octets, §or_crc[0], 2); err || actual != 2) + return false; + + uint16_t crc = 0xffff; + for (uint8_t data : sector_header) + crc = accumulate_crc(crc, data); + for (uint16_t i = 0; i < sector_octets; i++) + { + bufptr[i] ^= MAGIC_XOR; + crc = accumulate_crc(crc, bufptr[i]); + } + + sectors[sector_count].track = sector_header[2]; + sectors[sector_count].head = head; + sectors[sector_count].sector = sector_header[3]; + sectors[sector_count].size = sector_size; + sectors[sector_count].actual_size = sector_octets; + sectors[sector_count].data = bufptr; + // Teo claims this flag is set for protection with "all the gap bytes set to 0xF7"; actual purpose is a bit unclear + sectors[sector_count].deleted = bool(sector_header[0] & 0x04); + sectors[sector_count].bad_crc = crc != get_u16be(sector_crc); + + read_offset += sector_octets + 6; + bufptr += sector_octets; + track_size -= sector_size; + sector_count++; + } + if (is_mfm) + build_pc_track_mfm(track, head, image, 100000, sector_count, sectors, calc_default_pc_gap3_size(form_factor, sectors[0].actual_size)); + else + build_pc_track_fm(track, head, image, 100000 / 2, sector_count, sectors, calc_default_pc_gap3_size(form_factor, sectors[0].actual_size)); + } + } + + return true; +} diff --git a/src/lib/formats/sap_dsk.h b/src/lib/formats/sap_dsk.h new file mode 100644 index 00000000000..5bc86a2d6ef --- /dev/null +++ b/src/lib/formats/sap_dsk.h @@ -0,0 +1,30 @@ +// license:BSD-3-Clause +// copyright-holders:AJR + +#ifndef MAME_FORMATS_SAP_DSK_H +#define MAME_FORMATS_SAP_DSK_H + +#pragma once + +#include "flopimg.h" + +class sap_dsk_format : public floppy_image_format_t +{ +public: + sap_dsk_format() = default; + + virtual const char *name() const noexcept override; + virtual const char *description() const noexcept override; + virtual const char *extensions() const noexcept override; + virtual bool supports_save() const noexcept override; + + virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override; + virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override; + +private: + static inline uint16_t accumulate_crc(uint16_t crc, uint8_t data) noexcept; +}; + +extern const sap_dsk_format FLOPPY_SAP_FORMAT; + +#endif // MAME_FORMATS_SAP_DSK_H diff --git a/src/lib/formats/sdf_dsk.cpp b/src/lib/formats/sdf_dsk.cpp index 6e3f1147167..92a6b9fb653 100644 --- a/src/lib/formats/sdf_dsk.cpp +++ b/src/lib/formats/sdf_dsk.cpp @@ -50,8 +50,11 @@ int sdf_format::identify(util::random_read &io, uint32_t form_factor, const std: return 0; } - size_t actual; - io.read_at(0, header, HEADER_SIZE, actual); + auto const [err, actual] = read_at(io, 0, header, HEADER_SIZE); + if (err || (HEADER_SIZE != actual)) + { + return 0; + } int tracks = header[4]; int heads = header[5]; @@ -79,12 +82,11 @@ int sdf_format::identify(util::random_read &io, uint32_t form_factor, const std: bool sdf_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { - size_t actual; uint8_t header[HEADER_SIZE]; std::vector<uint8_t> track_data(TOTAL_TRACK_SIZE); std::vector<uint32_t> raw_track_data; - io.read_at(0, header, HEADER_SIZE, actual); + read_at(io, 0, header, HEADER_SIZE); // FIXME: check for errors and premature EOF const int tracks = header[4]; const int heads = header[5]; @@ -108,7 +110,7 @@ bool sdf_format::load(util::random_read &io, uint32_t form_factor, const std::ve raw_track_data.clear(); // Read track - io.read_at(HEADER_SIZE + (heads * track + head) * TOTAL_TRACK_SIZE, &track_data[0], TOTAL_TRACK_SIZE, actual); + read_at(io, HEADER_SIZE + (heads * track + head) * TOTAL_TRACK_SIZE, &track_data[0], TOTAL_TRACK_SIZE); // FIXME: check for errors and premature EOF int sector_count = track_data[0]; diff --git a/src/lib/formats/sol_cas.cpp b/src/lib/formats/sol_cas.cpp index 8c05a756042..1a793fb26a8 100644 --- a/src/lib/formats/sol_cas.cpp +++ b/src/lib/formats/sol_cas.cpp @@ -335,7 +335,7 @@ static int sol20_handle_cassette(int16_t *buffer, const uint8_t *bytes) Generate samples for the tape image ********************************************************************/ -static int sol20_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int sol20_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { return sol20_handle_cassette(buffer, bytes); } diff --git a/src/lib/formats/sorc_cas.cpp b/src/lib/formats/sorc_cas.cpp index 78417e80fc0..316fca7eaf4 100644 --- a/src/lib/formats/sorc_cas.cpp +++ b/src/lib/formats/sorc_cas.cpp @@ -107,7 +107,7 @@ static int sorcerer_handle_cassette(int16_t *buffer, const uint8_t *bytes) Generate samples for the tape image ********************************************************************/ -static int sorcerer_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int sorcerer_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { return sorcerer_handle_cassette(buffer, bytes); } diff --git a/src/lib/formats/spc1000_cas.cpp b/src/lib/formats/spc1000_cas.cpp index e6b3959e2e7..a3830c4a679 100644 --- a/src/lib/formats/spc1000_cas.cpp +++ b/src/lib/formats/spc1000_cas.cpp @@ -89,12 +89,12 @@ static int spc1000_handle_cas(int16_t *buffer, const uint8_t *bytes) Generate samples for the tape image ********************************************************************/ -static int spc1000_tap_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int spc1000_tap_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { return spc1000_handle_tap(buffer, bytes); } -static int spc1000_cas_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int spc1000_cas_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { return spc1000_handle_cas(buffer, bytes); } diff --git a/src/lib/formats/st_dsk.cpp b/src/lib/formats/st_dsk.cpp index 17e0d9efc17..fc105de6fcb 100644 --- a/src/lib/formats/st_dsk.cpp +++ b/src/lib/formats/st_dsk.cpp @@ -79,8 +79,7 @@ bool st_format::load(util::random_read &io, uint32_t form_factor, const std::vec int track_size = sector_count*512; for(int track=0; track < track_count; track++) { for(int head=0; head < head_count; head++) { - size_t actual; - io.read_at((track*head_count + head)*track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ read_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors and premature EOF generate_track(atari_st_fcp_get_desc(track, head, head_count, sector_count), track, head, sectors, sector_count, 100000, image); } @@ -116,8 +115,7 @@ bool st_format::save(util::random_read_write &io, const std::vector<uint32_t> &v for(int track=0; track < track_count; track++) { for(int head=0; head < head_count; head++) { get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata); - size_t actual; - io.write_at((track*head_count + head)*track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ write_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors } } @@ -152,8 +150,7 @@ bool msa_format::supports_save() const noexcept void msa_format::read_header(util::random_read &io, uint16_t &sign, uint16_t §, uint16_t &head, uint16_t &strack, uint16_t &etrack) { uint8_t h[10]; - size_t actual; - io.read_at(0, h, 10, actual); + /*auto const [err, actual] =*/ read_at(io, 0, h, 10); // FIXME: check for errors and premature EOF sign = get_u16be(&h[0]); sect = get_u16be(&h[2]); head = get_u16be(&h[4]); @@ -247,12 +244,11 @@ bool msa_format::load(util::random_read &io, uint32_t form_factor, const std::ve for(int track=strack; track <= etrack; track++) { for(int head=0; head <= heads; head++) { - size_t actual; uint8_t th[2]; - io.read_at(pos, th, 2, actual); + read_at(io, pos, th, 2); // FIXME: check for errors and premature EOF pos += 2; int tsize = get_u16be(th); - io.read_at(pos, sectdata, tsize, actual); + read_at(io, pos, sectdata, tsize); // FIXME: check for errors and premature EOF pos += tsize; if(tsize < track_size) { if(!uncompress(sectdata, tsize, track_size)) @@ -301,8 +297,7 @@ bool msa_format::save(util::random_read_write &io, const std::vector<uint32_t> & if(io.seek(0, SEEK_SET)) return false; - size_t actual; - io.write(header, 10, actual); + write(io, header, 10); // FIXME: check for errors uint8_t sectdata[11*512]; uint8_t compdata[11*512]; @@ -315,13 +310,13 @@ bool msa_format::save(util::random_read_write &io, const std::vector<uint32_t> & if(compress(sectdata, track_size, compdata, csize)) { uint8_t th[2]; put_u16be(th, csize); - io.write(th, 2, actual); - io.write(compdata, csize, actual); + write(io, th, 2); // FIXME: check for errors + write(io, compdata, csize); // FIXME: check for errors } else { uint8_t th[2]; put_u16be(th, track_size); - io.write(th, 2, actual); - io.write(sectdata, track_size, actual); + write(io, th, 2); // FIXME: check for errors + write(io, sectdata, track_size); // FIXME: check for errors } } } diff --git a/src/lib/formats/svi_cas.cpp b/src/lib/formats/svi_cas.cpp index 137eaa27a52..6e9a59e6ae9 100644 --- a/src/lib/formats/svi_cas.cpp +++ b/src/lib/formats/svi_cas.cpp @@ -24,7 +24,7 @@ static int cas_size; // FIXME: global variable prevents multiple instances /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int svi_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int svi_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { int cas_pos, samples_pos, n, i; diff --git a/src/lib/formats/svi_dsk.cpp b/src/lib/formats/svi_dsk.cpp index a60491fb20d..4d326542ad5 100644 --- a/src/lib/formats/svi_dsk.cpp +++ b/src/lib/formats/svi_dsk.cpp @@ -83,8 +83,7 @@ bool svi_format::load(util::random_read &io, uint32_t form_factor, const std::ve sectors[i].bad_crc = false; sectors[i].data = §or_data[sector_offset]; - size_t actual; - io.read(sectors[i].data, sector_size, actual); + /*auto const [err, actual] =*/ read(io, sectors[i].data, sector_size); // FIXME: check for errors and premature EOF sector_offset += sector_size; } @@ -113,8 +112,7 @@ bool svi_format::save(util::random_read_write &io, const std::vector<uint32_t> & for (int i = 0; i < 18; i++) { - size_t actual; - io.write(sectors[i + 1].data(), 128, actual); + /*auto const [err, actual] =*/ write(io, sectors[i + 1].data(), 128); // FIXME: check for errors } // rest are mfm tracks @@ -130,8 +128,7 @@ bool svi_format::save(util::random_read_write &io, const std::vector<uint32_t> & for (int i = 0; i < 17; i++) { - size_t actual; - io.write(sectors[i + 1].data(), 256, actual); + /*auto const [err, actual] =*/ write(io, sectors[i + 1].data(), 256); // FIXME: check for errors } } } diff --git a/src/lib/formats/td0_dsk.cpp b/src/lib/formats/td0_dsk.cpp index a9ba54022a0..956bbd002c7 100644 --- a/src/lib/formats/td0_dsk.cpp +++ b/src/lib/formats/td0_dsk.cpp @@ -20,6 +20,7 @@ #include "multibyte.h" #include <cstring> +#include <tuple> #define BUFSZ 512 // new input buffer @@ -330,8 +331,7 @@ int td0dsk_t::data_read(uint8_t *buf, uint16_t size) if (size > image_size - floppy_file_offset) { size = image_size - floppy_file_offset; } - size_t actual; - floppy_file.read_at(floppy_file_offset, buf, size, actual); + /*auto const [err, actual] =*/ read_at(floppy_file, floppy_file_offset, buf, size); // FIXME: check for errors and premature EOF floppy_file_offset += size; return size; } @@ -810,10 +810,13 @@ const char *td0_format::extensions() const noexcept int td0_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { - size_t actual; uint8_t h[7]; + auto const [err, actual] = read_at(io, 0, h, 7); // FIXME: does this need to read 7 bytes? it only check 2 bytes. Also check for premature EOF. + if(err) + { + return 0; + } - io.read_at(0, h, 7, actual); if(((h[0] == 'T') && (h[1] == 'D')) || ((h[0] == 't') && (h[1] == 'd'))) { return FIFID_SIGN; @@ -823,6 +826,7 @@ int td0_format::identify(util::random_read &io, uint32_t form_factor, const std: bool td0_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { + std::error_condition err; size_t actual; int track_count = 0; int head_count = 0; @@ -830,9 +834,10 @@ bool td0_format::load(util::random_read &io, uint32_t form_factor, const std::ve int offset = 0; const int max_size = 4*1024*1024; // 4MB ought to be large enough for any floppy std::vector<uint8_t> imagebuf(max_size); - uint8_t header[12]; - if(io.read_at(0, header, 12, actual) || actual != 12) + uint8_t header[12]; + std::tie(err, actual) = read_at(io, 0, header, 12); + if(err || (actual != 12)) return false; head_count = header[9]; @@ -849,7 +854,8 @@ bool td0_format::load(util::random_read &io, uint32_t form_factor, const std::ve uint64_t image_size; if(io.length(image_size)) return false; - if(io.read_at(12, &imagebuf[0], image_size - 12, actual) || actual != (image_size - 12)) + std::tie(err, actual) = read_at(io, 12, &imagebuf[0], image_size - 12); + if(err || (actual != (image_size - 12))) return false; } diff --git a/src/lib/formats/thom_cas.cpp b/src/lib/formats/thom_cas.cpp index 02bab6217fe..f6f9e317d4f 100644 --- a/src/lib/formats/thom_cas.cpp +++ b/src/lib/formats/thom_cas.cpp @@ -58,6 +58,8 @@ static uint8_t* to7_k7_bits; and so, is used by emulators that bypass the BIOS routine. It is generally hacked from the raw image more or less by hand (in particular, to by-pass special loaders and copy-protection schemes). + Many of these hacks use emulator-exclusive 6809 instructions and will + not work in MAME. As we run the original BIOS routine, we need to hack back the k7 file into the original stream. @@ -462,7 +464,8 @@ static cassette_image::error mo5_k5_load( cassette_image *cass ) size_t size = cass->image_size( ), pos = 0; int i, sz, sz2, hbit = 0; uint8_t typ, block[264], sum; - int invalid = 0, hbitsize = 0, dcmoto = 0; + int invalid = 0, hbitsize = 0; + bool dcmoto = false, dcmoto_old = false; LOG (( "mo5_k5_load: start conversion, size=%li\n", (long)size )); PRINT (( "mo5_k5_load: open cassette, length: %li bytes\n", (long) size )); @@ -553,7 +556,9 @@ static cassette_image::error mo5_k5_load( cassette_image *cass ) cass->image_read( block, pos, 6 ); if ( ! memcmp( block, "DCMOTO", 6 ) || ! memcmp( block, "DCMO5", 5 ) || ! memcmp( block, "DCMO6", 5 ) ) - dcmoto = 1; + dcmoto = true; + if ( block[0] == 0xdc && block[1] == 0x01 ) + dcmoto_old = true; /* loop over regular blocks */ while ( pos < size ) @@ -574,6 +579,12 @@ static cassette_image::error mo5_k5_load( cassette_image *cass ) pos += 5; } } + else if ( dcmoto_old ) + { + cass->image_read( block, pos, 1 ); + if ( block[0] == 0xdc ) + pos++; + } /* skip 0x01 filler */ for ( sz = 0; pos < size; pos++, sz++ ) @@ -693,7 +704,12 @@ static cassette_image::error mo5_k5_load( cassette_image *cass ) pos -= sz; goto rebounce; } - if ( invalid < 10 && sz > 6 && ( (in == 0x3c && in2 == 0x5a) || (in == 0xc3 && in2 == 0x5a) || (in == 0xc3 && in2 == 0x3c) || (in == 0x87 && in2 == 0x4a) ) ) + if ( invalid < 10 && sz > 6 && + ( (in == 0x3c && in2 == 0x5a) || + (in == 0xc3 && in2 == 0x5a) || + (in == 0xc3 && in2 == 0x3c) || + (in == 0x87 && in2 == 0x4a) || + (in == 0x3c && in2 == 0x55) ) ) // AndroĂŻdes { /* special block found */ K5_FILL_0( 1200 ); diff --git a/src/lib/formats/thom_dsk.cpp b/src/lib/formats/thom_dsk.cpp index ad2e4495ca2..3399a1aa487 100644 --- a/src/lib/formats/thom_dsk.cpp +++ b/src/lib/formats/thom_dsk.cpp @@ -50,6 +50,12 @@ const thomson_525_format::format thomson_525_format::formats[] = { {} }; +int thomson_525_format::get_image_offset(const format &f, int head, int track) const +{ + return (track + (head ? f.track_count : 0)) * compute_track_size(f); +} + + thomson_35_format::thomson_35_format() : wd177x_format(formats) { @@ -70,6 +76,8 @@ const char *thomson_35_format::extensions() const noexcept return "fd"; } +// 1280K .fd images exist but are not supported. They represent a notional type +// of 4-sided disk that can be inserted into 2 drives at once. const thomson_35_format::format thomson_35_format::formats[] = { { floppy_image::FF_35, floppy_image::SSDD, floppy_image::MFM, @@ -90,5 +98,10 @@ const thomson_35_format::format thomson_35_format::formats[] = { {} }; +int thomson_35_format::get_image_offset(const format &f, int head, int track) const +{ + return (track + (head ? f.track_count : 0)) * compute_track_size(f); +} + const thomson_525_format FLOPPY_THOMSON_525_FORMAT; const thomson_35_format FLOPPY_THOMSON_35_FORMAT; diff --git a/src/lib/formats/thom_dsk.h b/src/lib/formats/thom_dsk.h index 52927f15de8..09ea1cbec0c 100644 --- a/src/lib/formats/thom_dsk.h +++ b/src/lib/formats/thom_dsk.h @@ -16,6 +16,8 @@ public: virtual const char *description() const noexcept override; virtual const char *extensions() const noexcept override; + int get_image_offset(const format &f, int head, int track) const override; + private: static const format formats[]; }; @@ -29,6 +31,8 @@ public: virtual const char *description() const noexcept override; virtual const char *extensions() const noexcept override; + int get_image_offset(const format &f, int head, int track) const override; + private: static const format formats[]; }; diff --git a/src/lib/formats/ti99_dsk.cpp b/src/lib/formats/ti99_dsk.cpp index 64b8d81aa67..4cca5bdb996 100644 --- a/src/lib/formats/ti99_dsk.cpp +++ b/src/lib/formats/ti99_dsk.cpp @@ -954,8 +954,7 @@ int ti99_sdf_format::identify(util::random_read &io, uint32_t form_factor, const { // Read first sector (Volume Information Block) ti99vib vib; - size_t actual; - io.read_at(0, &vib, sizeof(ti99vib), actual); + /*auto const [err, actual] =*/ read_at(io, 0, &vib, sizeof(ti99vib)); // FIXME: check for errors and premature EOF // Check from contents if ((vib.id[0]=='D')&&(vib.id[1]=='S')&&(vib.id[2]=='K')) @@ -993,8 +992,7 @@ void ti99_sdf_format::determine_sizes(util::random_read &io, int& cell_size, int // Read first sector ti99vib vib; - size_t actual; - io.read_at(0, &vib, sizeof(ti99vib), actual); + /*auto const [err, actual] =*/ read_at(io, 0, &vib, sizeof(ti99vib)); // FIXME: check for errors and premature EOF // Check from contents if ((vib.id[0]=='D')&&(vib.id[1]=='S')&&(vib.id[2]=='K')) @@ -1079,8 +1077,7 @@ void ti99_sdf_format::load_track(util::random_read &io, uint8_t *sectordata, int int logicaltrack = (head==0)? track : (2*trackcount - track - 1); int position = logicaltrack * get_track_size(sectorcount); - size_t actual; - io.read_at(position, sectordata, sectorcount*SECTOR_SIZE, actual); + /*auto const [err, actual] =*/ read_at(io, position, sectordata, sectorcount*SECTOR_SIZE); // FIXME: check for errors and premature EOF // Interleave and skew int interleave = 7; @@ -1158,8 +1155,7 @@ void ti99_sdf_format::write_track(util::random_read_write &io, uint8_t *sectorda { uint8_t const *const buf = sectordata + i * SECTOR_SIZE; LOGMASKED(LOG_DETAIL, "[ti99_dsk] Writing sector %d (offset %06x)\n", sector[i], sector[i] * SECTOR_SIZE); - size_t actual; - io.write_at(trackoffset + sector[i] * SECTOR_SIZE, buf, SECTOR_SIZE, actual); + /*auto const [err, actual] =*/ write_at(io, trackoffset + sector[i] * SECTOR_SIZE, buf, SECTOR_SIZE); // FIXME: check for errors } } @@ -1246,8 +1242,7 @@ int ti99_tdf_format::identify(util::random_read &io, uint32_t form_factor, const LOGMASKED(LOG_INFO, "[ti99_dsk] Image file length matches TDF\n"); // Fetch track 0 - size_t actual; - io.read_at(0, fulltrack, get_track_size(sector_count), actual); + /*auto const [err, actual] =*/ read_at(io, 0, fulltrack, get_track_size(sector_count)); // FIXME: check for errors and premature EOF if (sector_count == 9) { @@ -1358,12 +1353,11 @@ void ti99_tdf_format::determine_sizes(util::random_read &io, int& cell_size, int */ void ti99_tdf_format::load_track(util::random_read &io, uint8_t *sectordata, int *sector, int *secoffset, int head, int track, int sectorcount, int trackcount) const { - size_t actual; uint8_t fulltrack[12544]; // space for a full TDF track // Read beginning of track 0. We need this to get the first gap, according // to the format - io.read_at(0, fulltrack, 100, actual); + read_at(io, 0, fulltrack, 100); // FIXME: check for errors and premature EOF int offset = 0; int tracksize = get_track_size(sectorcount); @@ -1404,7 +1398,7 @@ void ti99_tdf_format::load_track(util::random_read &io, uint8_t *sectordata, int int base = (head * trackcount + track) * tracksize; int position = 0; - io.read_at(base, fulltrack, tracksize, actual); + read_at(io, base, fulltrack, tracksize); // FIXME: check for errors and premature EOF for (int i=0; i < sectorcount; i++) { @@ -1506,8 +1500,7 @@ void ti99_tdf_format::write_track(util::random_read_write &io, uint8_t *sectorda for (int i=0; i < param[WGAP3]; i++) trackdata[pos++] = param[WGAPBYTE]; } for (int i=0; i < param[WGAP4]; i++) trackdata[pos++] = param[WGAPBYTE]; - size_t actual; - io.write_at(offset, trackdata, get_track_size(sector_count), actual); + /*auto const [err, actual] =*/ write_at(io, offset, trackdata, get_track_size(sector_count)); // FIXME: check for errors } int ti99_tdf_format::get_track_size(int sector_count) const diff --git a/src/lib/formats/trd_dsk.cpp b/src/lib/formats/trd_dsk.cpp index 4ec6d4a962a..cf1a7f00445 100644 --- a/src/lib/formats/trd_dsk.cpp +++ b/src/lib/formats/trd_dsk.cpp @@ -12,6 +12,8 @@ #include "ioprocs.h" +#include <tuple> + trd_format::trd_format() : wd177x_format(formats) { @@ -51,16 +53,19 @@ int trd_format::find_size(util::random_read &io, uint32_t form_factor, const std { index = i; // at least size match, save it for the case if there will be no exact matches + std::error_condition err; size_t actual; uint8_t sectdata[0x100]; if (f.encoding == floppy_image::MFM) - io.read_at(0x800, sectdata, 0x100, actual); + std::tie(err, actual) = read_at(io, 0x800, sectdata, 0x100); else { - io.read_at(0x100, sectdata, 0x100, actual); + std::tie(err, actual) = read_at(io, 0x100, sectdata, 0x100); for (int i = 0; i < 0x100; i++) sectdata[i] ^= 0xff; } + if (err || (0x100 != actual)) + continue; uint8_t disktype = sectdata[0xe3]; // 16 - DS80, 17 - DS40, 18 - SS80, 19 - SS40 if (disktype < 0x16 || disktype > 0x19) diff --git a/src/lib/formats/trs80_dsk.cpp b/src/lib/formats/trs80_dsk.cpp index 511d9fdfbf1..f670273a5af 100644 --- a/src/lib/formats/trs80_dsk.cpp +++ b/src/lib/formats/trs80_dsk.cpp @@ -146,9 +146,9 @@ int jv3_format::identify(util::random_read &io, uint32_t form_factor, const std: if (image_size < 0x2200) return 0; // too small, silent return - std::vector<uint8_t> data(image_size); - size_t actual; - io.read_at(0, data.data(), image_size, actual); + auto const [err, data, actual] = read_at(io, 0, image_size); + if (err || (actual != image_size)) + return 0; const uint32_t entries = 2901; const uint32_t header_size = entries *3 +1; @@ -238,9 +238,9 @@ bool jv3_format::load(util::random_read &io, uint32_t form_factor, const std::ve uint64_t image_size; if (io.length(image_size)) return false; - std::vector<uint8_t> data(image_size); - size_t actual; - io.read_at(0, data.data(), data.size(), actual); + auto const [err, data, actual] = read_at(io, 0, image_size); + if (err || (actual != image_size)) + return 0; const uint32_t entries = 2901; const uint32_t header_size = entries *3 +1; bool is_dd = false, is_ds = false; @@ -365,13 +365,14 @@ bool jv3_format::save(util::random_read_write &io, const std::vector<uint32_t> & { // If the disk already exists, find out if it's writable uint64_t image_size; - if (!io.length(image_size)) + if (!io.length(image_size) && (image_size >= 0x2200)) { - std::vector<uint8_t> data(image_size); - size_t actual; - io.read_at(0, data.data(), data.size(), actual); - if ((data.size() >= 0x2200) && (data[0x21ff] == 0)) - return false; // disk is readonly + uint8_t flag; + auto const [err, actual] = read_at(io, 0x21ff, &flag, 1); + if (err || (1 != actual)) // TODO: should we save over the top if we couldn’t read the read-only flag? + return false; + if (flag == 0) + return false; // disk is read-only } } @@ -413,8 +414,7 @@ bool jv3_format::save(util::random_read_write &io, const std::vector<uint32_t> & header[sect_ptr++] = track; header[sect_ptr++] = i; header[sect_ptr++] = head ? 0x10 : 0; - size_t actual; - io.write_at(data_ptr, &dummy[0], 256, actual); + /*auto const [err, actual] =*/ write_at(io, data_ptr, &dummy[0], 256); // FIXME: check for errors data_ptr += 256; } } @@ -435,16 +435,14 @@ bool jv3_format::save(util::random_read_write &io, const std::vector<uint32_t> & flags |= (sectors[i].size() >> 8) ^1; flags |= head ? 0x10 : 0; header[sect_ptr++] = flags; - size_t actual; - io.write_at(data_ptr, sectors[i].data(), sectors[i].size(), actual); + /*auto const [err, actual] =*/ write_at(io, data_ptr, sectors[i].data(), sectors[i].size()); // FIXME: check for errors data_ptr += sectors[i].size(); } } } } // Save the header - size_t actual; - io.write_at(0, header, 0x2200, actual); + /*auto const [err, actual] =*/ write_at(io, 0, header, 0x2200); // FIXME: check for errors return true; } diff --git a/src/lib/formats/trs_cas.cpp b/src/lib/formats/trs_cas.cpp index 8b1ede7ec81..2648c536320 100644 --- a/src/lib/formats/trs_cas.cpp +++ b/src/lib/formats/trs_cas.cpp @@ -174,7 +174,7 @@ static int trs80m3_handle_cas(int16_t *buffer, const uint8_t *casdata) /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int trs80_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int trs80_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { if (cas_size && (bytes[0] == 0x55)) return trs80m3_handle_cas( buffer, bytes ); diff --git a/src/lib/formats/tzx_cas.cpp b/src/lib/formats/tzx_cas.cpp index 4d14e567453..f758c1b31e6 100644 --- a/src/lib/formats/tzx_cas.cpp +++ b/src/lib/formats/tzx_cas.cpp @@ -72,7 +72,7 @@ static void toggle_wave_data(void) static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen ) { - int pos = sizeof(TZX_HEADER) + 2; + uint32_t pos = sizeof(TZX_HEADER) + 2; int max_block_count = INITIAL_MAX_BLOCK_COUNT; int loopcount = 0, loopoffset = 0; blocks = (uint8_t**)malloc(max_block_count * sizeof(uint8_t*)); @@ -192,7 +192,14 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen ) break; } - block_count++; + if (pos > caslen) + { + LOG_FORMATS("Block %d(ID=%d) with wrong length discarded\n", block_count, blocktype); + } + else + { + block_count++; + } } } @@ -779,7 +786,7 @@ cleanup: return -1; } -static int tzx_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) +static int tzx_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { int16_t *p = buffer; int size = 0; @@ -788,7 +795,7 @@ static int tzx_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) return size; } -static int cdt_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) +static int cdt_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { int16_t *p = buffer; int size = 0; @@ -797,72 +804,114 @@ static int cdt_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) return size; } -static int tap_cas_to_wav_size( const uint8_t *casdata, int caslen ) +static int tap_cas_to_wav_size(const uint8_t *casdata, int caslen) { + /* + TAP Header: + | 0 | W | Length of Data Block (N) | -+ + | 2 | B | Flag Byte | -+ + | 3 | B*(N-2) | Data | | Data Block + | N+1 | B | XOR Checksum of [2..N] | -+ + */ + int size = 0; const uint8_t *p = casdata; - while (p < casdata + caslen) + while (caslen > 2) { int data_size = get_u16le(&p[0]); - int pilot_length = (p[2] == 0x00) ? 8063 : 3223; - LOG_FORMATS("tap_cas_to_wav_size: Handling TAP block containing 0x%X bytes", data_size); p += 2; + caslen -= 2; + if (caslen < data_size) + { + LOG_FORMATS("tap_cas_to_wav_size: Requested 0x%X bytes but only 0x%X available.\n", data_size, caslen); + data_size = caslen; + } + caslen -= data_size; + + // Data Block + LOG_FORMATS("tap_cas_to_wav_size: Handling TAP block containing 0x%X bytes", data_size); + const int pilot_length = (p[0] == 0x00) ? 8063 : 3223; size += tzx_cas_handle_block(nullptr, p, 1000, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8); - LOG_FORMATS(", total size is now: %d\n", size); + LOG_FORMATS(", total size is now: %d", size); + + // Validate Checksum + const uint8_t checksum = p[data_size - 1]; + LOG_FORMATS(", checksum 0x%X\n", checksum); + uint8_t check = 0x00; + for(int i = 0; i < (data_size - 1); i++) + { + check ^= p[i]; + } + if (check != checksum) + { + osd_printf_warning("tap_cas_to_wav_size: wrong checksum 0x%X\n", check); + } + p += data_size; } return size; } -static int tap_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) +static int tap_cas_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int bytes_length) { int16_t *p = buffer; int size = 0; - while (size < length) + int block = 0; + while (bytes_length > 2) { int data_size = get_u16le(&bytes[0]); - int pilot_length = (bytes[2] == 0x00) ? 8063 : 3223; - LOG_FORMATS("tap_cas_fill_wave: Handling TAP block containing 0x%X bytes\n", data_size); bytes += 2; + bytes_length -= 2; + + LOG_FORMATS("tap_cas_fill_wave: Handling TAP block containing 0x%X bytes\n", data_size); + if (bytes_length < data_size) + { + osd_printf_warning("BAD Image: block #%d required %d byte(s), but only %d available\n", block, data_size, bytes_length); + data_size = bytes_length; // Take as much as we can. + } + bytes_length -= data_size; + + int pilot_length = (bytes[0] == 0x00) ? 8063 : 3223; size += tzx_cas_handle_block(&p, bytes, 1000, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8); bytes += data_size; + ++block; } return size; } static const cassette_image::LegacyWaveFiller tzx_legacy_fill_wave = { - tzx_cas_fill_wave, /* fill_wave */ - -1, /* chunk_size */ - 0, /* chunk_samples */ - tzx_cas_to_wav_size, /* chunk_sample_calc */ - TZX_WAV_FREQUENCY, /* sample_frequency */ - 0, /* header_samples */ - 0 /* trailer_samples */ + tzx_cas_fill_wave, // fill_wave + -1, // chunk_size + 0, // chunk_samples + tzx_cas_to_wav_size, // chunk_sample_calc + TZX_WAV_FREQUENCY, // sample_frequency + 0, // header_samples + 0, // trailer_samples }; static const cassette_image::LegacyWaveFiller tap_legacy_fill_wave = { - tap_cas_fill_wave, /* fill_wave */ - -1, /* chunk_size */ - 0, /* chunk_samples */ - tap_cas_to_wav_size, /* chunk_sample_calc */ - TZX_WAV_FREQUENCY, /* sample_frequency */ - 0, /* header_samples */ - 0 /* trailer_samples */ + tap_cas_fill_wave, // fill_wave + -1, // chunk_size + 0, // chunk_samples + tap_cas_to_wav_size, // chunk_sample_calc + TZX_WAV_FREQUENCY, // sample_frequency + 0, // header_samples + 0 // trailer_samples }; static const cassette_image::LegacyWaveFiller cdt_legacy_fill_wave = { - cdt_cas_fill_wave, /* fill_wave */ - -1, /* chunk_size */ - 0, /* chunk_samples */ - tzx_cas_to_wav_size, /* chunk_sample_calc */ - TZX_WAV_FREQUENCY, /* sample_frequency */ - 0, /* header_samples */ - 0 /* trailer_samples */ + cdt_cas_fill_wave, // fill_wave + -1, // chunk_size + 0, // chunk_samples + tzx_cas_to_wav_size, // chunk_sample_calc + TZX_WAV_FREQUENCY, // sample_frequency + 0, // header_samples + 0 // trailer_samples }; static cassette_image::error tzx_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) diff --git a/src/lib/formats/uef_cas.cpp b/src/lib/formats/uef_cas.cpp index f284af43035..02f150915a7 100644 --- a/src/lib/formats/uef_cas.cpp +++ b/src/lib/formats/uef_cas.cpp @@ -247,7 +247,7 @@ static int16_t* uef_cas_fill_bit( uint8_t loops, int16_t *buffer, bool bit ) return buffer; } -static int uef_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) +static int uef_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { if ( bytes[0] == 0x1f && bytes[1] == 0x8b ) { if ( gz_ptr == nullptr ) { @@ -264,16 +264,16 @@ static int uef_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) int chunk_type = get_u16le( &bytes[pos] ); int chunk_length = get_u32le( &bytes[pos+2] ); - uint32_t baud_length, j; - uint8_t i, *c; + uint32_t baud_length; + const uint8_t *c; pos += 6; switch( chunk_type ) { case 0x0100: /* implicit start/stop bit data block */ case 0x0104: // used by atom dumps, looks like normal data - for( j = 0; j < chunk_length; j++ ) { + for( uint32_t j = 0; j < chunk_length; j++ ) { uint8_t byte = bytes[pos+j]; p = uef_cas_fill_bit( loops, p, 0 ); - for( i = 0; i < 8; i++ ) { + for( uint8_t i = 0; i < 8; i++ ) { p = uef_cas_fill_bit( loops, p, (byte >> i) & 1 ); } p = uef_cas_fill_bit( loops, p, 1 ); @@ -285,11 +285,10 @@ static int uef_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) LOG_FORMATS( "Unsupported chunk type: %04x\n", chunk_type ); break; case 0x0102: /* explicit tape data block */ - j = ( chunk_length * 10 ) - bytes[pos]; c = bytes + pos; - while( j ) { + for( uint32_t j = ( chunk_length * 10 ) - bytes[pos]; j; ) { uint8_t byte = *c; - for( i = 0; i < 8 && i < j; i++ ) { + for( uint8_t i = 0; i < 8 && i < j; i++ ) { p = uef_cas_fill_bit( loops, p, (byte >> i) & 1 ); j--; } @@ -311,7 +310,7 @@ static int uef_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) } break; case 0x0116: /* floating point gap */ - for( baud_length = (get_uef_float(bytes+pos)*UEF_WAV_FREQUENCY); baud_length; baud_length-- ) { + for( baud_length = (get_uef_float(bytes + pos)*UEF_WAV_FREQUENCY); baud_length; baud_length-- ) { *p = WAVE_NULL; p++; length -= 1; } diff --git a/src/lib/formats/uniflex_dsk.cpp b/src/lib/formats/uniflex_dsk.cpp index 0143bf8cf0e..063d3c877f9 100644 --- a/src/lib/formats/uniflex_dsk.cpp +++ b/src/lib/formats/uniflex_dsk.cpp @@ -53,8 +53,9 @@ int uniflex_format::find_size(util::random_read &io, uint32_t form_factor, const // Look at the SIR sector, the second sector. uint8_t sir[192]; - size_t actual; - io.read_at(1 * 512, sir, sizeof(sir), actual); + auto const [err, actual] = read_at(io, 1 * 512, sir, sizeof(sir)); // FIXME: check for premature EOF + if (err) + return -1; uint16_t fdn_block_count = get_u16be(&sir[0x10]); uint32_t last_block_number = get_u24be(&sir[0x12]); diff --git a/src/lib/formats/upd765_dsk.cpp b/src/lib/formats/upd765_dsk.cpp index f3f08f37b4e..589ab1e1666 100644 --- a/src/lib/formats/upd765_dsk.cpp +++ b/src/lib/formats/upd765_dsk.cpp @@ -231,8 +231,7 @@ bool upd765_format::load(util::random_read &io, uint32_t form_factor, const std: for(int track=0; track < f.track_count; track++) for(int head=0; head < f.head_count; head++) { build_sector_description(f, sectdata, sectors, track, head); - size_t actual; - io.read_at((track*f.head_count + head)*track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ read_at(io, (track*f.head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors and premature EOF generate_track(desc, track, head, sectors, f.sector_count, total_size, image); } @@ -345,20 +344,19 @@ bool upd765_format::save(util::random_read_write &io, const std::vector<uint32_t if(chosen_candidate == -1) chosen_candidate = 0; - const format &f = formats[chosen_candidate]; int track_size = compute_track_size(f); uint8_t sectdata[40*512]; desc_s sectors[40]; - for(int track=0; track < f.track_count; track++) + for(int track=0; track < f.track_count; track++) { for(int head=0; head < f.head_count; head++) { build_sector_description(f, sectdata, sectors, track, head); extract_sectors(image, f, sectors, track, head); - size_t actual; - io.write_at((track*f.head_count + head)*track_size, sectdata, track_size, actual); + /*auto const [err, actual] =*/ write_at(io, (track*f.head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors } + } return true; } diff --git a/src/lib/formats/vdk_dsk.cpp b/src/lib/formats/vdk_dsk.cpp index e47d3502981..02e25ec5df9 100644 --- a/src/lib/formats/vdk_dsk.cpp +++ b/src/lib/formats/vdk_dsk.cpp @@ -36,9 +36,10 @@ const char *vdk_format::extensions() const noexcept int vdk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const { - size_t actual; uint8_t id[2]; - io.read_at(0, id, 2, actual); + auto const [err, actual] = read_at(io, 0, id, 2); + if (err || (2 != actual)) + return 0; if (id[0] == 'd' && id[1] == 'k') return FIFID_SIGN; @@ -48,12 +49,11 @@ int vdk_format::identify(util::random_read &io, uint32_t form_factor, const std: bool vdk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { - size_t actual; if (io.seek(0, SEEK_SET)) return false; uint8_t header[0x100]; - io.read(header, 0x100, actual); + read(io, header, 0x100); // FIXME: check for errors and premature EOF int const header_size = header[3] * 0x100 + header[2]; int const track_count = header[8]; @@ -81,7 +81,7 @@ bool vdk_format::load(util::random_read &io, uint32_t form_factor, const std::ve sectors[i].bad_crc = false; sectors[i].data = §or_data[sector_offset]; - io.read(sectors[i].data, SECTOR_SIZE, actual); + read(io, sectors[i].data, SECTOR_SIZE); // FIXME: check for errors and premature EOF sector_offset += SECTOR_SIZE; } @@ -95,7 +95,6 @@ bool vdk_format::load(util::random_read &io, uint32_t form_factor, const std::ve bool vdk_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const { - size_t actual; if (io.seek(0, SEEK_SET)) return false; @@ -118,7 +117,7 @@ bool vdk_format::save(util::random_read_write &io, const std::vector<uint32_t> & header[10] = 0; header[11] = 0; - io.write(header, sizeof(header), actual); + write(io, header, sizeof(header)); // FIXME: check for errors // write disk data for (int track = 0; track < track_count; track++) @@ -129,7 +128,7 @@ bool vdk_format::save(util::random_read_write &io, const std::vector<uint32_t> & auto sectors = extract_sectors_from_bitstream_mfm_pc(bitstream); for (int i = 0; i < SECTOR_COUNT; i++) - io.write(sectors[FIRST_SECTOR_ID + i].data(), SECTOR_SIZE, actual); + write(io, sectors[FIRST_SECTOR_ID + i].data(), SECTOR_SIZE); // FIXME: check for errors } } diff --git a/src/lib/formats/vg5k_cas.cpp b/src/lib/formats/vg5k_cas.cpp index fcdb9ba7f02..724a4889229 100644 --- a/src/lib/formats/vg5k_cas.cpp +++ b/src/lib/formats/vg5k_cas.cpp @@ -184,7 +184,7 @@ static int vg5k_handle_tap(int16_t *buffer, const uint8_t *casdata) /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int vg5k_k7_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int vg5k_k7_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { return vg5k_handle_tap(buffer, bytes); } diff --git a/src/lib/formats/vgi_dsk.cpp b/src/lib/formats/vgi_dsk.cpp index ae6cd53018e..a86ffdefb75 100644 --- a/src/lib/formats/vgi_dsk.cpp +++ b/src/lib/formats/vgi_dsk.cpp @@ -18,8 +18,8 @@ http://www.bitsavers.org/pdf/micropolis/metafloppy/1084-01_1040_1050_Users_Manua #include <cstring> -static const int track_size = 100'000; -static const int half_bitcell_size = 2000; +static constexpr int TRACK_SIZE = 100'000; +static constexpr int HALF_BITCELL_SIZE = 2000; micropolis_vgi_format::micropolis_vgi_format() : floppy_image_format_t() { @@ -32,10 +32,10 @@ struct format { }; static const format formats[] = { - {1, 35, floppy_image::SSDD}, // MOD-I - {2, 35, floppy_image::DSDD}, - {1, 77, floppy_image::SSQD}, // MOD-II - {2, 77, floppy_image::DSQD}, + {1, 35, floppy_image::SSDD16}, // MOD-I + {2, 35, floppy_image::DSDD16}, + {1, 77, floppy_image::SSQD16}, // MOD-II + {2, 77, floppy_image::DSQD16}, {} }; @@ -76,14 +76,14 @@ bool micropolis_vgi_format::load(util::random_read &io, uint32_t form_factor, co for (int track = 0; track < fmt.track_count; track++) { for (int sector = 0; sector < 16; sector++) { for (int i = 0; i < 40; i++) - mfm_w(buf, 8, 0, half_bitcell_size); - std::size_t actual; - if (io.read(sector_bytes, std::size(sector_bytes), actual)) + mfm_w(buf, 8, 0, HALF_BITCELL_SIZE); + auto const [err, actual] = read(io, sector_bytes, std::size(sector_bytes)); + if (err || (actual != std::size(sector_bytes))) return false; for (int i = 0; i < std::size(sector_bytes); i++) - mfm_w(buf, 8, sector_bytes[i], half_bitcell_size); - while (buf.size() < track_size/16 * (sector+1)) - mfm_w(buf, 8, 0, half_bitcell_size); + mfm_w(buf, 8, sector_bytes[i], HALF_BITCELL_SIZE); + while (buf.size() < TRACK_SIZE/16 * (sector+1)) + mfm_w(buf, 8, 0, HALF_BITCELL_SIZE); } generate_track_from_levels(track, head, buf, 0, image); buf.clear(); @@ -116,9 +116,9 @@ bool micropolis_vgi_format::save(util::random_read_write &io, const std::vector< uint8_t sector_bytes[275]; for (int head = 0; head < fmt.head_count; head++) { for (int track = 0; track < fmt.track_count; track++) { - std::vector<bool> bitstream = generate_bitstream_from_track(track, head, half_bitcell_size, image); + std::vector<bool> bitstream = generate_bitstream_from_track(track, head, HALF_BITCELL_SIZE, image); for (int sector = 0; sector < 16; sector++) { - int sector_start = track_size/16 * sector; + int sector_start = TRACK_SIZE/16 * sector; uint32_t pos = sector_start + 512 - 16; uint16_t shift_reg = 0; while (pos < sector_start + 60*16 && pos < bitstream.size()) { @@ -134,8 +134,8 @@ bool micropolis_vgi_format::save(util::random_read_write &io, const std::vector< memset(sector_bytes, 0, std::size(sector_bytes)); } - std::size_t actual; - if (io.write(sector_bytes, std::size(sector_bytes), actual)) + auto const [err, actual] = write(io, sector_bytes, std::size(sector_bytes)); + if (err) return false; } } diff --git a/src/lib/formats/victor9k_dsk.cpp b/src/lib/formats/victor9k_dsk.cpp index a0fdaedab5a..803c75303d8 100644 --- a/src/lib/formats/victor9k_dsk.cpp +++ b/src/lib/formats/victor9k_dsk.cpp @@ -136,12 +136,12 @@ const char *victor9k_format::extensions() const noexcept int victor9k_format::find_size(util::random_read &io) { uint64_t size; - if(io.length(size)) + if (io.length(size)) return -1; - for(int i=0; formats[i].sector_count; i++) { + for (int i = 0; formats[i].sector_count; i++) { const format &f = formats[i]; - if(size == (uint32_t) f.sector_count*f.sector_base_size) + if(size == uint32_t(f.sector_count*f.sector_base_size)) return i; } @@ -155,7 +155,7 @@ int victor9k_format::find_size(util::random_read &io, uint32_t form_factor) int victor9k_format::identify(const floppy_image &image) { - for(int i=0; formats[i].form_factor; i++) { + for (int i = 0; formats[i].form_factor; i++) { const format &f = formats[i]; if(f.variant == image.get_variant()) return i; @@ -291,7 +291,7 @@ void victor9k_format::build_sector_description(const format &f, uint8_t *sectdat bool victor9k_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const { int const type = find_size(io, form_factor); - if(type == -1) + if (type == -1) return false; const format &f = formats[type]; @@ -300,15 +300,12 @@ bool victor9k_format::load(util::random_read &io, uint32_t form_factor, const st f.head_count, f.sector_count); uint64_t size; - if(io.length(size)) + if (io.length(size)) return false; - std::vector<uint8_t> img; - try { img.resize(size); } - catch (...) { return false; } - - size_t actual; - io.read_at(0, &img[0], size, actual); + auto const [err, img, actual] = read_at(io, 0, size); + if (err || (actual != size)) + return false; log_boot_sector(&img[0]); @@ -449,17 +446,17 @@ bool victor9k_format::save(util::random_read_write &io, const std::vector<uint32 { int type = victor9k_format::identify(image); uint64_t size; - io.length(size); + io.length(size); // FIXME: check for errors osd_printf_verbose("save type: %01d, size: %d\n", type, size); - if(type == -1) + if (type == -1) return false; const format &f = formats[type]; osd_printf_verbose("save Heads: %01d Tracks: %02d Sectors/track head[1]track[1]: %02d\n ", f.head_count, f.track_count, sectors_per_track[1][1]); - for(int head=0; head < f.head_count; head++) { + for (int head = 0; head < f.head_count; head++) { for(int track=0; track < f.track_count; track++) { int sector_count = sectors_per_track[head][track]; int track_size = compute_track_size(f, head, track); @@ -471,8 +468,7 @@ bool victor9k_format::save(util::random_read_write &io, const std::vector<uint32 build_sector_description(f, sectdata, 0, sectors, sector_count); extract_sectors(image, f, sectors, track, head, sector_count); - size_t actual; - io.write_at(offset, sectdata, track_size, actual); + /*auto const [err, actual] =*/ write_at(io, offset, sectdata, track_size); // FIXME: check for errors } } @@ -481,7 +477,6 @@ bool victor9k_format::save(util::random_read_write &io, const std::vector<uint32 void victor9k_format::extract_sectors(const floppy_image &image, const format &f, desc_s *sdesc, int track, int head, int sector_count) { - // Extract the sectors auto bitstream = generate_bitstream_from_track(track, head, cell_size[speed_zone[head][track]], image); auto sectors = extract_sectors_from_bitstream_victor_gcr5(bitstream); @@ -493,14 +488,14 @@ void victor9k_format::extract_sectors(const floppy_image &image, const format &f } } - for(int i=0; i<sector_count; i++) { + for (int i = 0; i<sector_count; i++) { desc_s &ds = sdesc[i]; const auto &data = sectors[ds.sector_id]; osd_printf_verbose("Head: %01d TracK: %02d Total Sectors: %02d Current Sector: %02d ", head, track, sector_count, i); - if(data.empty()) { + if (data.empty()) { memset((uint8_t *)ds.data, 0, ds.size); - } else if(data.size() < ds.size) { + } else if (data.size() < ds.size) { memcpy((uint8_t *)ds.data, data.data(), data.size() - 1); memset((uint8_t *)ds.data + data.size() - 1, 0, data.size() - ds.size); osd_printf_verbose("data.size(): %01d\n", data.size()); diff --git a/src/lib/formats/vt_cas.cpp b/src/lib/formats/vt_cas.cpp index 046e97880d8..2052647bfd8 100644 --- a/src/lib/formats/vt_cas.cpp +++ b/src/lib/formats/vt_cas.cpp @@ -8,7 +8,7 @@ #define SILENCE 8000 -static int generic_fill_wave(int16_t *buffer, int length, uint8_t *code, int bitsamples, int bytesamples, int lo, int16_t *(*fill_wave_byte)(int16_t *buffer, int byte)) +static int generic_fill_wave(int16_t *buffer, int length, const uint8_t *code, int bitsamples, int bytesamples, int lo, int16_t *(*fill_wave_byte)(int16_t *buffer, int byte)) { static int nullbyte; @@ -90,7 +90,7 @@ static int16_t *vtech1_fill_wave_byte(int16_t *buffer, int byte) return buffer; } -static int vtech1_cassette_fill_wave(int16_t *buffer, int length, uint8_t *code) +static int vtech1_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *code, int) { return generic_fill_wave(buffer, length, code, V1_BITSAMPLES, V1_BYTESAMPLES, V1_LO, vtech1_fill_wave_byte); } @@ -185,7 +185,7 @@ static int16_t *vtech2_fill_wave_byte(int16_t *buffer, int byte) return buffer; } -static int vtech2_cassette_fill_wave(int16_t *buffer, int length, uint8_t *code) +static int vtech2_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *code, int) { return generic_fill_wave(buffer, length, code, VT2_BITSAMPLES, VT2_BYTESAMPLES, VT2_LO, vtech2_fill_wave_byte); } diff --git a/src/lib/formats/vt_dsk.cpp b/src/lib/formats/vt_dsk.cpp index 202ec784187..da7e450b673 100644 --- a/src/lib/formats/vt_dsk.cpp +++ b/src/lib/formats/vt_dsk.cpp @@ -21,6 +21,7 @@ void vtech_common_format::wbit(std::vector<uint32_t> &buffer, uint32_t &pos, bool bit) { + buffer.push_back(pos | floppy_image::MG_F); if(bit) { pos += 2237; buffer.push_back(pos | floppy_image::MG_F); @@ -35,13 +36,13 @@ void vtech_common_format::wbyte(std::vector<uint32_t> &buffer, uint32_t &pos, ui wbit(buffer, pos, (byte >> i) & 1); } -void vtech_common_format::image_to_flux(const std::vector<uint8_t> &bdata, floppy_image &image) +void vtech_common_format::image_to_flux(const uint8_t *bdata, size_t size, floppy_image &image) { static const uint8_t sector_map[16] = { 0x0, 0xb, 0x6, 0x1, 0xc, 0x7, 0x2, 0xd, 0x8, 0x3, 0xe, 0x9, 0x4, 0xf, 0xa, 0x5 }; - for(int track = 0; track != 40; track ++) { + for(int track = 0; track != 40; track++) { uint32_t pos = 0; std::vector<uint32_t> &buffer = image.get_buffer(track, 0); buffer.clear(); @@ -49,7 +50,7 @@ void vtech_common_format::image_to_flux(const std::vector<uint8_t> &bdata, flopp // One window of pad at the start to avoid problems with the write splice wbit(buffer, pos, 0); - for(int sector = 0; sector != 16; sector ++) { + for(int sector = 0; sector != 16; sector++) { uint8_t sid = sector_map[sector]; for(int i=0; i != 7; i++) wbyte(buffer, pos, 0x80); @@ -69,7 +70,7 @@ void vtech_common_format::image_to_flux(const std::vector<uint8_t> &bdata, flopp wbyte(buffer, pos, 0xe7); wbyte(buffer, pos, 0xfe); uint16_t chk = 0; - const uint8_t *src = bdata.data() + 16*128*track + 128*sid; + const uint8_t *src = &bdata[16*128*track + 128*sid]; for(int i=0; i != 128; i++) { chk += src[i]; wbyte(buffer, pos, src[i]); @@ -106,7 +107,7 @@ std::vector<uint8_t> vtech_common_format::flux_to_image(const floppy_image &imag for(;;) { int npos = cpos; for(;;) { - npos ++; + npos++; if(npos == sz) npos = 0; if((buffer[npos] & floppy_image::MG_MASK) == floppy_image::MG_F) @@ -115,7 +116,11 @@ std::vector<uint8_t> vtech_common_format::flux_to_image(const floppy_image &imag int dt = (buffer[npos] & floppy_image::TIME_MASK) - (buffer[cpos] & floppy_image::TIME_MASK); if(dt < 0) cpos += 200000000; - bitstream.push_back(dt < 9187 - 143); + int t = dt >= 9187 - 143 ? 0 : + dt >= 2237 - 143 && dt <= 2237 + 143 ? 1 : + 2; + if(t <= 1) + bitstream.push_back(t); if(npos <= cpos) break; cpos = npos; @@ -135,7 +140,7 @@ std::vector<uint8_t> vtech_common_format::flux_to_image(const floppy_image &imag buf = (buf << 1) | bitstream[sz-64+i]; for(;;) { buf = (buf << 1) | bitstream[pos]; - count ++; + count++; switch(mode) { case 0: // idle if(buf == 0x80808000fee718c3) @@ -219,7 +224,7 @@ const char *vtech_dsk_format::description() const noexcept const char *vtech_dsk_format::extensions() const noexcept { - return "dsk"; + return "dsk,dvz"; } int vtech_bin_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const @@ -243,15 +248,15 @@ int vtech_dsk_format::identify(util::random_read &io, uint32_t form_factor, cons if(size < 256) return 0; - std::vector<uint8_t> bdata(size); - size_t actual; - io.read_at(0, bdata.data(), size, actual); + auto const [err, bdata, actual] = read_at(io, 0, size); + if(err || (actual != size)) + return 0; // Structurally validate the presence of sector headers and data int count_sh = 0, count_sd = 0; uint64_t buf = 0; - for(uint8_t b : bdata) { - buf = (buf << 8) | b; + for(size_t i = 0; size > i; ++i) { + buf = (buf << 8) | bdata[i]; if(buf == 0x80808000fee718c3) count_sh++; else if(buf == 0x80808000c318e7fe) @@ -267,11 +272,11 @@ bool vtech_bin_format::load(util::random_read &io, uint32_t form_factor, const s if(io.length(size) || (size != 40*16*256)) return false; - std::vector<uint8_t> bdata(size); - size_t actual; - io.read_at(0, bdata.data(), size, actual); + auto const [err, bdata, actual] = read_at(io, 0, size); + if(err || (actual != size)) + return false; - image_to_flux(bdata, image); + image_to_flux(bdata.get(), size, image); image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD); return true; } @@ -281,9 +286,9 @@ bool vtech_dsk_format::load(util::random_read &io, uint32_t form_factor, const s uint64_t size; if(io.length(size)) return false; - std::vector<uint8_t> bdata(size); - size_t actual; - io.read_at(0, bdata.data(), size, actual); + auto const [err, bdata, actual] = read_at(io, 0, size); + if(err || (actual != size)) + return false; std::vector<uint8_t> bdatax(128*16*40, 0); @@ -293,9 +298,9 @@ bool vtech_dsk_format::load(util::random_read &io, uint32_t form_factor, const s uint64_t buf = 0; uint8_t *dest = nullptr; - for(uint8_t b : bdata) { - buf = (buf << 8) | b; - count ++; + for(size_t i = 0; size > i; ++i) { + buf = (buf << 8) | bdata[i]; + count++; switch(mode) { case 0: // idle if(buf == 0x80808000fee718c3) @@ -305,14 +310,14 @@ bool vtech_dsk_format::load(util::random_read &io, uint32_t form_factor, const s case 1: // sector header if(count == 3) { - uint8_t trk = buf >> 16; - uint8_t sector = buf >> 8; - uint8_t chk = buf; + const uint8_t trk = buf >> 16; + const uint8_t sector = buf >> 8; + const uint8_t chk = buf; if(chk != sector + trk) { mode = 0; break; } - dest = bdatax.data() + 128*16*trk + sector*128; + dest = &bdatax[128*16*trk + sector*128]; checksum = 0; mode = 2; } @@ -328,7 +333,7 @@ bool vtech_dsk_format::load(util::random_read &io, uint32_t form_factor, const s case 3: // sector data if(count <= 128) { - uint8_t byte = buf; + const uint8_t byte = buf; checksum += byte; *dest++ = byte; @@ -341,7 +346,7 @@ bool vtech_dsk_format::load(util::random_read &io, uint32_t form_factor, const s } } - image_to_flux(bdatax, image); + image_to_flux(bdatax.data(), bdatax.size(), image); image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD); return true; } @@ -354,8 +359,7 @@ bool vtech_bin_format::save(util::random_read_write &io, const std::vector<uint3 return false; auto bdata = flux_to_image(image); - size_t actual; - io.write_at(0, bdata.data(), bdata.size(), actual); + /*auto const [err, actual] =*/ write_at(io, 0, bdata.data(), bdata.size()); // FIXME: check for errors return true; } @@ -376,8 +380,8 @@ bool vtech_dsk_format::save(util::random_read_write &io, const std::vector<uint3 }; int pos = 0; - for(int track = 0; track != 40; track ++) { - for(int sector = 0; sector != 16; sector ++) { + for(int track = 0; track != 40; track++) { + for(int sector = 0; sector != 16; sector++) { uint8_t sid = sector_map[sector]; for(int i=0; i != 7; i++) bdatax[pos++] = 0x80; @@ -407,8 +411,7 @@ bool vtech_dsk_format::save(util::random_read_write &io, const std::vector<uint3 } } - size_t actual; - io.write_at(0, bdatax.data(), bdatax.size(), actual); + /*auto const [err, actual] =*/ write_at(io, 0, bdatax.data(), bdatax.size()); // FIXME: check for errors return true; } diff --git a/src/lib/formats/vt_dsk.h b/src/lib/formats/vt_dsk.h index db2687d4108..1348f9dbd4d 100644 --- a/src/lib/formats/vt_dsk.h +++ b/src/lib/formats/vt_dsk.h @@ -19,7 +19,7 @@ public: virtual bool supports_save() const noexcept override { return true; } protected: - static void image_to_flux(const std::vector<uint8_t> &bdata, floppy_image &image); + static void image_to_flux(const uint8_t *bdata, size_t size, floppy_image &image); static std::vector<uint8_t> flux_to_image(const floppy_image &image); static void wbit(std::vector<uint32_t> &buffer, uint32_t &pos, bool bit); diff --git a/src/lib/formats/wavfile.cpp b/src/lib/formats/wavfile.cpp index e410c7f9ff9..6cce494f1c8 100644 --- a/src/lib/formats/wavfile.cpp +++ b/src/lib/formats/wavfile.cpp @@ -170,7 +170,6 @@ static cassette_image::error wavfile_identify(cassette_image *cassette, cassette static cassette_image::error wavfile_load(cassette_image *cassette) { cassette_image::Options opts; - memset(&opts, 0, sizeof(opts)); return wavfile_process(cassette, &opts, true); } diff --git a/src/lib/formats/wd177x_dsk.cpp b/src/lib/formats/wd177x_dsk.cpp index c3bba8f7724..e5b02f02e32 100644 --- a/src/lib/formats/wd177x_dsk.cpp +++ b/src/lib/formats/wd177x_dsk.cpp @@ -262,8 +262,7 @@ bool wd177x_format::load(util::random_read &io, uint32_t form_factor, const std: build_sector_description(tf, sectdata, sectors, track, head); int track_size = compute_track_size(tf); - size_t actual; - io.read_at(get_image_offset(f, head, track), sectdata, track_size, actual); + /*auto const [err, actual] =*/ read_at(io, get_image_offset(f, head, track), sectdata, track_size); // FIXME: check for errors and premature EOF generate_track(desc, track, head, sectors, tf.sector_count, total_size, image); } @@ -389,8 +388,7 @@ bool wd177x_format::save(util::random_read_write &io, const std::vector<uint32_t build_sector_description(tf, sectdata, sectors, track, head); extract_sectors(image, tf, sectors, track, head); int track_size = compute_track_size(tf); - size_t actual; - io.write_at(get_image_offset(f, head, track), sectdata, track_size, actual); + /*auto const [err, actual] =*/ write_at(io, get_image_offset(f, head, track), sectdata, track_size); // FIXME: check for errors } } diff --git a/src/lib/formats/x07_cas.cpp b/src/lib/formats/x07_cas.cpp index 71e8b304efc..945e1ee5c1a 100644 --- a/src/lib/formats/x07_cas.cpp +++ b/src/lib/formats/x07_cas.cpp @@ -122,7 +122,7 @@ static int x07_handle_cassette(int16_t *buffer, const uint8_t *bytes) Generate samples for the tape image ********************************************************************/ -static int x07_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int x07_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { return x07_handle_cassette(buffer, bytes); } diff --git a/src/lib/formats/x1_tap.cpp b/src/lib/formats/x1_tap.cpp index 1a60bb18330..f730fe9fb21 100644 --- a/src/lib/formats/x1_tap.cpp +++ b/src/lib/formats/x1_tap.cpp @@ -101,7 +101,7 @@ static int x1_cas_to_wav_size (const uint8_t *casdata, int caslen) /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int x1_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int x1_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { return x1_handle_tap(buffer,bytes); } diff --git a/src/lib/formats/zx81_p.cpp b/src/lib/formats/zx81_p.cpp index e31b5f58933..515f5b78581 100644 --- a/src/lib/formats/zx81_p.cpp +++ b/src/lib/formats/zx81_p.cpp @@ -173,7 +173,7 @@ static int zx81_cassette_calculate_size_in_samples(const uint8_t *bytes, int len return (number_of_0_data+number_of_0_name)*ZX81_LOW_BIT_LENGTH + (number_of_1_data+number_of_1_name)*ZX81_HIGH_BIT_LENGTH + ZX81_PILOT_LENGTH; } -static int zx81_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int zx81_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { int16_t * p = buffer; int i; @@ -250,7 +250,7 @@ static int zx80_cassette_calculate_size_in_samples(const uint8_t *bytes, int len return number_of_0_data*ZX81_LOW_BIT_LENGTH + number_of_1_data*ZX81_HIGH_BIT_LENGTH + ZX81_PILOT_LENGTH; } -static int zx80_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes) +static int zx80_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int) { int16_t * p = buffer; int i; |