Partially complete rework of all the decoders to avoid seeking inside the

fluxmap. This requires resetting the FluxDecoder, which loses any pending
state, resulting in bad reads for (some) formats which don't have gaps between
sectors --- the DVK MX is the main victim.
This commit is contained in:
David Given
2022-02-12 00:55:09 +01:00
parent 610b7fe95c
commit 0933dc1afa
20 changed files with 204 additions and 227 deletions

View File

@@ -30,17 +30,14 @@ public:
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
nanoseconds_t advanceToNextRecord() override
{
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock)
return UNKNOWN_RECORD;
return SECTOR_RECORD;
return seekToPattern(SECTOR_PATTERN);
}
void decodeSectorRecord()
{
/* Skip ID mark. */
/* Skip ID mark (we know it's a AESLANIER_RECORD_SEPARATOR). */
readRawBits(16);

View File

@@ -29,20 +29,40 @@ public:
_config(config.amiga())
{}
RecordType advanceToNextRecord() override
void beginTrack() override
{
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock)
return UNKNOWN_RECORD;
return SECTOR_RECORD;
/* Force a seek for the first sector. */
_bad = true;
}
nanoseconds_t advanceToNextRecord() override
{
/* seekToPattern always advances one pulse, but Amiga sectors are
* usually right next each other (they're written out with no gaps).
* So, only actually do a seek if we haven't just read a reasonably
* good sector. */
if (_bad)
_clock = seekToPattern(SECTOR_PATTERN);
_bad = false;
return _clock;
}
void decodeSectorRecord() override
{
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
if (rawbits.size() < (AMIGA_RECORD_SIZE*16))
{
_bad = true;
return;
}
const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE*2);
if (rawbytes.reader().read_be48() != AMIGA_SECTOR_RECORD)
{
_bad = true;
return;
}
const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE);
const uint8_t* ptr = bytes.begin() + 3;
@@ -76,6 +96,8 @@ public:
private:
const AmigaDecoderProto& _config;
nanoseconds_t _clock;
bool _bad;
};
std::unique_ptr<AbstractDecoder> createAmigaDecoder(const DecoderProto& config)

View File

@@ -71,15 +71,9 @@ public:
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
nanoseconds_t advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord()

View File

@@ -59,15 +59,9 @@ public:
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
nanoseconds_t advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord()

View File

@@ -58,15 +58,9 @@ public:
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
nanoseconds_t advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord()

View File

@@ -58,15 +58,9 @@ public:
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
nanoseconds_t advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord()

View File

@@ -104,13 +104,9 @@ public:
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
nanoseconds_t advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(SECTOR_ID_PATTERN, matcher);
if (matcher == &SECTOR_ID_PATTERN)
return RecordType::SECTOR_RECORD;
return RecordType::UNKNOWN_RECORD;
return seekToPattern(SECTOR_ID_PATTERN);
}
void decodeSectorRecord()

View File

