mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Convert scptoflux to a proper flux source.
This commit is contained in:
@@ -33,6 +33,9 @@ std::unique_ptr<FluxSource> FluxSource::create(const FluxSourceProto& config)
|
|||||||
|
|
||||||
case FluxSourceProto::kTestPattern:
|
case FluxSourceProto::kTestPattern:
|
||||||
return createTestPatternFluxSource(config.test_pattern());
|
return createTestPatternFluxSource(config.test_pattern());
|
||||||
|
|
||||||
|
case FluxSourceProto::kScp:
|
||||||
|
return createScpFluxSource(config.scp());
|
||||||
}
|
}
|
||||||
|
|
||||||
Error() << "bad input disk configuration";
|
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 =
|
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("^(.*\\.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("^erase:$"), [&](const auto& s) { f->mutable_erase(); }},
|
||||||
{ std::regex("^kryoflux:(.*)$"), [&](const auto& s) { f->mutable_kryoflux()->set_directory(s); }},
|
{ std::regex("^kryoflux:(.*)$"), [&](const auto& s) { f->mutable_kryoflux()->set_directory(s); }},
|
||||||
{ std::regex("^testpattern:(.*)"), [&](const auto& s) { f->mutable_test_pattern(); }},
|
{ std::regex("^testpattern:(.*)"), [&](const auto& s) { f->mutable_test_pattern(); }},
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class HardwareFluxSourceProto;
|
|||||||
class TestPatternFluxSourceProto;
|
class TestPatternFluxSourceProto;
|
||||||
class EraseFluxSourceProto;
|
class EraseFluxSourceProto;
|
||||||
class KryofluxFluxSourceProto;
|
class KryofluxFluxSourceProto;
|
||||||
|
class ScpFluxSourceProto;
|
||||||
|
|
||||||
class FluxSource
|
class FluxSource
|
||||||
{
|
{
|
||||||
@@ -20,6 +21,7 @@ private:
|
|||||||
static std::unique_ptr<FluxSource> createSqliteFluxSource(const std::string& filename);
|
static std::unique_ptr<FluxSource> createSqliteFluxSource(const std::string& filename);
|
||||||
static std::unique_ptr<FluxSource> createHardwareFluxSource(const HardwareFluxSourceProto& config);
|
static std::unique_ptr<FluxSource> createHardwareFluxSource(const HardwareFluxSourceProto& config);
|
||||||
static std::unique_ptr<FluxSource> createKryofluxFluxSource(const KryofluxFluxSourceProto& 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> createTestPatternFluxSource(const TestPatternFluxSourceProto& config);
|
||||||
static std::unique_ptr<FluxSource> createEraseFluxSource(const EraseFluxSourceProto& config);
|
static std::unique_ptr<FluxSource> createEraseFluxSource(const EraseFluxSourceProto& config);
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ message KryofluxFluxSourceProto {
|
|||||||
optional string directory = 1 [(help) = "path to Kryoflux stream directory"];
|
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 {
|
message FluxSourceProto {
|
||||||
oneof source {
|
oneof source {
|
||||||
string fluxfile = 1;
|
string fluxfile = 1;
|
||||||
@@ -31,6 +36,7 @@ message FluxSourceProto {
|
|||||||
TestPatternFluxSourceProto test_pattern = 3;
|
TestPatternFluxSourceProto test_pattern = 3;
|
||||||
EraseFluxSourceProto erase = 4;
|
EraseFluxSourceProto erase = 4;
|
||||||
KryofluxFluxSourceProto kryoflux = 5;
|
KryofluxFluxSourceProto kryoflux = 5;
|
||||||
|
ScpFluxSourceProto scp = 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
130
lib/fluxsource/scpfluxsource.cc
Normal file
130
lib/fluxsource/scpfluxsource.cc
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -27,6 +27,8 @@ void Track::readFluxmap()
|
|||||||
{
|
{
|
||||||
std::cout << fmt::format("{0:>3}.{1}: ", physicalTrack, physicalSide) << std::flush;
|
std::cout << fmt::format("{0:>3}.{1}: ", physicalTrack, physicalSide) << std::flush;
|
||||||
fluxmap = fluxsource->readFlux(physicalTrack, physicalSide);
|
fluxmap = fluxsource->readFlux(physicalTrack, physicalSide);
|
||||||
|
if (!fluxmap)
|
||||||
|
fluxmap.reset(new Fluxmap());
|
||||||
std::cout << fmt::format(
|
std::cout << fmt::format(
|
||||||
"{0} ms in {1} bytes\n",
|
"{0} ms in {1} bytes\n",
|
||||||
fluxmap->duration()/1e6,
|
fluxmap->duration()/1e6,
|
||||||
@@ -35,42 +37,6 @@ void Track::readFluxmap()
|
|||||||
outputFluxSink->writeFlux(physicalTrack, physicalSide, *fluxmap);
|
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)
|
static bool conflictable(Sector::Status status)
|
||||||
{
|
{
|
||||||
return (status == Sector::OK) || (status == Sector::CONFLICT);
|
return (status == Sector::OK) || (status == Sector::CONFLICT);
|
||||||
|
|||||||
@@ -325,6 +325,7 @@ buildlibrary libbackend.a \
|
|||||||
lib/fluxsource/hardwarefluxsource.cc \
|
lib/fluxsource/hardwarefluxsource.cc \
|
||||||
lib/fluxsource/kryoflux.cc \
|
lib/fluxsource/kryoflux.cc \
|
||||||
lib/fluxsource/kryofluxfluxsource.cc \
|
lib/fluxsource/kryofluxfluxsource.cc \
|
||||||
|
lib/fluxsource/scpfluxsource.cc \
|
||||||
lib/fluxsource/sqlitefluxsource.cc \
|
lib/fluxsource/sqlitefluxsource.cc \
|
||||||
lib/fluxsource/testpatternfluxsource.cc \
|
lib/fluxsource/testpatternfluxsource.cc \
|
||||||
lib/globals.cc \
|
lib/globals.cc \
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ static StringFlag sourceFlux(
|
|||||||
"",
|
"",
|
||||||
[](const auto& value)
|
[](const auto& value)
|
||||||
{
|
{
|
||||||
config.mutable_input()->mutable_flux()->set_fluxfile(value);
|
FluxSource::updateConfigForFilename(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
static IntFlag sourceDrive(
|
static IntFlag sourceDrive(
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ static StringFlag sourceFlux(
|
|||||||
"",
|
"",
|
||||||
[](const auto& value)
|
[](const auto& value)
|
||||||
{
|
{
|
||||||
config.mutable_input()->mutable_flux()->set_fluxfile(value);
|
FluxSource::updateConfigForFilename(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
static IntFlag sourceDrive(
|
static IntFlag sourceDrive(
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -47,7 +47,6 @@ static std::vector<Command> commands =
|
|||||||
static std::vector<Command> convertables =
|
static std::vector<Command> convertables =
|
||||||
{
|
{
|
||||||
{ "cwftoflux", mainConvertCwfToFlux, "Converts CatWeasel stream files to flux.", },
|
{ "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.", },
|
{ "image", mainConvertImage, "Converts one disk image to another.", },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user