Files
fluxengine/lib/reader.cc
David Given be41c1de76 You now need to explicitly specify whether flux files get merged or
overwritten, because the old behaviour was weird. Reader uses SqliteFluxSink to
write flux files, rather than raw database accesses.
2020-02-08 12:41:50 +01:00

282 lines
7.4 KiB
C++

#include "globals.h"
#include "flags.h"
#include "usb.h"
#include "fluxsource/fluxsource.h"
#include "fluxsink/fluxsink.h"
#include "reader.h"
#include "fluxmap.h"
#include "sql.h"
#include "dataspec.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "sectorset.h"
#include "visualiser.h"
#include "record.h"
#include "image.h"
#include "bytes.h"
#include "decoders/rawbits.h"
#include "track.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
FlagGroup readerFlags
{
&hardwareFluxSourceFlags,
&sqliteFluxSinkFlags,
&fluxmapReaderFlags,
&visualiserFlags
};
static DataSpecFlag source(
{ "--source", "-s" },
"source for data",
":t=0-79:s=0-1:d=0");
static DataSpecFlag output(
{ "--output", "-o" },
"output image file to write to",
"");
static StringFlag destination(
{ "--write-flux", "-f" },
"write the raw magnetic flux to this file",
"");
static StringFlag visualise(
{ "--write-svg" },
"write a visualisation of the disk to this file",
"");
static SettableFlag justRead(
{ "--just-read" },
"just read the disk and do no further processing");
static SettableFlag dumpRecords(
{ "--dump-records" },
"Dump the parsed but undecoded records.");
static SettableFlag dumpSectors(
{ "--dump-sectors" },
"Dump the decoded sectors.");
static IntFlag retries(
{ "--retries" },
"How many times to retry each track in the event of a read failure.",
5);
static SettableFlag highDensityFlag(
{ "--high-density", "--hd" },
"set the drive to high density mode");
static std::unique_ptr<FluxSink> outputFluxSink;
void setReaderDefaultSource(const std::string& source)
{
::source.set(source);
}
void setReaderDefaultOutput(const std::string& output)
{
::output.set(output);
}
void setReaderRevolutions(int revolutions)
{
setHardwareFluxSourceRevolutions(revolutions);
}
void Track::readFluxmap()
{
std::cout << fmt::format("{0:>3}.{1}: ", physicalTrack, physicalSide) << std::flush;
fluxmap = fluxsource->readFlux(physicalTrack, physicalSide);
std::cout << fmt::format(
"{0} ms in {1} bytes\n",
int(fluxmap->duration()/1e6),
fluxmap->bytes());
if (outputFluxSink)
outputFluxSink->writeFlux(physicalTrack, physicalSide, *fluxmap);
}
std::vector<std::unique_ptr<Track>> readTracks()
{
const FluxSpec spec(source);
std::cout << "Reading from: " << source << std::endl;
setHardwareFluxSourceDensity(highDensityFlag);
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;
}
static bool conflictable(Sector::Status status)
{
return (status == Sector::OK) || (status == Sector::CONFLICT);
}
static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replacement)
{
if (replacing && conflictable(replacing->status) && conflictable(replacement.status))
{
if (replacement.data != replacing->data)
{
std::cout << std::endl
<< " multiple conflicting copies of sector " << replacing->logicalSector
<< " seen; ";
replacing->status = Sector::CONFLICT;
return;
}
}
if (!replacing || ((replacing->status != Sector::OK) && (replacement.status == Sector::OK)))
{
if (!replacing)
replacing.reset(new Sector);
*replacing = replacement;
}
}
void readDiskCommand(AbstractDecoder& decoder)
{
const ImageSpec outputSpec(output);
ImageWriter::verifyImageSpec(outputSpec);
bool failures = false;
SectorSet allSectors;
auto tracks = readTracks();
for (const auto& track : tracks)
{
std::map<int, std::unique_ptr<Sector>> readSectors;
for (int retry = ::retries; retry >= 0; retry--)
{
track->readFluxmap();
decoder.decodeToSectors(*track);
std::cout << " ";
if (!track->sectors.empty())
{
std::cout << fmt::format("{} records, {} sectors; ",
track->rawrecords.size(),
track->sectors.size());
if (track->sectors.size() > 0)
std::cout << fmt::format("{:.2f}us clock ({:.0f}kHz); ",
track->sectors.begin()->clock / 1000.0,
1000000.0 / track->sectors.begin()->clock);
for (auto& sector : track->sectors)
{
auto& replacing = readSectors[sector.logicalSector];
replace_sector(replacing, sector);
}
bool hasBadSectors = false;
for (const auto& i : readSectors)
{
const auto& sector = i.second;
if (sector->status != Sector::OK)
{
std::cout << std::endl
<< " Failed to read sector " << sector->logicalSector
<< " (" << Sector::statusToString((Sector::Status)sector->status) << "); ";
hasBadSectors = true;
}
}
if (hasBadSectors)
failures = false;
std::cout << std::endl
<< " ";
if (!hasBadSectors)
break;
}
if (!track->fluxsource->retryable())
break;
if (retry == 0)
std::cout << "giving up" << std::endl
<< " ";
else
std::cout << retry << " retries remaining" << std::endl;
}
if (dumpRecords)
{
std::cout << "\nRaw (undecoded) records follow:\n\n";
for (auto& record : track->rawrecords)
{
std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n",
record.position.ns() / 1000.0, record.clock / 1000.0);
hexdump(std::cout, record.data);
std::cout << std::endl;
}
}
if (dumpSectors)
{
std::cout << "\nDecoded sectors follow:\n\n";
for (auto& i : readSectors)
{
auto& sector = i.second;
std::cout << fmt::format("{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock\n",
sector->logicalTrack, sector->logicalSide, sector->logicalSector,
sector->position.ns() / 1000.0, sector->clock / 1000.0);
hexdump(std::cout, sector->data);
std::cout << std::endl;
}
}
int size = 0;
bool printedTrack = false;
for (auto& i : readSectors)
{
auto& sector = i.second;
if (sector)
{
if (!printedTrack)
{
std::cout << fmt::format("logical track {}.{}; ", sector->logicalTrack, sector->logicalSide);
printedTrack = true;
}
size += sector->data.size();
std::unique_ptr<Sector>& replacing = allSectors.get(sector->logicalTrack, sector->logicalSide, sector->logicalSector);
replace_sector(replacing, *sector);
}
}
std::cout << size << " bytes decoded." << std::endl;
}
if (!visualise.get().empty())
visualiseSectorsToFile(allSectors, visualise.get());
writeSectorsToFile(allSectors, outputSpec);
if (failures)
std::cerr << "Warning: some sectors could not be decoded." << std::endl;
}