Refactor FluxEngine to use dataspecs for source and destinations. Document

them.
This commit is contained in:
David Given
2019-01-21 00:38:03 +01:00
parent 4b6b4aa9f7
commit 6af432f798
16 changed files with 189 additions and 204 deletions

View File

@@ -215,6 +215,41 @@ ready. What next?
### The programs
#### Source and destination specifiers
When reading from or writing to _a disk_ (or a file pretending to be a disk),
use the `-s` and `-d` options to tell FluxEngine which bits of the disk you
want to access. These use a common syntax:
```
fe-readibm -s fakedisk.flux:t=0-79:s=0
```
- To access a real disk, leave out the filename (so `:t=0-79:s=0`).
- To access only some tracks, use the `t=` modifier. To access only some
sides, use the `s=` modifier.
- Inside a modifier, you can use a comma separated list of ranges. So
`:t=0-3` and `:t=0,1,2,3` are equivalent.
- When specifying a range, you can also specify the step. For example,
`:t=0-79x2` would be used when accessing a 40-track disk with double stepping.
Source and destination specifiers work entirely in *physical units*.
FluxEngine is intended to be connected to an 80 (or 82) track double sided
drive, and these are the units used. If the format you're trying to access
lays out its tracks differently, then you'll need a specifier which tells
FluxEngine how to find those tracks. See the 40-track disk example above.
If you _don't_ specify a modifier, you'll get the default, which should be
sensible for the command you're using.
**Important note:** FluxEngine _always_ uses zero-based units (even if the
*disk format says otherwise).
### The commands
The FluxEngine client software is a largely undocumented set of small tools.
You'll have to play with them. They all support `--help`. They're not
installed anywhere and after building you'll find them in the `.obj`

View File

@@ -1,7 +1,9 @@
#include "globals.h"
#include "flags.h"
#include "dataspec.h"
#include "fmt/format.h"
#include <regex>
#include <sstream>
static const std::regex MOD_REGEX("([a-z]*)=([-x+0-9,]*)");
static const std::regex DATA_REGEX("([0-9]+)(?:(?:-([0-9]+))|(?:\\+([0-9]+)))?(?:x([0-9]+))?");
@@ -34,6 +36,7 @@ DataSpec::Modifier DataSpec::parseMod(const std::string& spec)
Modifier m;
m.name = match[1];
m.source = spec;
for (auto& data : split(match[2], ","))
{
int start = 0;
@@ -90,3 +93,14 @@ void DataSpec::set(const std::string& spec)
}
}
}
DataSpec::operator std::string(void) const
{
std::stringstream ss;
ss << filename;
for (const auto& mod : modifiers)
ss << ':' << mod.second.source;
return ss.str();
}

View File

@@ -20,6 +20,7 @@ public:
{
std::string name;
std::set<unsigned> data;
std::string source;
bool operator == (const Modifier& other) const
{ return (name == other.name) && (data == other.data); }
@@ -38,10 +39,31 @@ public:
{ set(spec); }
void set(const std::string& spec);
operator std::string () const;
std::string filename;
std::map<std::string, Modifier> modifiers;
std::vector<Location> locations;
};
std::ostream& operator << (std::ostream& os, const DataSpec& dataSpec)
{ os << (std::string)dataSpec; return os; }
class DataSpecFlag : public Flag
{
public:
DataSpecFlag(const std::vector<std::string>& names, const std::string helptext,
const std::string& defaultValue):
Flag(names, helptext),
value(defaultValue)
{}
bool hasArgument() const { return true; }
const std::string defaultValueAsString() const { return value; }
void set(const std::string& value) { this->value.set(value); }
public:
DataSpec value;
};
#endif

View File

@@ -82,9 +82,9 @@ void Flag::parseFlags(int argc, const char* argv[])
void BoolFlag::set(const std::string& value)
{
if ((value == "true") || (value == "y"))
_value = true;
this->value = true;
else if ((value == "false") || (value == "n"))
_value = false;
this->value = false;
else
Error() << "can't parse '" << value << "'; try 'true' or 'false'";
}
@@ -105,7 +105,7 @@ static void doHelp()
}
if (flag->hasArgument())
std::cout << " <default: \"" << flag->defaultValue() << "\">";
std::cout << " <default: \"" << flag->defaultValueAsString() << "\">";
std::cout << ": " << flag->helptext() << std::endl;
}
exit(0);

View File