@@ -98,58 +98,55 @@ public:
_config(config.ibm())
{}
RecordType advanceToNextRecord() override
nanoseconds_t advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
/* If this is the MFM prefix byte, the the decoder is going to expect three
* extra bytes on the front of the header. */
_currentHeaderLength = (matcher == &MFM_PATTERN) ? 3 : 0;
Fluxmap::Position here = tell();
resetFluxDecoder();
if (_currentHeaderLength > 0)
readRawBits(_currentHeaderLength*16);
auto idbits = readRawBits(16);
const Bytes idbytes = decodeFmMfm(idbits);
uint8_t id = idbytes.slice(0, 1)[0];
if (eof())
return RecordType::UNKNOWN_RECORD;
seek(here);
switch (id)
{
case IBM_IDAM:
return RecordType::SECTOR_RECORD;
case IBM_DAM1:
case IBM_DAM2:
case IBM_TRS80DAM1:
case IBM_TRS80DAM2:
return RecordType::DATA_RECORD;
}
return RecordType::UNKNOWN_RECORD;
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN;
auto bits = readRawBits(recordSize*16);
auto bytes = decodeFmMfm(bits).slice(0, recordSize);
/* This is really annoying because the IBM record scheme has a
* variable-sized header _and_ the checksum covers this header too. So
* we have to read and decode a byte at a time until we know where the
* record itself starts, saving the bytes for the checksumming later.
*/
Bytes bytes;
ByteWriter bw(bytes);
auto readByte = [&]() {
auto bits = readRawBits(16);
auto bytes = decodeFmMfm(bits).slice(0, 1);
uint8_t byte = bytes[0];
bw.write_8(byte);
return byte;
};
uint8_t id = readByte();
if (id == 0xa1)
{
readByte();
readByte();
id = readByte();
}
if (id != IBM_IDAM)
return;
ByteReader br(bytes);
br.seek(bw.pos);
auto bits = readRawBits(IBM_IDAM_LEN*16);
bw += decodeFmMfm(bits).slice(0, IBM_IDAM_LEN);
IbmDecoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, _sector->physicalCylinder, _sector->physicalHead);
ByteReader br(bytes);
br.seek(_currentHeaderLength);
br.read_8(); /* skip ID byte */
_sector->logicalTrack = br.read_8();
_sector->logicalSide = br.read_8();
_sector->logicalSector = br.read_8();
_currentSectorSize = 1 << (br.read_8() + 7);
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, br.pos));
uint16_t wantCrc = br.read_be16();
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, _currentHeaderLength + 5));
if (wantCrc == gotCrc)
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
@@ -163,17 +160,39 @@ public:
void decodeDataRecord() override
{
unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3;
auto bits = readRawBits(recordLength*16);
auto bytes = decodeFmMfm(bits).slice(0, recordLength);
/* This is the same deal as the sector record. */
Bytes bytes;
ByteWriter bw(bytes);
auto readByte = [&]() {
auto bits = readRawBits(16);
auto bytes = decodeFmMfm(bits).slice(0, 1);
uint8_t byte = bytes[0];
bw.write_8(byte);
return byte;
};
uint8_t id = readByte();
if (id == 0xa1)
{
readByte();
readByte();
id = readByte();
}
if ((id != IBM_DAM1) && (id != IBM_DAM2)
&& (id != IBM_TRS80DAM1) && (id != IBM_TRS80DAM2))
return;
ByteReader br(bytes);
br.seek(_currentHeaderLength);
br.read_8(); /* skip ID byte */
br.seek(bw.pos);
auto bits = readRawBits((_currentSectorSize + 2) * 16);
bw += decodeFmMfm(bits).slice(0, _currentSectorSize+2);
_sector->data = br.read(_currentSectorSize);
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, br.pos));
uint16_t wantCrc = br.read_be16();
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, recordLength-2));
_sector->status = (wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}

View File

@@ -129,15 +129,9 @@ public:
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
nanoseconds_t advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return DATA_RECORD;
return UNKNOWN_RECORD;
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord()

View File

@@ -33,15 +33,10 @@ public:
_config(config.micropolis())
{}
RecordType advanceToNextRecord()
nanoseconds_t advanceToNextRecord() override
{
_fmr->seekToIndexMark();
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(SECTOR_SYNC_PATTERN, matcher);
if (matcher == &SECTOR_SYNC_PATTERN) {
return SECTOR_RECORD;
}
return UNKNOWN_RECORD;
seekToIndexMark();
return seekToPattern(SECTOR_SYNC_PATTERN);
}
void decodeSectorRecord()

View File

