Convert scptoflux to a proper flux source.

This commit is contained in:
David Given
2021-05-18 01:09:55 +02:00
parent c79feb405c
commit d246fca9df
11 changed files with 147 additions and 179 deletions

View File

@@ -33,6 +33,9 @@ std::unique_ptr<FluxSource> FluxSource::create(const FluxSourceProto& config)
case FluxSourceProto::kTestPattern:
return createTestPatternFluxSource(config.test_pattern());
case FluxSourceProto::kScp:
return createScpFluxSource(config.scp());
}
Error() << "bad input disk configuration";
@@ -45,6 +48,7 @@ void FluxSource::updateConfigForFilename(const std::string& filename)
static const std::vector<std::pair<std::regex, std::function<void(const std::string&)>>> formats =
{
{ std::regex("^(.*\\.flux)$"), [&](const auto& s) { f->set_fluxfile(s); }},
{ std::regex("^(.*\\.scp)$"), [&](const auto& s) { f->mutable_scp()->set_filename(s); }},
{ std::regex("^erase:$"), [&](const auto& s) { f->mutable_erase(); }},
{ std::regex("^kryoflux:(.*)$"), [&](const auto& s) { f->mutable_kryoflux()->set_directory(s); }},
{ std::regex("^testpattern:(.*)"), [&](const auto& s) { f->mutable_test_pattern(); }},

View File

@@ -10,6 +10,7 @@ class HardwareFluxSourceProto;
class TestPatternFluxSourceProto;
class EraseFluxSourceProto;
class KryofluxFluxSourceProto;
class ScpFluxSourceProto;
class FluxSource
{
@@ -20,6 +21,7 @@ private:
static std::unique_ptr<FluxSource> createSqliteFluxSource(const std::string& filename);
static std::unique_ptr<FluxSource> createHardwareFluxSource(const HardwareFluxSourceProto& config);
static std::unique_ptr<FluxSource> createKryofluxFluxSource(const KryofluxFluxSourceProto& config);
static std::unique_ptr<FluxSource> createScpFluxSource(const ScpFluxSourceProto& config);
static std::unique_ptr<FluxSource> createTestPatternFluxSource(const TestPatternFluxSourceProto& config);
static std::unique_ptr<FluxSource> createEraseFluxSource(const EraseFluxSourceProto& config);

View File

@@ -24,6 +24,11 @@ message KryofluxFluxSourceProto {
optional string directory = 1 [(help) = "path to Kryoflux stream directory"];
}
message ScpFluxSourceProto {
optional string filename = 1 [default = "flux.scp",
(help) = ".scp file to read flux from"];
}
message FluxSourceProto {
oneof source {
string fluxfile = 1;
@@ -31,6 +36,7 @@ message FluxSourceProto {
TestPatternFluxSourceProto test_pattern = 3;
EraseFluxSourceProto erase = 4;
KryofluxFluxSourceProto kryoflux = 5;
ScpFluxSourceProto scp = 6;
}
}

View File

@@ -0,0 +1,130 @@
#include "globals.h"
#include "fluxmap.h"
#include "kryoflux.h"
#include "lib/fluxsource/fluxsource.pb.h"
#include "fluxsource/fluxsource.h"
#include "scp.h"
#include "proto.h"
#include "fmt/format.h"
#include <fstream>
static int trackno(int strack)
{
return strack >> 1;
}
static int headno(int strack)
{
return strack & 1;
}
static int strackno(int track, int side)
{
return (track << 1) | side;
}
class ScpFluxSource : public FluxSource
{
public:
ScpFluxSource(const ScpFluxSourceProto& config):
_config(config)
{
_if.open(_config.filename(), std::ios::in | std::ios::binary);
if (!_if.is_open())
Error() << fmt::format("cannot open input file '{}': {}", _config.filename(), strerror(errno));
_if.read((char*) &_header, sizeof(_header));
check_for_error();
if ((_header.file_id[0] != 'S')
|| (_header.file_id[1] != 'C')
|| (_header.file_id[2] != 'P'))
Error() << "input not a SCP file";
_resolution = 25 * (_header.resolution + 1);
int startSide = (_header.heads == 2) ? 1 : 0;
int endSide = (_header.heads == 1) ? 0 : 1;
if ((_header.cell_width != 0) && (_header.cell_width != 16))
Error() << "currently only 16-bit cells in SCP files are supported";
std::cout << fmt::format("SCP tracks {}-{}, heads {}-{}\n",
trackno(_header.start_track), trackno(_header.end_track), startSide, endSide);
std::cout << fmt::format("SCP sample resolution: {} ns\n", _resolution);
}
public:
std::unique_ptr<Fluxmap> readFlux(int track, int side)
{
int strack = strackno(track, side);
uint32_t offset = Bytes(_header.track[strack], 4).reader().read_le32();
if (offset == 0)
return std::unique_ptr<Fluxmap>();
ScpTrack trackheader;
_if.seekg(offset, std::ios::beg);
_if.read((char*) &trackheader, sizeof(trackheader));
check_for_error();
if ((trackheader.track_id[0] != 'T')
|| (trackheader.track_id[1] != 'R')
|| (trackheader.track_id[2] != 'K'))
Error() << "corrupt SCP file";
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
nanoseconds_t pending = 0;
unsigned inputBytes = 0;
for (int revolution = 0; revolution < _header.revolutions; revolution++)
{
if (revolution != 0)
fluxmap->appendIndex();
uint32_t datalength = Bytes(trackheader.revolution[revolution].length, 4).reader().read_le32();
uint32_t dataoffset = Bytes(trackheader.revolution[revolution].offset, 4).reader().read_le32();
Bytes data(datalength*2);
_if.seekg(dataoffset + offset, std::ios::beg);
_if.read((char*) data.begin(), data.size());
check_for_error();
ByteReader br(data);
for (int cell = 0; cell < datalength; cell++)
{
uint16_t interval = br.read_be16();
if (interval)
{
fluxmap->appendInterval((interval + pending) * _resolution / NS_PER_TICK);
fluxmap->appendPulse();
pending = 0;
}
else
pending += 0x10000;
}
inputBytes += datalength*2;
}
return fluxmap;
}
void recalibrate() {}
private:
void check_for_error()
{
if (_if.fail())
Error() << fmt::format("SCP read I/O error: {}", strerror(errno));
}
private:
const ScpFluxSourceProto& _config;
std::ifstream _if;
ScpHeader _header;
nanoseconds_t _resolution;
};
std::unique_ptr<FluxSource> FluxSource::createScpFluxSource(const ScpFluxSourceProto& config)
{
return std::unique_ptr<FluxSource>(new ScpFluxSource(config));
}

View File

@@ -27,6 +27,8 @@ void Track::readFluxmap()
{
std::cout << fmt::format("{0:>3}.{1}: ", physicalTrack, physicalSide) << std::flush;
fluxmap = fluxsource->readFlux(physicalTrack, physicalSide);
if (!fluxmap)
fluxmap.reset(new Fluxmap());
std::cout << fmt::format(
"{0} ms in {1} bytes\n",
fluxmap->duration()/1e6,
@@ -35,42 +37,6 @@ void Track::readFluxmap()
outputFluxSink->writeFlux(physicalTrack, physicalSide, *fluxmap);
}
#if 0
std::vector<std::unique_ptr<Track>> readTracks(FluxSource& fluxSource)
{
const FluxSpec spec(source);
std::cout << "Reading from: " << source << std::endl;
if (!destination.get().empty())
{
std::cout << "Writing a copy of the flux to " << destination.get() << std::endl;
outputFluxSink = FluxSink::createSqliteFluxSink(destination.get());
}
std::shared_ptr<FluxSource> fluxSource = FluxSource::create(spec);
std::vector<std::unique_ptr<Track>> tracks;
for (const auto& location : spec.locations)
{
auto track = std::make_unique<Track>(location.track, location.side);
track->fluxsource = fluxSource;
tracks.push_back(std::move(track));
}
if (justRead)
{
for (auto& track : tracks)
track->readFluxmap();
std::cout << "--just-read specified, halting now" << std::endl;
exit(0);
}
return tracks;
}
#endif
static bool conflictable(Sector::Status status)
{
return (status == Sector::OK) || (status == Sector::CONFLICT);

View File

View File

@@ -325,6 +325,7 @@ buildlibrary libbackend.a \
lib/fluxsource/hardwarefluxsource.cc \
lib/fluxsource/kryoflux.cc \
lib/fluxsource/kryofluxfluxsource.cc \
lib/fluxsource/scpfluxsource.cc \
lib/fluxsource/sqlitefluxsource.cc \
lib/fluxsource/testpatternfluxsource.cc \
lib/globals.cc \

View File

@@ -26,7 +26,7 @@ static StringFlag sourceFlux(
"",
[](const auto& value)
{
config.mutable_input()->mutable_flux()->set_fluxfile(value);
FluxSource::updateConfigForFilename(value);
});
static IntFlag sourceDrive(

View File

@@ -25,7 +25,7 @@ static StringFlag sourceFlux(
"",
[](const auto& value)
{
config.mutable_input()->mutable_flux()->set_fluxfile(value);
FluxSource::updateConfigForFilename(value);
});
static IntFlag sourceDrive(

View File

@@ -1,140 +0,0 @@
#include "globals.h"
#include "fluxmap.h"
#include "sql.h"
#include "bytes.h"
#include "protocol.h"
#include "fmt/format.h"
#include "scp.h"
#include <fstream>
static std::ifstream inputFile;
static sqlite3* outputDb;
static ScpHeader header;
static nanoseconds_t resolution;
static int startSide;
static int endSide;
static void syntax()
{
std::cout << "Syntax: fluxengine convert scptoflux <scpfile> <fluxfile>\n";
exit(0);
}
static void check_for_error()
{
if (inputFile.fail())
Error() << fmt::format("I/O error: {}", strerror(errno));
}
static int trackno(int strack)
{
return strack >> 1;
}
static int headno(int strack)
{
return strack & 1;
}
static void read_header()
{
inputFile.read((char*) &header, sizeof(header));
check_for_error();
if ((header.file_id[0] != 'S')
|| (header.file_id[1] != 'C')
|| (header.file_id[2] != 'P'))
Error() << "input not a SCP file";
resolution = 25 * (header.resolution + 1);
startSide = (header.heads == 2) ? 1 : 0;
endSide = (header.heads == 1) ? 0 : 1;
if ((header.cell_width != 0) && (header.cell_width != 16))
Error() << "currently only 16-bit cells are supported";
std::cout << fmt::format("tracks {}-{}, heads {}-{}\n",
trackno(header.start_track), trackno(header.end_track), startSide, endSide);
std::cout << fmt::format("sample resolution: {} ns\n", resolution);
}
static void read_track(int strack)
{
uint32_t offset = Bytes(header.track[strack], 4).reader().read_le32();
if (offset == 0)
return;
ScpTrack trackheader;
inputFile.seekg(offset, std::ios::beg);
inputFile.read((char*) &trackheader, sizeof(trackheader));
check_for_error();
if ((trackheader.track_id[0] != 'T')
|| (trackheader.track_id[1] != 'R')
|| (trackheader.track_id[2] != 'K'))
Error() << "corrupt SCP file";
std::cout << fmt::format("{}.{}: ", trackno(strack), headno(strack))
<< std::flush;
Fluxmap fluxmap;
nanoseconds_t pending = 0;
unsigned inputBytes = 0;
for (int revolution = 0; revolution < header.revolutions; revolution++)
{
if (revolution != 0)
fluxmap.appendIndex();
uint32_t datalength = Bytes(trackheader.revolution[revolution].length, 4).reader().read_le32();
uint32_t dataoffset = Bytes(trackheader.revolution[revolution].offset, 4).reader().read_le32();
Bytes data(datalength*2);
inputFile.seekg(dataoffset + offset, std::ios::beg);
inputFile.read((char*) data.begin(), data.size());
check_for_error();
ByteReader br(data);
for (int cell = 0; cell < datalength; cell++)
{
uint16_t interval = br.read_be16();
if (interval)
{
fluxmap.appendInterval((interval + pending) * resolution / NS_PER_TICK);
fluxmap.appendPulse();
pending = 0;
}
else
pending += 0x10000;
}
inputBytes += datalength*2;
}
std::cout << fmt::format(" {:.3f} ms in {} input bytes and {} output bytes\n",
fluxmap.duration() / 1e6, inputBytes, fluxmap.bytes());
sqlWriteFlux(outputDb, trackno(strack), headno(strack), fluxmap);
}
int mainConvertScpToFlux(int argc, const char* argv[])
{
if (argc != 3)
syntax();
inputFile.open(argv[1], std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << fmt::format("cannot open input file '{}'", argv[1]);
outputDb = sqlOpen(argv[2], SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
sqlPrepareFlux(outputDb);
sqlWriteIntProperty(outputDb, "version", FLUX_VERSION_CURRENT);
sqlStmt(outputDb, "BEGIN;");
read_header();
inputFile.seekg(sizeof(header), std::ios::beg);
for (unsigned i=header.start_track; i<=header.end_track; i++)
read_track(i);
sqlStmt(outputDb, "COMMIT;");
sqlClose(outputDb);
return 0;
}

View File

@@ -47,7 +47,6 @@ static std::vector<Command> commands =
static std::vector<Command> convertables =
{
{ "cwftoflux", mainConvertCwfToFlux, "Converts CatWeasel stream files to flux.", },
{ "scptoflux", mainConvertScpToFlux, "Converts Supercard Pro stream files to flux.", },
{ "image", mainConvertImage, "Converts one disk image to another.", },
};