@@ -1,6 +1,8 @@
#ifndef FLAGS_H
#define FLAGS_H
class DataSpec;
class Flag
{
public:
@@ -14,7 +16,7 @@ public:
const std::string& helptext() const { return _helptext; }
virtual bool hasArgument() const = 0;
virtual const std::string defaultValue() const = 0;
virtual const std::string defaultValueAsString() const = 0;
virtual void set(const std::string& value) = 0;
private:
@@ -32,7 +34,7 @@ public:
{}
bool hasArgument() const { return false; }
const std::string defaultValue() const { return ""; }
const std::string defaultValueAsString() const { return ""; }
void set(const std::string& value) { _callback(); }
private:
@@ -49,7 +51,7 @@ public:
operator bool() const { return _value; }
bool hasArgument() const { return false; }
const std::string defaultValue() const { return "false"; }
const std::string defaultValueAsString() const { return "false"; }
void set(const std::string& value) { _value = true; }
private:
@@ -63,18 +65,16 @@ public:
ValueFlag(const std::vector<std::string>& names, const std::string helptext,
const T defaultValue):
Flag(names, helptext),
_defaultValue(defaultValue),
_value(defaultValue)
defaultValue(defaultValue),
value(defaultValue)
{}
T value() const { return _value; }
operator T() const { return _value; }
operator T() const { return value; }
bool hasArgument() const { return true; }
protected:
T _defaultValue;
T _value;
T defaultValue;
T value;
};
class StringFlag : public ValueFlag<std::string>
@@ -85,8 +85,8 @@ public:
ValueFlag(names, helptext, defaultValue)
{}
const std::string defaultValue() const { return _defaultValue; }
void set(const std::string& value) { _value = value; }
const std::string defaultValueAsString() const { return defaultValue; }
void set(const std::string& value) { this->value = value; }
};
class IntFlag : public ValueFlag<int>
@@ -97,8 +97,8 @@ public:
ValueFlag(names, helptext, defaultValue)
{}
const std::string defaultValue() const { return std::to_string(_defaultValue); }
void set(const std::string& value) { _value = std::stoi(value); }
const std::string defaultValueAsString() const { return std::to_string(defaultValue); }
void set(const std::string& value) { this->value = std::stoi(value); }
};
class DoubleFlag : public ValueFlag<double>
@@ -109,8 +109,8 @@ public:
ValueFlag(names, helptext, defaultValue)
{}
const std::string defaultValue() const { return std::to_string(_defaultValue); }
void set(const std::string& value) { _value = std::stod(value); }
const std::string defaultValueAsString() const { return std::to_string(defaultValue); }
void set(const std::string& value) { this->value = std::stod(value); }
};
class BoolFlag : public ValueFlag<double>
@@ -121,7 +121,7 @@ public:
ValueFlag(names, helptext, defaultValue)
{}
const std::string defaultValue() const { return _defaultValue ? "true" : "false"; }
const std::string defaultValueAsString() const { return defaultValue ? "true" : "false"; }
void set(const std::string& value);
};

View File