@@ -16,8 +16,10 @@ const int SECTOR_SIZE = 256;
*/
/* FM beginning of track marker:
* 0 0 f 3 decoded nibbles
* 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1
* 1010 1010 1010 1010 1111 1111 1010 1111
* a a a a f f a f
* a a a a f f a f encoded nibbles
*/
const FluxPattern ID_PATTERN(32, 0xaaaaffaf);
@@ -30,59 +32,43 @@ public:
void beginTrack()
{
_currentSector = -1;
_clock = 0;
_clock = _sector->clock = seekToPattern(ID_PATTERN);
_currentSector = 0;
}
RecordType advanceToNextRecord()
nanoseconds_t advanceToNextRecord() override
{
if (_currentSector == -1)
{
/* First sector in the track: look for the sync marker. */
const FluxMatcher* matcher = nullptr;
_sector->clock = _clock = _fmr->seekToPattern(ID_PATTERN, matcher);
readRawBits(32); /* skip the ID mark */
_logicalTrack = decodeFmMfm(readRawBits(32)).slice(0, 32).reader().read_be16();
}
else if (_currentSector == 10)
if (_currentSector == 10)
{
/* That was the last sector on the disk. */
return UNKNOWN_RECORD;
return 0;
}
else
{
/* Otherwise we assume the clock from the first sector is still valid.
* The decoder framwork will automatically stop when we hit the end of
* the track. */
_sector->clock = _clock;
}
_currentSector++;
return SECTOR_RECORD;
return _clock;
}
void decodeSectorRecord()
{
auto bits = readRawBits((SECTOR_SIZE+2)*16);
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2).swab();
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2);
uint16_t gotChecksum = 0;
ByteReader br(bytes);
for (int i=0; i<(SECTOR_SIZE/2); i++)
gotChecksum += br.read_le16();
uint16_t wantChecksum = br.read_le16();
gotChecksum += br.read_be16();
uint16_t wantChecksum = br.read_be16();
_sector->logicalTrack = _logicalTrack;
_sector->logicalTrack = _sector->physicalCylinder;
_sector->logicalSide = _sector->physicalHead;
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE);
_sector->data = bytes.slice(0, SECTOR_SIZE).swab();
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
_currentSector++;
}
private:
nanoseconds_t _clock;
int _currentSector;
int _logicalTrack;
};
std::unique_ptr<AbstractDecoder> createMxDecoder(const DecoderProto& config)

View File

