Looks like we're going to have to rework the reader/writer/source/sink

interfaces, so do fluxsink. This lets us test for overwriting a flux
file on writing in the GUI. HG: Enter commit message.
This commit is contained in:
David Given
2025-10-14 21:54:59 +02:00
parent 2de8b52e56
commit 2d6cb22e3a
14 changed files with 578 additions and 417 deletions

View File

@@ -52,7 +52,7 @@ need to apply extra options to change the format if desired.
## Options ## Options
- : - $format:
- `143`: 143kB 5.25" SSDD hard-sectored; Micropolis MetaFloppy Mod I - `143`: 143kB 5.25" SSDD hard-sectored; Micropolis MetaFloppy Mod I
- `287`: 287kB 5.25" DSDD hard-sectored; Micropolis MetaFloppy Mod I - `287`: 287kB 5.25" DSDD hard-sectored; Micropolis MetaFloppy Mod I
- `315`: 315kB 5.25" SSDD hard-sectored; Micropolis MetaFloppy Mod II - `315`: 315kB 5.25" SSDD hard-sectored; Micropolis MetaFloppy Mod II

View File

@@ -410,7 +410,7 @@ static ReadGroupResult readGroup(const DiskLayout& diskLayout,
void writeTracks(const DiskLayout& diskLayout, void writeTracks(const DiskLayout& diskLayout,
FluxSink& fluxSink, FluxSink& fluxSinkFactory,
std::function<std::unique_ptr<const Fluxmap>( std::function<std::unique_ptr<const Fluxmap>(
const std::shared_ptr<const LogicalTrackLayout>&)> producer, const std::shared_ptr<const LogicalTrackLayout>&)> producer,
std::function<bool(const std::shared_ptr<const LogicalTrackLayout>&)> std::function<bool(const std::shared_ptr<const LogicalTrackLayout>&)>
@@ -419,8 +419,10 @@ void writeTracks(const DiskLayout& diskLayout,
{ {
log(BeginOperationLogMessage{"Encoding and writing to disk"}); log(BeginOperationLogMessage{"Encoding and writing to disk"});
if (fluxSink.isHardware()) if (fluxSinkFactory.isHardware())
measureDiskRotation(); measureDiskRotation();
{
auto fluxSink = fluxSinkFactory.create();
int index = 0; int index = 0;
for (auto& ch : logicalLocations) for (auto& ch : logicalLocations)
{ {
@@ -449,7 +451,7 @@ void writeTracks(const DiskLayout& diskLayout,
if (!fluxmap) if (!fluxmap)
goto erase; goto erase;
fluxSink.writeFlux( fluxSink->addFlux(
physicalCylinder, physicalHead, *fluxmap); physicalCylinder, physicalHead, *fluxmap);
log("writing {0} ms in {1} bytes", log("writing {0} ms in {1} bytes",
int(fluxmap->duration() / 1e6), int(fluxmap->duration() / 1e6),
@@ -461,7 +463,8 @@ void writeTracks(const DiskLayout& diskLayout,
/* Erase this track rather than writing. */ /* Erase this track rather than writing. */
Fluxmap blank; Fluxmap blank;
fluxSink.writeFlux(physicalCylinder, physicalHead, blank); fluxSink->addFlux(
physicalCylinder, physicalHead, blank);
log("erased"); log("erased");
} }
@@ -478,6 +481,7 @@ void writeTracks(const DiskLayout& diskLayout,
retriesRemaining--; retriesRemaining--;
} }
} }
}
log(EndOperationLogMessage{"Write complete"}); log(EndOperationLogMessage{"Write complete"});
} }
@@ -672,17 +676,22 @@ void readDiskCommand(const DiskLayout& diskLayout,
Decoder& decoder, Decoder& decoder,
DecodedDisk& decodedDisk) DecodedDisk& decodedDisk)
{ {
std::unique_ptr<FluxSink> outputFluxSink; std::unique_ptr<FluxSink> outputFluxSinkFactory;
if (globalConfig()->decoder().has_copy_flux_to()) if (globalConfig()->decoder().has_copy_flux_to())
outputFluxSink = outputFluxSinkFactory =
FluxSink::create(globalConfig()->decoder().copy_flux_to()); FluxSink::create(globalConfig()->decoder().copy_flux_to());
log(BeginOperationLogMessage{"Reading and decoding disk"}); log(BeginOperationLogMessage{"Reading and decoding disk"});
{
std::unique_ptr<FluxSink::Sink> outputFluxSink;
if (outputFluxSinkFactory)
outputFluxSink = outputFluxSinkFactory->create();
unsigned index = 0; unsigned index = 0;
for (auto& [logicalLocation, ltl] : diskLayout.layoutByLogicalLocation) for (auto& [logicalLocation, ltl] : diskLayout.layoutByLogicalLocation)
{ {
log(OperationProgressLogMessage{ log(OperationProgressLogMessage{
index * 100 / (unsigned)diskLayout.layoutByLogicalLocation.size()}); index * 100 /
(unsigned)diskLayout.layoutByLogicalLocation.size()});
index++; index++;
testForEmergencyStop(); testForEmergencyStop();
@@ -701,7 +710,7 @@ void readDiskCommand(const DiskLayout& diskLayout,
if (outputFluxSink) if (outputFluxSink)
{ {
for (const auto& data : trackFluxes) for (const auto& data : trackFluxes)
outputFluxSink->writeFlux(data->ptl->physicalCylinder, outputFluxSink->addFlux(data->ptl->physicalCylinder,
data->ptl->physicalHead, data->ptl->physicalHead,
*data->fluxmap); *data->fluxmap);
} }
@@ -768,10 +777,11 @@ void readDiskCommand(const DiskLayout& diskLayout,
all_sectors = collectSectors(all_sectors); all_sectors = collectSectors(all_sectors);
decodedDisk.image = std::make_shared<Image>(all_sectors); decodedDisk.image = std::make_shared<Image>(all_sectors);
/* Log a _copy_ of the decodedDisk structure so that the logger doesn't /* Log a _copy_ of the decodedDisk structure so that the logger
* see the decodedDisk get mutated in subsequent reads. */ * doesn't see the decodedDisk get mutated in subsequent reads. */
log(DiskReadLogMessage{std::make_shared<DecodedDisk>(decodedDisk)}); log(DiskReadLogMessage{std::make_shared<DecodedDisk>(decodedDisk)});
} }
}
if (!decodedDisk.image) if (!decodedDisk.image)
decodedDisk.image = std::make_shared<Image>(); decodedDisk.image = std::make_shared<Image>();

View File

@@ -17,27 +17,25 @@
#include <sys/types.h> #include <sys/types.h>
#include "fmt/chrono.h" #include "fmt/chrono.h"
namespace static uint32_t ticks_to_a2r(uint32_t ticks)
{
uint32_t ticks_to_a2r(uint32_t ticks)
{ {
return ticks * NS_PER_TICK / A2R_NS_PER_TICK; return ticks * NS_PER_TICK / A2R_NS_PER_TICK;
} }
class A2RFluxSink : public FluxSink class A2RSink : public FluxSink::Sink
{ {
public: public:
A2RFluxSink(const A2RFluxSinkProto& lconfig): A2RSink(const std::string& filename):
_config(lconfig), _filename(filename),
_bytes{}, _bytes{},
_writer{_bytes.writer()} _writer(_bytes.writer())
{ {
time_t now{std::time(nullptr)}; time_t now{std::time(nullptr)};
auto t = gmtime(&now); auto t = gmtime(&now);
_metadata["image_date"] = fmt::format("{:%FT%TZ}", *t); _metadata["image_date"] = fmt::format("{:%FT%TZ}", *t);
} }
~A2RFluxSink() ~A2RSink()
{ {
// FIXME: should use a passed-in DiskLayout object. // FIXME: should use a passed-in DiskLayout object.
auto diskLayout = createDiskLayout(); auto diskLayout = createDiskLayout();
@@ -58,8 +56,7 @@ namespace
writeStream(); writeStream();
writeMeta(); writeMeta();
std::ofstream of( std::ofstream of(_filename, std::ios::out | std::ios::binary);
_config.filename(), std::ios::out | std::ios::binary);
if (!of.is_open()) if (!of.is_open())
error("cannot open output file"); error("cannot open output file");
_bytes.writeTo(of); _bytes.writeTo(of);
@@ -123,21 +120,22 @@ namespace
writeChunkAndData(A2R_CHUNK_STRM, _strmBytes); writeChunkAndData(A2R_CHUNK_STRM, _strmBytes);
} }
void writeFlux(int cylinder, int head, const Fluxmap& fluxmap) override public:
void addFlux(int cylinder, int head, const Fluxmap& fluxmap) override
{ {
if (!fluxmap.bytes()) if (!fluxmap.bytes())
{ {
return; return;
} }
// Writing from an image (as opposed to from a floppy) will contain // Writing from an image (as opposed to from a floppy) will
// exactly one revolution and no index events. // contain exactly one revolution and no index events.
auto is_image = [](auto& fluxmap) auto is_image = [](auto& fluxmap)
{ {
FluxmapReader fmr(fluxmap); FluxmapReader fmr(fluxmap);
fmr.skipToEvent(F_BIT_INDEX); fmr.skipToEvent(F_BIT_INDEX);
// but maybe there is no index, if we're writing from an image // but maybe there is no index, if we're writing from an
// to an a2r // image to an a2r
return fmr.eof(); return fmr.eof();
}; };
@@ -191,8 +189,8 @@ namespace
if (is_image(fluxmap)) if (is_image(fluxmap))
{ {
// A timing stream with no index represents exactly one // A timing stream with no index represents exactly one
// revolution with no index. However, a2r nominally contains 450 // revolution with no index. However, a2r nominally contains
// degress of rotation, 250ms at 300rpm. // 450 degress of rotation, 250ms at 300rpm.
write_flux(); write_flux();
loopPoint = totalTicks; loopPoint = totalTicks;
fmr.rewind(); fmr.rewind();
@@ -201,8 +199,8 @@ namespace
} }
else else
{ {
// We have an index, so this is a real read from a floppy and // We have an index, so this is a real read from a floppy
// should be "one revolution plus a bit" // and should be "one revolution plus a bit"
fmr.skipToEvent(F_BIT_INDEX); fmr.skipToEvent(F_BIT_INDEX);
write_flux(); write_flux();
} }
@@ -220,13 +218,8 @@ namespace
_strmWriter += trackBytes; _strmWriter += trackBytes;
} }
operator std::string() const override
{
return fmt::format("a2r({})", _config.filename());
}
private: private:
const A2RFluxSinkProto& _config; std::string _filename;
Bytes _bytes; Bytes _bytes;
ByteWriter _writer; ByteWriter _writer;
Bytes _strmBytes; Bytes _strmBytes;
@@ -237,7 +230,30 @@ namespace
int _minCylinder; int _minCylinder;
int _maxCylinder; int _maxCylinder;
}; };
} // namespace
class A2RFluxSink : public FluxSink
{
public:
A2RFluxSink(const A2RFluxSinkProto& lconfig): _config(lconfig) {}
std::unique_ptr<Sink> create() override
{
return std::make_unique<A2RSink>(_config.filename());
}
std::optional<std::filesystem::path> getPath() const override
{
return std::make_optional(_config.filename());
}
operator std::string() const override
{
return fmt::format("a2r({})", _config.filename());
}
private:
const A2RFluxSinkProto& _config;
};
std::unique_ptr<FluxSink> FluxSink::createA2RFluxSink( std::unique_ptr<FluxSink> FluxSink::createA2RFluxSink(
const A2RFluxSinkProto& config) const A2RFluxSinkProto& config)

View File

@@ -1,4 +1,5 @@
#include "lib/core/globals.h" #include "lib/core/globals.h"
#include "lib/core/logger.h"
#include "lib/config/flags.h" #include "lib/config/flags.h"
#include "lib/data/fluxmap.h" #include "lib/data/fluxmap.h"
#include "lib/core/bytes.h" #include "lib/core/bytes.h"
@@ -11,27 +12,29 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
class AuFluxSink : public FluxSink class AuSink : public FluxSink::Sink
{ {
public: public:
AuFluxSink(const AuFluxSinkProto& config): _config(config) {} AuSink(const std::string& directory, bool indexMarkers):
_directory(directory),
~AuFluxSink() _indexMarkers(indexMarkers)
{ {
std::cerr << "Warning: do not play these files, or you will break your "
"speakers and/or ears!\n";
} }
public: ~AuSink()
void writeFlux(int track, int head, const Fluxmap& fluxmap) override {
log("Warning: do not play these files, or you will break your "
"speakers and/or ears!");
}
void addFlux(int track, int head, const Fluxmap& fluxmap) override
{ {
unsigned totalTicks = fluxmap.ticks() + 2; unsigned totalTicks = fluxmap.ticks() + 2;
unsigned channels = _config.index_markers() ? 2 : 1; unsigned channels = _indexMarkers ? 2 : 1;
mkdir(_config.directory().c_str(), 0744); mkdir(_directory.c_str(), 0744);
std::ofstream of( std::ofstream of(
fmt::format( fmt::format("{}/c{:02d}.h{:01d}.au", _directory, track, head),
"{}/c{:02d}.h{:01d}.au", _config.directory(), track, head),
std::ios::out | std::ios::binary); std::ios::out | std::ios::binary);
if (!of.is_open()) if (!of.is_open())
error("cannot open output file"); error("cannot open output file");
@@ -73,7 +76,7 @@ public:
if (event & F_BIT_PULSE) if (event & F_BIT_PULSE)
data[timestamp * channels + 0] = 0x7f; data[timestamp * channels + 0] = 0x7f;
if (_config.index_markers() && (event & F_BIT_INDEX)) if (_indexMarkers && (event & F_BIT_INDEX))
data[timestamp * channels + 1] = 0x7f; data[timestamp * channels + 1] = 0x7f;
} }
@@ -81,6 +84,27 @@ public:
} }
} }
private:
std::string _directory;
bool _indexMarkers;
};
class AuFluxSink : public FluxSink
{
public:
AuFluxSink(const AuFluxSinkProto& config): _config(config) {}
std::unique_ptr<Sink> create() override
{
return std::make_unique<AuSink>(
_config.directory(), _config.index_markers());
}
std::optional<std::filesystem::path> getPath() const override
{
return std::make_optional(_config.directory());
}
operator std::string() const override operator std::string() const override
{ {
return fmt::format("au({})", _config.directory()); return fmt::format("au({})", _config.directory());

View File

@@ -16,15 +16,10 @@
#include <sys/types.h> #include <sys/types.h>
#include <filesystem> #include <filesystem>
class Fl2FluxSink : public FluxSink class Fl2Sink : public FluxSink::Sink
{ {
public: public:
Fl2FluxSink(const Fl2FluxSinkProto& lconfig): Fl2Sink(const std::string& filename): _filename(filename)
Fl2FluxSink(lconfig.filename())
{
}
Fl2FluxSink(const std::string& filename): _filename(filename)
{ {
std::ofstream of(filename); std::ofstream of(filename);
if (!of.is_open()) if (!of.is_open())
@@ -33,7 +28,7 @@ public:
std::filesystem::remove(filename); std::filesystem::remove(filename);
} }
~Fl2FluxSink() ~Fl2Sink()
{ {
log("FL2: writing {}", _filename); log("FL2: writing {}", _filename);
@@ -54,27 +49,46 @@ public:
saveFl2File(_filename, proto); saveFl2File(_filename, proto);
} }
public: void addFlux(int track, int head, const Fluxmap& fluxmap) override
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
{ {
auto& vector = _data[std::make_pair(track, head)]; auto& vector = _data[std::make_pair(track, head)];
vector.push_back(fluxmap.rawBytes()); vector.push_back(fluxmap.rawBytes());
} }
operator std::string() const override
{
return fmt::format("fl2({})", _filename);
}
private: private:
std::string _filename; std::string _filename;
std::map<std::pair<unsigned, unsigned>, std::vector<Bytes>> _data; std::map<std::pair<unsigned, unsigned>, std::vector<Bytes>> _data;
}; };
class Fl2FluxSink : public FluxSink
{
public:
Fl2FluxSink(const std::string& filename): _filename(filename) {}
std::unique_ptr<Sink> create() override
{
return std::make_unique<Fl2Sink>(_filename);
}
std::optional<std::filesystem::path> getPath() const override
{
return std::make_optional(_filename);
}
public:
operator std::string() const override
{
return fmt::format("fl2({})", _filename);
}
private:
const std::string _filename;
};
std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink( std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink(
const Fl2FluxSinkProto& config) const Fl2FluxSinkProto& config)
{ {
return std::unique_ptr<FluxSink>(new Fl2FluxSink(config)); return std::unique_ptr<FluxSink>(new Fl2FluxSink(config.filename()));
} }
std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink( std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink(

View File

@@ -4,6 +4,7 @@
#include "lib/config/flags.h" #include "lib/config/flags.h"
#include "lib/data/locations.h" #include "lib/data/locations.h"
#include <ostream> #include <ostream>
#include <filesystem>
class Fluxmap; class Fluxmap;
class FluxSinkProto; class FluxSinkProto;
@@ -40,14 +41,27 @@ public:
static std::unique_ptr<FluxSink> create(Config& config); static std::unique_ptr<FluxSink> create(Config& config);
static std::unique_ptr<FluxSink> create(const FluxSinkProto& config); static std::unique_ptr<FluxSink> create(const FluxSinkProto& config);
public:
class Sink
{
public:
Sink() {}
virtual ~Sink() {}
public: public:
/* Writes a fluxmap to a track and side. */ /* Writes a fluxmap to a track and side. */
virtual void writeFlux(int track, int side, const Fluxmap& fluxmap) = 0; virtual void addFlux(int track, int side, const Fluxmap& fluxmap) = 0;
void writeFlux(const CylinderHead& location, const Fluxmap& fluxmap) void addFlux(const CylinderHead& location, const Fluxmap& fluxmap)
{ {
writeFlux(location.cylinder, location.head, fluxmap); addFlux(location.cylinder, location.head, fluxmap);
} }
};
public:
/* Creates a writer object. */
virtual std::unique_ptr<Sink> create() = 0;
/* Returns whether this is writing to real hardware or not. */ /* Returns whether this is writing to real hardware or not. */
@@ -56,6 +70,14 @@ public:
return false; return false;
} }
/* Returns the path (filename or directory) being written to, if there is
* one. */
virtual std::optional<std::filesystem::path> getPath() const
{
return {};
}
virtual operator std::string() const = 0; virtual operator std::string() const = 0;
}; };

View File

@@ -8,15 +8,9 @@
#include "lib/fluxsink/fluxsink.h" #include "lib/fluxsink/fluxsink.h"
#include "lib/fluxsink/fluxsink.pb.h" #include "lib/fluxsink/fluxsink.pb.h"
class HardwareFluxSink : public FluxSink class HardwareSink : public FluxSink::Sink
{ {
public: void addFlux(int track, int side, const Fluxmap& fluxmap) override
HardwareFluxSink(const HardwareFluxSinkProto& conf): _config(conf) {}
~HardwareFluxSink() {}
public:
void writeFlux(int track, int side, const Fluxmap& fluxmap) override
{ {
auto& drive = globalConfig()->drive(); auto& drive = globalConfig()->drive();
usbSetDrive(drive.drive(), drive.high_density(), drive.index_mode()); usbSetDrive(drive.drive(), drive.high_density(), drive.index_mode());
@@ -25,6 +19,15 @@ public:
return usbWrite( return usbWrite(
side, fluxmap.rawBytes(), drive.hard_sector_threshold_ns()); side, fluxmap.rawBytes(), drive.hard_sector_threshold_ns());
} }
};
class HardwareFluxSink : public FluxSink
{
public:
std::unique_ptr<Sink> create() override
{
return std::make_unique<HardwareSink>();
}
bool isHardware() const override bool isHardware() const override
{ {
@@ -33,15 +36,12 @@ public:
operator std::string() const override operator std::string() const override
{ {
return fmt::format("drive {}", globalConfig()->drive().drive()); return "hardware {}";
} }
private:
const HardwareFluxSinkProto& _config;
}; };
std::unique_ptr<FluxSink> FluxSink::createHardwareFluxSink( std::unique_ptr<FluxSink> FluxSink::createHardwareFluxSink(
const HardwareFluxSinkProto& config) const HardwareFluxSinkProto& config)
{ {
return std::unique_ptr<FluxSink>(new HardwareFluxSink(config)); return std::unique_ptr<FluxSink>();
} }

View File

@@ -36,10 +36,13 @@ static void appendChecksum(uint32_t& checksum, const Bytes& bytes)
checksum += br.read_8(); checksum += br.read_8();
} }
class ScpFluxSink : public FluxSink class ScpSink : public FluxSink::Sink
{ {
public: public:
ScpFluxSink(const ScpFluxSinkProto& lconfig): _config(lconfig) ScpSink(const std::string& filename, uint8_t typeByte, bool alignWithIndex):
_filename(filename),
_typeByte(typeByte),
_alignWithIndex(alignWithIndex)
{ {
// FIXME: should use a passed-in DiskLayout object. // FIXME: should use a passed-in DiskLayout object.
auto diskLayout = createDiskLayout(); auto diskLayout = createDiskLayout();
@@ -50,7 +53,7 @@ public:
_fileheader.file_id[1] = 'C'; _fileheader.file_id[1] = 'C';
_fileheader.file_id[2] = 'P'; _fileheader.file_id[2] = 'P';
_fileheader.version = 0x18; /* Version 1.8 of the spec */ _fileheader.version = 0x18; /* Version 1.8 of the spec */
_fileheader.type = _config.type_byte(); _fileheader.type = _typeByte;
_fileheader.start_track = strackno(minCylinder, minHead); _fileheader.start_track = strackno(minCylinder, minHead);
_fileheader.end_track = strackno(maxCylinder, maxHead); _fileheader.end_track = strackno(maxCylinder, maxHead);
_fileheader.flags = SCP_FLAG_INDEXED; _fileheader.flags = SCP_FLAG_INDEXED;
@@ -72,7 +75,7 @@ public:
_fileheader.end_track - _fileheader.start_track + 1); _fileheader.end_track - _fileheader.start_track + 1);
} }
~ScpFluxSink() ~ScpSink()
{ {
uint32_t checksum = 0; uint32_t checksum = 0;
appendChecksum(checksum, appendChecksum(checksum,
@@ -82,7 +85,7 @@ public:
write_le32(_fileheader.checksum, checksum); write_le32(_fileheader.checksum, checksum);
log("SCP: writing output file"); log("SCP: writing output file");
std::ofstream of(_config.filename(), std::ios::out | std::ios::binary); std::ofstream of(_filename, std::ios::out | std::ios::binary);
if (!of.is_open()) if (!of.is_open())
error("cannot open output file"); error("cannot open output file");
of.write((const char*)&_fileheader, sizeof(_fileheader)); of.write((const char*)&_fileheader, sizeof(_fileheader));
@@ -90,8 +93,7 @@ public:
of.close(); of.close();
} }
public: void addFlux(int track, int head, const Fluxmap& fluxmap) override
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
{ {
ByteWriter trackdataWriter(_trackdata); ByteWriter trackdataWriter(_trackdata);
trackdataWriter.seekToEnd(); trackdataWriter.seekToEnd();
@@ -99,7 +101,8 @@ public:
if (strack >= std::size(_fileheader.track)) if (strack >= std::size(_fileheader.track))
{ {
log("SCP: cannot write track {} head {}, there are not not enough " log("SCP: cannot write track {} head {}, there are not not "
"enough "
"Track Data Headers.", "Track Data Headers.",
track, track,
head); head);
@@ -117,7 +120,7 @@ public:
int revolution = int revolution =
-1; // -1 indicates that we are before the first index pulse -1; // -1 indicates that we are before the first index pulse
if (_config.align_with_index()) if (_alignWithIndex)
{ {
fmr.skipToEvent(F_BIT_INDEX); fmr.skipToEvent(F_BIT_INDEX);
revolution = 0; revolution = 0;
@@ -136,9 +139,9 @@ public:
totalTicks += ticks; totalTicks += ticks;
revTicks += ticks; revTicks += ticks;
// if we haven't output any revolutions yet by the end of the track, // if we haven't output any revolutions yet by the end of the
// assume that the whole track is one rev // track, assume that the whole track is one rev also discard
// also discard any duplicate index pulses // any duplicate index pulses
if (((fmr.eof() && revolution <= 0) || if (((fmr.eof() && revolution <= 0) ||
((event & F_BIT_INDEX)) && revTicks > 0)) ((event & F_BIT_INDEX)) && revTicks > 0))
{ {
@@ -181,6 +184,32 @@ public:
trackdataWriter += fluxdata; trackdataWriter += fluxdata;
} }
private:
std::string _filename;
uint8_t _typeByte;
bool _alignWithIndex;
ScpHeader _fileheader = {0};
Bytes _trackdata;
};
class ScpFluxSink : public FluxSink
{
public:
ScpFluxSink(const ScpFluxSinkProto& lconfig): _config(lconfig) {}
std::unique_ptr<Sink> create() override
{
return std::make_unique<ScpSink>(_config.filename(),
_config.type_byte(),
_config.align_with_index());
}
std::optional<std::filesystem::path> getPath() const override
{
return std::make_optional(_config.filename());
}
public:
operator std::string() const override operator std::string() const override
{ {
return fmt::format("scp({})", _config.filename()); return fmt::format("scp({})", _config.filename());
@@ -188,8 +217,6 @@ public:
private: private:
const ScpFluxSinkProto& _config; const ScpFluxSinkProto& _config;
ScpHeader _fileheader = {0};
Bytes _trackdata;
}; };
std::unique_ptr<FluxSink> FluxSink::createScpFluxSink( std::unique_ptr<FluxSink> FluxSink::createScpFluxSink(

View File

@@ -11,18 +11,16 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
class VcdFluxSink : public FluxSink class VcdSink : public FluxSink::Sink
{ {
public: public:
VcdFluxSink(const VcdFluxSinkProto& config): _config(config) {} VcdSink(const std::string& directory): _directory(directory) {}
public: void addFlux(int track, int head, const Fluxmap& fluxmap) override
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
{ {
mkdir(_config.directory().c_str(), 0744); mkdir(_directory.c_str(), 0744);
std::ofstream of( std::ofstream of(
fmt::format( fmt::format("{}/c{:02d}.h{:01d}.vcd", _directory, track, head),
"{}/c{:02d}.h{:01d}.vcd", _config.directory(), track, head),
std::ios::out | std::ios::binary); std::ios::out | std::ios::binary);
if (!of.is_open()) if (!of.is_open())
error("cannot open output file"); error("cannot open output file");
@@ -64,6 +62,26 @@ public:
of << "\n"; of << "\n";
} }
private:
const std::string _directory;
};
class VcdFluxSink : public FluxSink
{
public:
VcdFluxSink(const VcdFluxSinkProto& config): _config(config) {}
std::unique_ptr<Sink> create() override
{
return std::make_unique<VcdSink>(_config.directory());
}
std::optional<std::filesystem::path> getPath() const override
{
return std::make_optional(_config.directory());
}
public:
operator std::string() const override operator std::string() const override
{ {
return fmt::format("vcd({})", _config.directory()); return fmt::format("vcd({})", _config.directory());

View File

@@ -53,13 +53,14 @@ int mainConvert(int argc, const char* argv[])
minHead, minHead,
maxHead); maxHead);
auto fluxSink = FluxSink::create(globalConfig()); auto fluxSinkFactory = FluxSink::create(globalConfig());
auto fluxSink = fluxSinkFactory->create();
for (const auto& physicalLocation : diskLayout->physicalLocations) for (const auto& physicalLocation : diskLayout->physicalLocations)
{ {
auto fi = fluxSource->readFlux(physicalLocation); auto fi = fluxSource->readFlux(physicalLocation);
while (fi->hasNext()) while (fi->hasNext())
fluxSink->writeFlux(physicalLocation, *fi->next()); fluxSink->addFlux(physicalLocation, *fi->next());
} }
return 0; return 0;

View File

@@ -3,6 +3,7 @@
#include <hex/api/imhex_api/hex_editor.hpp> #include <hex/api/imhex_api/hex_editor.hpp>
#include <hex/api/content_registry/settings.hpp> #include <hex/api/content_registry/settings.hpp>
#include <hex/api/task_manager.hpp> #include <hex/api/task_manager.hpp>
#include <popups/popup_question.hpp>
#include <toasts/toast_notification.hpp> #include <toasts/toast_notification.hpp>
#include "lib/core/globals.h" #include "lib/core/globals.h"
#include "lib/core/utils.h" #include "lib/core/utils.h"
@@ -580,6 +581,29 @@ void Datastore::beginWrite()
globalConfig().getVerificationFluxSourceProto()); globalConfig().getVerificationFluxSourceProto());
} }
auto path = fluxSink->getPath();
if (path.has_value() && std::filesystem::exists(*path))
{
{
bool result;
wtRunSynchronouslyOnUiThread((
std::function<void()>)[&] {
hex::ui::PopupQuestion::open(
"fluxengine.messages.writingFluxToFile"_lang,
[&]
{
result = true;
},
[&]
{
result = false;
});
});
if (!result)
throw EmergencyStopException();
}
}
auto image = diskFlux->image; auto image = diskFlux->image;
writeDiskCommand(*diskLayout, writeDiskCommand(*diskLayout,
*image, *image,
@@ -717,7 +741,9 @@ void Datastore::writeFluxFile(const std::fs::path& path)
error("no loaded image"); error("no loaded image");
if (diskFlux->image->getGeometry().totalBytes != if (diskFlux->image->getGeometry().totalBytes !=
diskLayout->totalBytes) diskLayout->totalBytes)
error("loaded image is not the right size for this format"); error(
"loaded image is not the right size for this "
"format");
globalConfig().setFluxSink(path.string()); globalConfig().setFluxSink(path.string());
auto fluxSource = FluxSource::createMemoryFluxSource(*diskFlux); auto fluxSource = FluxSource::createMemoryFluxSource(*diskFlux);

View File

@@ -101,9 +101,9 @@ void DiskProvider::writeRaw(u64 offset, const void* buffer, size_t size)
[[nodiscard]] u64 DiskProvider::getActualSize() const [[nodiscard]] u64 DiskProvider::getActualSize() const
{ {
const auto& diskFlux = Datastore::getDecodedDisk(); const auto& diskLayout = Datastore::getDiskLayout();
if (diskFlux && diskFlux->image) if (diskLayout)
return diskFlux->image->getGeometry().totalBytes; return diskLayout->totalBytes;
return 0; return 0;
} }

View File

@@ -52,5 +52,7 @@
"fluxengine.view.status.writeFlux": "Writing flux file to disk", "fluxengine.view.status.writeFlux": "Writing flux file to disk",
"fluxengine.view.status.readImage": "Reading image file from disk", "fluxengine.view.status.readImage": "Reading image file from disk",
"fluxengine.view.status.writeImage": "Writing image file to disk", "fluxengine.view.status.writeImage": "Writing image file to disk",
"fluxengine.view.status.blankFilesystem": "Creating blank filesystem" "fluxengine.view.status.blankFilesystem": "Creating blank filesystem",
"fluxengine.messages.writingFluxToFile": "The current configuration is to write to a flux file rather than to hardware. Is this what you intended?"
} }

View File

@@ -271,7 +271,8 @@ int main(int argc, const char* argv[])
int version = sqlGetVersion(db); int version = sqlGetVersion(db);
{ {
auto fluxsink = FluxSink::createFl2FluxSink(outFilename); auto fluxSink = FluxSink::createFl2FluxSink(outFilename)->create();
for (const auto& locations : sqlFindFlux(db)) for (const auto& locations : sqlFindFlux(db))
{ {
unsigned cylinder = locations.first; unsigned cylinder = locations.first;
@@ -295,7 +296,7 @@ int main(int argc, const char* argv[])
"bug)", "bug)",
version); version);
} }
fluxsink->writeFlux(cylinder, head, fluxmap); fluxSink->addFlux(cylinder, head, fluxmap);
std::cout << '.' << std::flush; std::cout << '.' << std::flush;
} }