@@ -6,16 +6,11 @@
#include "sql.h"
#include "dataspec.h"
#include "fmt/format.h"
#include <regex>
static const std::regex SOURCE_REGEX("([^:]*)"
"(?::t=([0-9]+)(?:-([0-9]+))?)?"
"(?::s=([0-9]+)(?:-([0-9]+))?)?");
static StringFlag source(
static DataSpecFlag source(
{ "--source", "-s" },
"source for data",
"");
":t=0-79:s=0-1");
static StringFlag destination(
{ "--write-flux", "-f" },
@@ -31,89 +26,53 @@ static IntFlag revolutions(
"read this many revolutions of the disk",
1);
static DataSpec readerspec("foo:f=7:z=9-10");
static std::string basefilename;
static int starttrack = 0;
static int endtrack = 79;
static int startside = 0;
static int endside = 1;
static sqlite3* indb;
static sqlite3* outdb;
void setReaderDefaults(int minTrack, int maxTrack, int minSide, int maxSide)
void setReaderDefaultSource(const std::string& source)
{
starttrack = minTrack;
endtrack = maxTrack;
startside = minSide;
endside = maxSide;
::source.set(source);
}
Fluxmap& ReaderTrack::read()
std::unique_ptr<Fluxmap> ReaderTrack::read()
{
if (!_read)
{
std::cout << fmt::format("{0:>3}.{1}: ", track, side) << std::flush;
reallyRead();
std::cout << fmt::format("{0} ms in {1} bytes", int(_fluxmap->duration()/1e6), _fluxmap->bytes()) << std::endl;
_read = true;
std::cout << fmt::format("{0:>3}.{1}: ", track, side) << std::flush;
std::unique_ptr<Fluxmap> fluxmap = reallyRead();
std::cout << fmt::format(
"{0} ms in {1} bytes", int(fluxmap->duration()/1e6), fluxmap->bytes()) << std::endl;
if (outdb)
sqlWriteFlux(outdb, track, side, *_fluxmap);
}
if (outdb)
sqlWriteFlux(outdb, track, side, *fluxmap);
return *_fluxmap.get();
return fluxmap;
}
void ReaderTrack::forceReread()
{
_read = false;
}
void CapturedReaderTrack::reallyRead()
std::unique_ptr<Fluxmap> CapturedReaderTrack::reallyRead()
{
usbSeek(track);
_fluxmap = usbRead(side, revolutions);
return usbRead(side, revolutions);
}
void FileReaderTrack::reallyRead()
std::unique_ptr<Fluxmap> FileReaderTrack::reallyRead()
{
if (!indb)
{
indb = sqlOpen(basefilename, SQLITE_OPEN_READONLY);
indb = sqlOpen(source.value.filename, SQLITE_OPEN_READONLY);
atexit([]() { sqlClose(indb); });
}
_fluxmap = sqlReadFlux(indb, track, side);
return sqlReadFlux(indb, track, side);
}
std::vector<std::unique_ptr<ReaderTrack>> readTracks()
{
auto f = source.value();
std::smatch match;
if (!std::regex_match(f, match, SOURCE_REGEX))
Error() << "invalid source specifier '" << source.value() << "'";
basefilename = match[1];
if (match[2].length() != 0)
starttrack = endtrack = std::stoi(match[2]);
if (match[3].length() != 0)
endtrack = std::stoi(match[3]);
if (match[4].length() != 0)
startside = endside = std::stoi(match[4]);
if (match[5].length() != 0)
endside = std::stoi(match[5]);
const DataSpec& dataSpec = source.value;
std::cout << "Reading from: "
<< (basefilename.empty() ? "a real floppy disk" : basefilename) << std::endl
<< "Tracks: "
<< starttrack << " to " << endtrack << " inclusive" << std::endl
<< "Sides: "
<< startside << " to " << endside << " inclusive" << std::endl;
std::cout << "Reading from: " << dataSpec << std::endl;
if (!destination.value().empty())
if (!destination.value.empty())
{
outdb = sqlOpen(destination, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
std::cout << "Writing a copy of the flux to " << destination.value() << std::endl;
std::cout << "Writing a copy of the flux to " << destination.value << std::endl;
sqlPrepareFlux(outdb);
sqlStmt(outdb, "BEGIN;");
atexit([]()
@@ -125,18 +84,15 @@ std::vector<std::unique_ptr<ReaderTrack>> readTracks()
}
std::vector<std::unique_ptr<ReaderTrack>> tracks;
for (int track=starttrack; track<=endtrack; track++)
for (const auto& location : dataSpec.locations)
{
for (int side=startside; side<=endside; side++)
{
std::unique_ptr<ReaderTrack> t(
basefilename.empty()
? (ReaderTrack*)new CapturedReaderTrack()
: (ReaderTrack*)new FileReaderTrack());
t->track = track;
t->side = side;
tracks.push_back(std::move(t));
}
std::unique_ptr<ReaderTrack> t(
dataSpec.filename.empty()
? (ReaderTrack*)new CapturedReaderTrack()
: (ReaderTrack*)new FileReaderTrack());
t->track = location.track;
t->side = location.side;
tracks.push_back(std::move(t));
}
if (justRead)

View File

@@ -11,28 +11,23 @@ public:
int track;
int side;
Fluxmap& read();
void forceReread();
virtual void reallyRead() = 0;
protected:
bool _read = false;
std::unique_ptr<Fluxmap> _fluxmap;
std::unique_ptr<Fluxmap> read();
virtual std::unique_ptr<Fluxmap> reallyRead() = 0;
};
class CapturedReaderTrack : public ReaderTrack
{
public:
void reallyRead();
std::unique_ptr<Fluxmap> reallyRead();
};
class FileReaderTrack : public ReaderTrack
{
public:
void reallyRead();
std::unique_ptr<Fluxmap> reallyRead();
};
extern void setReaderDefaults(int minTrack, int maxTrack, int minSide, int maxSide);
extern void setReaderDefaultSource(const std::string& source);
extern std::vector<std::unique_ptr<ReaderTrack>> readTracks();
#endif

View File

@@ -5,62 +5,31 @@
#include "sql.h"
#include "protocol.h"
#include "usb.h"
#include "dataspec.h"
#include "fmt/format.h"
#include <regex>
static const std::regex DEST_REGEX("([^:]*)"
"(?::t=([0-9]+)(?:-([0-9]+))?)?"
"(?::s=([0-9]+)(?:-([0-9]+))?)?");
static StringFlag dest(
static DataSpecFlag dest(
{ "--dest", "-d" },
"destination for data",
"");
":t=0-79:s=0-1");
static std::string basefilename;
static int starttrack = 0;
static int endtrack = 79;
static int startside = 0;
static int endside = 1;
static sqlite3* outdb;
void setWriterDefaults(int minTrack, int maxTrack, int minSide, int maxSide)
void setWriterDefaultDest(const std::string& dest)
{
starttrack = minTrack;
endtrack = maxTrack;
startside = minSide;
endside = maxSide;
::dest.set(dest);
}
void writeTracks(
int minTrack, int maxTrack,
const std::function<Fluxmap(int track, int side)> producer)
const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer)
{
auto f = dest.value();
std::smatch match;
if (!std::regex_match(f, match, DEST_REGEX))
Error() << "invalid destination specifier '" << dest.value() << "'";
basefilename = match[1];
if (match[2].length() != 0)
starttrack = endtrack = std::stoi(match[2]);
if (match[3].length() != 0)
endtrack = std::stoi(match[3]);
if (match[4].length() != 0)
startside = endside = std::stoi(match[4]);
if (match[5].length() != 0)
endside = std::stoi(match[5]);
const auto& spec = dest.value;
std::cout << "Writing to: "
<< (basefilename.empty() ? "a real floppy disk" : basefilename) << std::endl
<< "Tracks: "
<< starttrack << " to " << endtrack << " inclusive" << std::endl
<< "Sides: "
<< startside << " to " << endside << " inclusive" << std::endl;
std::cout << "Writing to: " << spec << std::endl;
if (!basefilename.empty())
if (!spec.filename.empty())
{
outdb = sqlOpen(basefilename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
outdb = sqlOpen(spec.filename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
sqlPrepareFlux(outdb);
sqlStmt(outdb, "BEGIN;");
atexit([]()
@@ -71,33 +40,31 @@ void writeTracks(
);
}
for (int track=starttrack; track<=endtrack; track++)
for (const auto& location : spec.locations)
{
for (int side=startside; side<=endside; side++)
std::cout << fmt::format("{0:>3}.{1}: ", location.track, location.side) << std::flush;
std::unique_ptr<Fluxmap> fluxmap = producer(location.track, location.side);
if (!fluxmap)
{
std::cout << fmt::format("{0:>3}.{1}: ", track, side) << std::flush;
if ((track < minTrack) || (track > maxTrack))
if (!outdb)
{
if (!outdb)
{
std::cout << "erasing" << std::endl;
usbSeek(track);
usbErase(side);
}
std::cout << "erasing" << std::endl;
usbSeek(location.track);
usbErase(location.side);
}
}
else
{
fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2);
if (outdb)
sqlWriteFlux(outdb, location.track, location.side, *fluxmap);
else
{
Fluxmap fluxmap = producer(track, side);
fluxmap.precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2);
if (outdb)
sqlWriteFlux(outdb, track, side, fluxmap);
else
{
usbSeek(track);
usbWrite(side, fluxmap);
}
std::cout << fmt::format("{0} ms in {1} bytes", int(fluxmap.duration()/1e6), fluxmap.bytes()) << std::endl;
usbSeek(location.track);
usbWrite(location.side, *fluxmap);
}
std::cout << fmt::format(
"{0} ms in {1} bytes", int(fluxmap->duration()/1e6), fluxmap->bytes()) << std::endl;
}
}
}

View File

@@ -3,14 +3,13 @@
class Fluxmap;
extern void setWriterDefaults(int minTrack, int maxTrack, int minSide, int maxSide);
extern void setWriterDefaultDest(const std::string& dest);
extern void writeTracks(
int minTrack, int maxTrack,
const std::function<Fluxmap(int track, int side)> producer);
extern void writeTracks(const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer);
extern void fillBitmapTo(std::vector<bool>& bitmap,
unsigned& cursor, unsigned terminateAt,
const std::vector<bool>& pattern);
#endif

View File

@@ -4,10 +4,10 @@
int main(int argc, const char* argv[])
{
setWriterDefaults(0, 81, 0, 1);
setWriterDefaultDest(":t=0-81:s=0-1");
Flag::parseFlags(argc, argv);
writeTracks(-1, -1, NULL);
writeTracks(NULL);
return 0;
}

View File

@@ -45,12 +45,12 @@ int main(int argc, const char* argv[])
{
if ((track->track == trackFlag) && (track->side == sideFlag))
{
Fluxmap& fluxmap = track->read();
std::unique_ptr<Fluxmap> fluxmap = track->read();
nanoseconds_t clockPeriod = fluxmap.guessClock();
nanoseconds_t clockPeriod = fluxmap->guessClock();
std::cout << fmt::format(" {:.2f} us clock; ", (double)clockPeriod/1000.0) << std::flush;
auto bitmap = fluxmap.decodeToBits(clockPeriod*clockScaleFlag);
auto bitmap = fluxmap->decodeToBits(clockPeriod*clockScaleFlag);
std::cout << fmt::format("{} bytes encoded.", bitmap.size()/8) << std::endl;
if (dumpFluxFlag)
@@ -66,9 +66,9 @@ int main(int argc, const char* argv[])
nanoseconds_t nextclock = clockPeriod;
int ticks = 0;
std::cout << fmt::format("{: 10.3f}:-", 0.0);
for (int cursor=0; cursor<fluxmap.bytes(); cursor++)
for (int cursor=0; cursor<fluxmap->bytes(); cursor++)
{
int interval = fluxmap[cursor];
int interval = (*fluxmap)[cursor];
if (interval == 0)
interval = 0x100;
ticks += interval;

View File

@@ -30,7 +30,7 @@ static IntFlag retries(
int main(int argc, const char* argv[])
{
setReaderDefaults(0, 81, 0, 0);
setReaderDefaultSource(":t=0-81:s=0");
Flag::parseFlags(argc, argv);
bool failures = false;
@@ -40,12 +40,12 @@ int main(int argc, const char* argv[])
std::map<int, std::unique_ptr<Sector>> readSectors;
for (int retry = ::retries; retry >= 0; retry--)
{
Fluxmap& fluxmap = track->read();
std::unique_ptr<Fluxmap> fluxmap = track->read();
nanoseconds_t clockPeriod = fluxmap.guessClock();
nanoseconds_t clockPeriod = fluxmap->guessClock();
std::cout << fmt::format(" {:.1f} us clock; ", (double)clockPeriod/1000.0) << std::flush;
auto bitmap = fluxmap.decodeToBits(clockPeriod);
auto bitmap = fluxmap->decodeToBits(clockPeriod);
std::cout << fmt::format("{} bytes encoded; ", bitmap.size()/8) << std::flush;
auto records = decodeBitsToRecordsBrother(bitmap);
@@ -103,7 +103,6 @@ int main(int argc, const char* argv[])
std::cout << std::endl
<< " " << retry << " retries remaining" << std::endl;
track->forceReread();
}
int size = 0;

View File

@@ -20,6 +20,7 @@ static SettableFlag dumpRecords(
int main(int argc, const char* argv[])
{
setReaderDefaultSource(":t=0-79:s=0-1");
Flag::parseFlags(argc, argv);
bool failures = false;
@@ -28,12 +29,12 @@ int main(int argc, const char* argv[])
{
int retries = 5;
retry:
Fluxmap& fluxmap = track->read();
nanoseconds_t clockPeriod = fluxmap.guessClock();
std::unique_ptr<Fluxmap> fluxmap = track->read();
nanoseconds_t clockPeriod = fluxmap->guessClock();
std::cout << fmt::format(" {:.1f} us clock; ", (double)clockPeriod/1000.0) << std::flush;
/* For MFM, the bit clock is half the detected clock. */
auto bitmap = fluxmap.decodeToBits(clockPeriod/2);
auto bitmap = fluxmap->decodeToBits(clockPeriod/2);
std::cout << fmt::format("{} bytes encoded; ", bitmap.size()/8) << std::flush;
auto records = decodeBitsToRecordsMfm(bitmap);
@@ -61,7 +62,6 @@ int main(int argc, const char* argv[])
std::cout << std::endl
<< " " << retries << " retries remaining" << std::endl;
retries--;
track->forceReread();
goto retry;
}
}

View File

@@ -32,11 +32,6 @@ static DoubleFlag sectorSpacingMs(
"Time between successive sector headers (milliseconds).",
16.2);
static IntFlag trackOffset(
{ "--track-offset" },
"Number of tracks to offset when writing the image.",
0);
static DoubleFlag postHeaderSpacingMs(
{ "--post-header-spacing" },
"Time between a sector's header and data records (milliseconds).",
@@ -57,7 +52,7 @@ static int charToInt(char c)
int main(int argc, const char* argv[])
{
setWriterDefaults(0, 81, 0, 0);
setWriterDefaultDest(":t=0-77:s=0");
Flag::parseFlags(argc, argv);
SectorSet allSectors;
@@ -71,16 +66,14 @@ int main(int argc, const char* argv[])
const std::string& skew = sectorSkew;
writeTracks(
trackOffset, trackOffset+77,
[&](int physicalTrack, int physicalSide) -> Fluxmap
[&](int track, int side) -> std::unique_ptr<Fluxmap>
{
int logicalTrack = physicalTrack - trackOffset;
if ((track < 0) || (track > 77) || (side != 0))
return std::unique_ptr<Fluxmap>();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
std::cerr << "logical track " << logicalTrack << std::endl
<< " ";
for (int sectorCount=0; sectorCount<geometry.sectors; sectorCount++)
{
int sectorId = charToInt(skew.at(sectorCount));
@@ -89,10 +82,10 @@ int main(int argc, const char* argv[])
double dataMs = headerMs + postHeaderSpacingMs;
unsigned dataCursor = dataMs*1e3 / clockRateUs;
auto& sectorData = allSectors[{logicalTrack, 0, sectorId}];
auto& sectorData = allSectors[{track, 0, sectorId}];
fillBitmapTo(bits, cursor, headerCursor, { true, false });
writeBrotherSectorHeader(bits, cursor, logicalTrack, sectorId);
writeBrotherSectorHeader(bits, cursor, track, sectorId);
fillBitmapTo(bits, cursor, dataCursor, { true, false });
writeBrotherSectorData(bits, cursor, sectorData->data);
}
@@ -104,8 +97,8 @@ int main(int argc, const char* argv[])
// The pre-index gap is not normally reported.
// std::cerr << "pre-index gap " << 200.0 - (double)cursor*clockRateUs/1e3 << std::endl;
Fluxmap fluxmap;
fluxmap.appendBits(bits, clockRateUs*1e3);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
}
);

View File

@@ -9,8 +9,8 @@
int main(int argc, const char* argv[])
{
setReaderDefaults(0, 81, 0, 1);
setWriterDefaults(0, 81, 0, 1);
setReaderDefaultSource(":t=0-81:h=0-1");
setWriterDefaultDest(":t=0-81:s=0-1");
Flag::parseFlags(argc, argv);
auto tracks = readTracks();
@@ -18,8 +18,7 @@ int main(int argc, const char* argv[])
track->read();
writeTracks(
0, 81,
[&](int physicalTrack, int physicalSide) -> Fluxmap
[&](int physicalTrack, int physicalSide) -> std::unique_ptr<Fluxmap>
{
for (auto& track : tracks)
{

View File

@@ -1,4 +1,5 @@
#include "globals.h"
#include "flags.h"
#include "dataspec.h"
#include <assert.h>
@@ -39,30 +40,35 @@ static void test_dataspec(void)
assert((spec.locations
== std::vector<DataSpec::Location>
{{0, 0}, {0, 1}, {1, 0}, {1, 1}, {2, 0}, {2, 1}}));
assert((std::string)spec == "foo:s=0-1:t=0-2");
spec.set("bar");
assert(spec.filename == "bar");
assert((spec.locations
== std::vector<DataSpec::Location>
{{0, 0}, {0, 1}, {1, 0}, {1, 1}, {2, 0}, {2, 1}}));
assert((std::string)spec == "bar:s=0-1:t=0-2");
spec.set(":t=0");
assert(spec.filename.empty());
assert((spec.locations
== std::vector<DataSpec::Location>
{{0, 0}, {0, 1}}));
assert((std::string)spec == ":s=0-1:t=0");
spec.set(":s=1");
assert(spec.filename.empty());
assert((spec.locations
== std::vector<DataSpec::Location>
{{0, 1}}));
assert((std::string)spec == ":s=1:t=0");
spec.set(":t=9");
assert(spec.filename.empty());
assert((spec.locations
== std::vector<DataSpec::Location>
{{9, 1}}));
assert((std::string)spec == ":s=1:t=9");
}
int main(int argc, const char* argv[])