@@ -21,6 +21,8 @@
#include "lib/decoders/decoders.pb.h"
#include "fmt/format.h"
#define MFM_ID 0xaaaaaaaaaaaa5545LL
#define FM_ID 0xaaaaaaaaaaaaffefLL
/*
* MFM sectors have 32 bytes of 00's followed by two sync characters,
* specified in the North Star MDS manual as 0xFBFB.
@@ -33,14 +35,14 @@
* 0000 0000 0000 0000 0000 0000 0101 0101 0100 0101
* A A A A A A 5 5 4 5
*/
static const FluxPattern MFM_PATTERN(64, 0xAAAAAAAAAAAA5545LL);
static const FluxPattern MFM_PATTERN(64, MFM_ID);
/* FM sectors have 16 bytes of 00's followed by 0xFB.
* 00 FB
* 0000 0000 1111 1111 1110 1111
* A A F F E F
*/
static const FluxPattern FM_PATTERN(64, 0xAAAAAAAAAAAAFFEFLL);
static const FluxPattern FM_PATTERN(64, FM_ID);
const FluxMatchers ANY_SECTOR_PATTERN(
{
@@ -74,16 +76,16 @@ public:
{}
/* Search for FM or MFM sector record */
RecordType advanceToNextRecord() override
nanoseconds_t advanceToNextRecord() override
{
nanoseconds_t now = _fmr->tell().ns();
nanoseconds_t now = tell().ns();
/* For all but the first sector, seek to the next sector pulse.
* The first sector does not contain the sector pulse in the fluxmap.
*/
if (now != 0) {
_fmr->seekToIndexMark();
now = _fmr->tell().ns();
seekToIndexMark();
now = tell().ns();
}
/* Discard a possible partial sector at the end of the track.
@@ -91,23 +93,21 @@ public:
* whatever data read happens to match the checksum of 0, which is
* rare, but has been observed on some disks.
*/
if (now > (_fmr->getDuration() - 21e6)) {
_fmr->seekToIndexMark();
return(UNKNOWN_RECORD);
if (now > (getFluxmapDuration() - 21e6)) {
seekToIndexMark();
return 0;
}
int msSinceIndex = std::round(now / 1e6);
const FluxMatcher* matcher = nullptr;
/* Note that the seekToPattern ignores the sector pulses, so if
* a sector is not found for some reason, the seek will advance
* past one or more sector pulses. For this reason, calculate
* _hardSectorId after the sector header is found.
*/
_sector->clock = _fmr->seekToPattern(ANY_SECTOR_PATTERN, matcher);
nanoseconds_t clock = seekToPattern(ANY_SECTOR_PATTERN);
int sectorFoundTimeRaw = std::round((_fmr->tell().ns()) / 1e6);
int sectorFoundTimeRaw = std::round((tell().ns()) / 1e6);
int sectorFoundTime;
/* Round time to the nearest 20ms */
@@ -121,28 +121,15 @@ public:
/* Calculate the sector ID based on time since the index */
_hardSectorId = (sectorFoundTime / 20) % 10;
// std::cout << fmt::format(
// "Sector ID {}: hole at {}ms, sector start at {}ms",
// _hardSectorId, msSinceIndex, sectorFoundTimeRaw) << std::endl;
if (matcher == &MFM_PATTERN) {
_sectorType = SECTOR_TYPE_MFM;
return SECTOR_RECORD;
}
if (matcher == &FM_PATTERN) {
_sectorType = SECTOR_TYPE_FM;
return SECTOR_RECORD;
}
return UNKNOWN_RECORD;
return clock;
}
void decodeSectorRecord() override
{
unsigned recordSize, payloadSize, headerSize;
uint64_t id = toBytes(readRawBits(64)).reader().read_be64();
if (_sectorType == SECTOR_TYPE_MFM) {
unsigned recordSize, payloadSize, headerSize;
if (id == SECTOR_TYPE_MFM) {
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_DD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_DD;
headerSize = NORTHSTAR_HEADER_SIZE_DD;
@@ -153,8 +140,6 @@ public:
headerSize = NORTHSTAR_HEADER_SIZE_SD;
}
readRawBits(48);
auto rawbits = readRawBits(recordSize * 16);
auto bytes = decodeFmMfm(rawbits).slice(0, recordSize);
ByteReader br(bytes);

View File

@@ -45,15 +45,9 @@ public:
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
nanoseconds_t advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord()

View File

@@ -59,15 +59,9 @@ public:
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
nanoseconds_t advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return DATA_RECORD;
return UNKNOWN_RECORD;
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord()

View File

@@ -20,14 +20,10 @@ public:
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
nanoseconds_t advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_fmr->seekToIndexMark();
_sector->clock = _fmr->seekToPattern(SECTOR_START_PATTERN, matcher);
if (matcher == &SECTOR_START_PATTERN)
return SECTOR_RECORD;
return UNKNOWN_RECORD;
seekToIndexMark();
return seekToPattern(SECTOR_START_PATTERN);
}
void decodeSectorRecord()

View File

@@ -279,6 +279,16 @@ ByteWriter Bytes::writer()
return ByteWriter(*this);
}
uint64_t ByteReader::read_be48()
{
return ((uint64_t)read_be16() << 32) | read_be32();
}
uint64_t ByteReader::read_be64()
{
return ((uint64_t)read_be32() << 32) | read_be32();
}
ByteWriter& ByteWriter::operator +=(std::istream& stream)
{
Bytes buffer(4096);

View File

@@ -131,6 +131,9 @@ public:
return (b1<<24) | (b2<<16) | (b3<<8) | b4;
}
uint64_t read_be48();
uint64_t read_be64();
uint16_t read_le16()
{
uint8_t b1 = _bytes[pos++];

View File

@@ -68,58 +68,46 @@ std::unique_ptr<TrackDataFlux> AbstractDecoder::decodeToSectors(
FluxmapReader fmr(*fluxmap);
_fmr = &fmr;
beginTrack();
for (;;)
{
auto newSector = [&] {
_sector = std::make_shared<Sector>();
_sector->status = Sector::MISSING;
_sector->physicalCylinder = physicalCylinder;
_sector->physicalHead = physicalHead;
};
newSector();
beginTrack();
for (;;)
{
newSector();
Fluxmap::Position recordStart = fmr.tell();
_decoder.reset(new FluxDecoder(&fmr, _sector->clock, _config));
RecordType r = advanceToNextRecord();
_sector->clock = advanceToNextRecord();
if (fmr.eof() || !_sector->clock)
return std::move(_trackdata);
if ((r == UNKNOWN_RECORD) || (r == DATA_RECORD))
{
fmr.skipToEvent(F_BIT_PULSE);
continue;
}
/* Read the sector record. */
recordStart = fmr.tell();
resetFluxDecoder();
decodeSectorRecord();
Fluxmap::Position recordEnd = fmr.tell();
pushRecord(recordStart, recordEnd);
if (_sector->status == Sector::DATA_MISSING)
{
/* The data is in a separate record. */
_sector->headerStartTime = recordStart.ns();
_sector->headerEndTime = recordEnd.ns();
for (;;)
{
r = advanceToNextRecord();
if (r != UNKNOWN_RECORD)
_sector->clock = advanceToNextRecord();
if (fmr.eof() || !_sector->clock)
break;
if (fmr.eof())
break;
fmr.skipToEvent(F_BIT_PULSE);
}
recordStart = fmr.tell();
if (r == DATA_RECORD)
{
resetFluxDecoder();
decodeDataRecord();
if (_sector->status != Sector::DATA_MISSING)
break;
fmr.skipToEvent(F_BIT_PULSE);
resetFluxDecoder();
}
recordEnd = fmr.tell();
pushRecord(recordStart, recordEnd);
}
_sector->dataStartTime = recordStart.ns();
_sector->dataEndTime = recordEnd.ns();
if (_sector->status != Sector::MISSING)
_trackdata->sectors.push_back(_sector);
@@ -149,6 +137,20 @@ void AbstractDecoder::resetFluxDecoder()
_decoder.reset(new FluxDecoder(_fmr, _sector->clock, _config));
}
nanoseconds_t AbstractDecoder::seekToPattern(const FluxMatcher& pattern)
{
_fmr->skipToEvent(F_BIT_PULSE);
nanoseconds_t clock = _fmr->seekToPattern(pattern);
_decoder.reset(new FluxDecoder(_fmr, clock, _config));
return clock;
}
void AbstractDecoder::seekToIndexMark()
{
_fmr->skipToEvent(F_BIT_PULSE);
_fmr->seekToIndexMark();
}
std::vector<bool> AbstractDecoder::readRawBits(unsigned count)
{
return _decoder->readBits(count);

View File

@@ -57,22 +57,30 @@ public:
void seek(const Fluxmap::Position& pos)
{ return _fmr->seek(pos); }
nanoseconds_t seekToPattern(const FluxMatcher& pattern);
void seekToIndexMark();
bool eof() const
{ return _fmr->eof(); }
nanoseconds_t getFluxmapDuration() const
{ return _fmr->getDuration(); }
virtual std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const;
protected:
virtual void beginTrack() {};
virtual RecordType advanceToNextRecord() = 0;
virtual nanoseconds_t advanceToNextRecord() = 0;
virtual void decodeSectorRecord() = 0;
virtual void decodeDataRecord() {};
const DecoderProto& _config;
FluxmapReader* _fmr = nullptr;
std::unique_ptr<TrackDataFlux> _trackdata;
std::shared_ptr<Sector> _sector;
std::unique_ptr<FluxDecoder> _decoder;
private:
FluxmapReader* _fmr = nullptr;
};
#endif

View File

@@ -1,11 +1,11 @@
#!/bin/sh
set -e
tmp=/tmp/$$
format=$1
tmp=/tmp/$$-$format
srcfile=$tmp.src.img
fluxfile=$tmp.$2
destfile=$tmp.dest.img
format=$1
shift
shift