#include "globals.h" #include "flags.h" #include "usb/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 "bytes.h" #include "decoders/rawbits.h" #include "track.h" #include "imagewriter/imagewriter.h" #include "fmt/format.h" #include #include 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 StringFlag csvFile( { "--write-csv" }, "write a CSV report of the disk state", ""); static std::unique_ptr 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); } static void writeSectorsToFile(const SectorSet& sectors, const ImageSpec& spec) { std::unique_ptr writer(ImageWriter::create(sectors, spec)); writer->adjustGeometry(); writer->printMap(); if (!csvFile.get().empty()) writer->writeCsv(csvFile.get()); writer->writeImage(); } 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", fluxmap->duration()/1e6, fluxmap->bytes()); if (outputFluxSink) outputFluxSink->writeFlux(physicalTrack, physicalSide, *fluxmap); } std::vector> 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::create(spec); std::vector> tracks; for (const auto& location : spec.locations) { auto track = std::make_unique(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& 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> readSectors; for (int retry = ::retries; retry >= 0; retry--) { track->readFluxmap(); decoder.decodeToSectors(*track); std::cout << " "; 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; std::set requiredSectors = decoder.requiredSectors(*track); for (const auto& i : readSectors) { const auto& sector = i.second; requiredSectors.erase(sector->logicalSector); if (sector->status != Sector::OK) { std::cout << std::endl << " Failed to read sector " << sector->logicalSector << " (" << Sector::statusToString((Sector::Status)sector->status) << "); "; hasBadSectors = true; } } for (unsigned logicalSector : requiredSectors) { std::cout << "\n" << " Required sector " << logicalSector << " missing; "; 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& sector : track->sectors) { std::cout << fmt::format("{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock: status {}\n", sector.logicalTrack, sector.logicalSide, sector.logicalSector, sector.position.ns() / 1000.0, sector.clock / 1000.0, sector.status); 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& 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; }