Wire up some more control panel buttons. Create an in-memory sector

interface for doing filesystem stuff.
This commit is contained in:
David Given
2025-09-15 20:06:16 +02:00
parent 98f7febef7
commit 6127c9a46d
12 changed files with 210 additions and 47 deletions

View File

@@ -85,6 +85,22 @@ std::vector<CylinderHead> Layout::computePhysicalLocations()
return locations;
}
std::vector<CylinderHead> Layout::computeLogicalLocations()
{
if (!globalConfig()->tracks().empty())
return parseCylinderHeadsString(globalConfig()->tracks());
std::set<unsigned> tracks = iterate(0, globalConfig()->layout().tracks());
std::set<unsigned> heads = iterate(0, globalConfig()->layout().sides());
std::vector<CylinderHead> locations;
for (unsigned logicalCylinder : tracks)
for (unsigned logicalHead : heads)
locations.push_back(CylinderHead{logicalCylinder, logicalHead});
return locations;
}
Layout::LayoutBounds Layout::getBounds(
const std::vector<CylinderHead>& locations)
{
@@ -105,7 +121,9 @@ Layout::LayoutBounds Layout::getBounds(
}
std::vector<std::pair<int, int>> Layout::getTrackOrdering(
LayoutProto::Order ordering, unsigned guessedCylinders, unsigned guessedHeads)
LayoutProto::Order ordering,
unsigned guessedCylinders,
unsigned guessedHeads)
{
auto layout = globalConfig()->layout();
int tracks = layout.has_tracks() ? layout.tracks() : guessedCylinders;
@@ -206,7 +224,8 @@ std::shared_ptr<const TrackInfo> Layout::getLayoutOfTrack(
for (const auto& f : globalConfig()->layout().layoutdata())
{
if (f.has_track() && f.has_up_to_track() &&
((logicalCylinder < f.track()) || (logicalCylinder > f.up_to_track())))
((logicalCylinder < f.track()) ||
(logicalCylinder > f.up_to_track())))
continue;
if (f.has_track() && !f.has_up_to_track() &&
(logicalCylinder != f.track()))
@@ -222,7 +241,8 @@ std::shared_ptr<const TrackInfo> Layout::getLayoutOfTrack(
trackInfo->sectorSize = layoutdata.sector_size();
trackInfo->logicalCylinder = logicalCylinder;
trackInfo->logicalHead = logicalHead;
trackInfo->physicalCylinder = remapCylinderLogicalToPhysical(logicalCylinder);
trackInfo->physicalCylinder =
remapCylinderLogicalToPhysical(logicalCylinder);
trackInfo->physicalHead =
logicalHead ^ globalConfig()->layout().swap_sides();
trackInfo->groupSize = getTrackStep();
@@ -290,7 +310,8 @@ int Layout::getHeadWidth()
}
}
std::vector<LogicalLocation> Layout::computeFilesystemLogicalOrdering(){
std::vector<LogicalLocation> Layout::computeFilesystemLogicalOrdering()
{
std::vector<LogicalLocation> result;
auto& layout = globalConfig()->layout();
if (layout.has_tracks() && layout.has_sides())
@@ -309,8 +330,7 @@ std::vector<LogicalLocation> Layout::computeFilesystemLogicalOrdering(){
continue;
for (unsigned sectorId : trackLayout->filesystemSectorOrder)
result.push_back(
{track, side, sectorId});
result.push_back({track, side, sectorId});
}
}
return result;

View File

@@ -25,9 +25,10 @@ public:
static unsigned remapHeadLogicalToPhysical(unsigned logicalHead);
/* Uses the layout and current track and heads settings to determine
* which physical tracks are going to be read from or written to.
* which physical/logical tracks are going to be read from or written to.
*/
static std::vector<CylinderHead> computePhysicalLocations();
static std::vector<CylinderHead> computeLogicalLocations();
/* Uses the current layout to compute the filesystem's block order, in
* _logical_ tracks. */

View File

@@ -37,7 +37,11 @@ struct Sector : public LogicalLocation
Bytes data;
std::vector<std::shared_ptr<Record>> records;
Sector(std::shared_ptr<const TrackInfo>& layout, const LogicalLocation& location);
Sector(const Sector& other) = default;
Sector& operator=(const Sector& other) = default;
Sector(std::shared_ptr<const TrackInfo>& layout,
const LogicalLocation& location);
std::tuple<int, int, int, Status> key() const
{
@@ -45,19 +49,9 @@ struct Sector : public LogicalLocation
logicalCylinder, logicalHead, logicalSector, status);
}
bool operator==(const Sector& rhs) const
std::strong_ordering operator<=>(const Sector& rhs) const
{
return key() == rhs.key();
}
bool operator!=(const Sector& rhs) const
{
return key() != rhs.key();
}
bool operator<(const Sector& rhs) const
{
return key() < rhs.key();
return key() <=> rhs.key();
}
};

View File

@@ -35,6 +35,7 @@ cxxlibrary(
"./imagesectorinterface.cc",
"./lif.cc",
"./machfs.cc",
"./memorysectorinterface.cc",
"./microdos.cc",
"./philefs.cc",
"./prodos.cc",

View File

@@ -0,0 +1,71 @@
#include "lib/core/globals.h"
#include "lib/vfs/sectorinterface.h"
#include "lib/imagereader/imagereader.h"
#include "lib/imagewriter/imagewriter.h"
#include "lib/data/image.h"
#include "lib/data/layout.h"
#include "lib/data/sector.h"
#include "lib/core/bytes.h"
class MemorySectorInterface : public SectorInterface
{
public:
MemorySectorInterface(std::shared_ptr<Image> image): _backupImage(image)
{
discardChanges();
}
public:
std::shared_ptr<const Sector> get(
unsigned track, unsigned side, unsigned sectorId) override
{
return _liveImage->get(track, side, sectorId);
}
std::shared_ptr<Sector> put(
unsigned track, unsigned side, unsigned sectorId) override
{
_changed = true;
return _liveImage->put(track, side, sectorId);
}
bool isReadOnly() override
{
return false;
}
bool needsFlushing() override
{
return _changed;
}
void flushChanges() override
{
// _writer->writeImage(*_image);
_changed = false;
}
void discardChanges() override
{
_liveImage = std::make_unique<Image>();
for (auto sector : *_backupImage)
{
auto s = _liveImage->put(sector->logicalCylinder,
sector->logicalHead,
sector->logicalSector);
*s = *sector;
}
_changed = false;
}
private:
std::shared_ptr<Image> _backupImage;
std::shared_ptr<Image> _liveImage;
bool _changed = false;
};
std::unique_ptr<SectorInterface> SectorInterface::createMemorySectorInterface(
std::shared_ptr<Image> image)
{
return std::make_unique<MemorySectorInterface>(image);
}

View File

@@ -1,6 +1,7 @@
#ifndef SECTORINTERFACE_H
#define SECTORINTERFACE_H
class Image;
class ImageReader;
class ImageWriter;
class Sector;
@@ -35,6 +36,8 @@ public:
virtual void discardChanges() {}
public:
static std::unique_ptr<SectorInterface> createMemorySectorInterface(
std::shared_ptr<Image> image);
static std::unique_ptr<SectorInterface> createImageSectorInterface(
std::shared_ptr<ImageReader> reader,
std::shared_ptr<ImageWriter> writer);

View File

@@ -332,16 +332,16 @@ plugin(
srcs=[
"./abstractsectorview.cc",
"./abstractsectorview.h",
"./imageview.cc",
"./imageview.h",
"./physicalview.cc",
"./physicalview.h",
"./datastore.cc",
"./datastore.h",
"./diskprovider.cc",
"./diskprovider.h",
"./fluxengine.cc",
"./globals.h",
"./imageview.cc",
"./imageview.h",
"./physicalview.cc",
"./physicalview.h",
"./summaryview.cc",
"./summaryview.h",
"./utils.cc",

View File

@@ -10,10 +10,12 @@
#include "lib/data/flux.h"
#include "lib/data/image.h"
#include "lib/fluxsource/fluxsource.h"
#include "lib/fluxsink/fluxsink.h"
#include "lib/decoders/decoders.h"
#include "lib/imagewriter/imagewriter.h"
#include "lib/algorithms/readerwriter.h"
#include "lib/usb/usbfinder.h"
#include "lib/vfs/vfs.h"
#include "arch/arch.h"
#include "globals.h"
#include "datastore.h"
@@ -33,6 +35,7 @@ static std::thread workerThread;
static std::atomic<bool> busy;
static bool configurationValid;
static bool formattingSupported;
static std::map<std::string, Datastore::Device> devices;
static std::map<CylinderHead, std::shared_ptr<const TrackInfo>>
physicalCylinderLayouts;
@@ -197,6 +200,11 @@ bool Datastore::isConfigurationValid()
return isConfigurationValid;
}
bool Datastore::canFormat()
{
return formattingSupported;
}
const std::map<std::string, Datastore::Device>& Datastore::getDevices()
{
return devices;
@@ -319,7 +327,6 @@ void Datastore::rebuildConfiguration()
hex::TaskManager::doLaterOnce(
[]
{
hex::log::debug("FluxEngine configuration stale; rebuilding it");
configurationValid = false;
/* Reset and apply the format configuration. */
@@ -398,6 +405,30 @@ void Datastore::rebuildConfiguration()
/* Update the UI-thread copy of the bits of configuration we
* need. */
formattingSupported = false;
Datastore::runOnWorkerThread(
[]
{
bool formattingSupported;
try
{
auto filesystem = Filesystem::createFilesystem(
globalConfig()->filesystem(), nullptr);
uint32_t flags = filesystem->capabilities();
formattingSupported = flags & Filesystem::OP_CREATE;
}
catch (const ErrorException&)
{
formattingSupported = false;
}
hex::TaskManager::doLater(
[=]
{
::formattingSupported = formattingSupported;
});
});
Datastore::runOnWorkerThread(
[]
{
@@ -539,3 +570,33 @@ void Datastore::writeImage(const std::fs::path& path)
->writeImage(*Datastore::getDiskFlux()->image);
});
}
void Datastore::writeFluxFile(const std::fs::path& path)
{
busy = true;
Datastore::runOnWorkerThread(
[=]
{
ON_SCOPE_EXIT
{
rebuildConfiguration();
};
globalConfig().setFluxSink(path);
auto fluxSource = FluxSource::createMemoryFluxSource(*diskFlux);
auto fluxSink = FluxSink::create(globalConfig());
writeRawDiskCommand(*fluxSource, *fluxSink);
});
}
void Datastore::createBlankImage()
{
busy = true;
Datastore::runOnWorkerThread(
[=]
{
ON_SCOPE_EXIT
{
rebuildConfiguration();
};
});
}

View File

@@ -24,6 +24,7 @@ public:
static bool isBusy();
static bool isConfigurationValid();
static bool canFormat();
static void probeDevices();
static const std::map<std::string, Device>& getDevices();
@@ -42,6 +43,8 @@ public:
static void beginRead();
static void writeImage(const std::fs::path& path);
static void writeFluxFile(const std::fs::path& path);
static void createBlankImage();
static void stop();
static std::shared_ptr<const DiskFlux> getDiskFlux();

View File

@@ -55,6 +55,11 @@ static void saveSectorImage()
fs::openFileBrowser(fs::DialogMode::Save, {}, Datastore::writeImage);
}
static void saveFluxFile()
{
fs::openFileBrowser(fs::DialogMode::Save, {}, Datastore::writeFluxFile);
}
static void showOptions() {}
static void emitOptions(DynamicSetting<std::string>& setting,
@@ -146,7 +151,7 @@ static void emitOptions(DynamicSetting<std::string>& setting,
wolv::util::capitalizeString(selectedOption->comment())
.c_str());
else
ImGui::TextWrapped("***bad***");
ImGui::TextWrapped("***missing default***");
ImGui::SameLine();
if (ImGui::BeginCombo(fmt::format("##{}", it.comment()).c_str(),
nullptr,
@@ -163,7 +168,8 @@ static void emitOptions(DynamicSetting<std::string>& setting,
{
if (it.name().empty())
{
options.erase(selectedOption->name());
if (selectedOption)
options.erase(selectedOption->name());
options[ot.name()] = "true";
}
else
@@ -523,7 +529,7 @@ void SummaryView::drawContent()
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (MaybeDisabledButton(
if (maybeDisabledButton(
fmt::format(
"{} {}", ICON_TA_DEVICE_FLOPPY, "Read disk"),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
@@ -531,21 +537,21 @@ void SummaryView::drawContent()
Datastore::beginRead();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
MaybeDisabledButton(
maybeDisabledButton(
fmt::format(
"{} {}", ICON_VS_FOLDER_OPENED, "Load sector image"),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
MaybeDisabledButton(
maybeDisabledButton(
fmt::format("{} {}", ICON_VS_SAVE_AS, "Write disk").c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy || !hasImage);
ImGui::TableNextRow();
ImGui::TableNextColumn();
MaybeDisabledButton(
maybeDisabledButton(
fmt::format("{} {}", ICON_TA_REPEAT, "Reread bad tracks")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
@@ -553,7 +559,7 @@ void SummaryView::drawContent()
ImGui::TableNextColumn();
ImGui::Text(ICON_VS_ARROW_RIGHT);
ImGui::TableNextColumn();
if (MaybeDisabledButton(
if (maybeDisabledButton(
fmt::format(
"{} {}", ICON_VS_SAVE_ALL, "Save sector image")
.c_str(),
@@ -563,27 +569,30 @@ void SummaryView::drawContent()
ImGui::TableNextColumn();
ImGui::Text(ICON_VS_ARROW_RIGHT);
ImGui::TableNextColumn();
MaybeDisabledButton(
fmt::format("{} {}", ICON_TA_DOWNLOAD, "Save flux file")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy || !diskFlux);
if (maybeDisabledButton(
fmt::format("{} {}", ICON_TA_DOWNLOAD, "Save flux file")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy || !diskFlux))
saveFluxFile();
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (MaybeDisabledButton(
fmt::format("{} {}", ICON_TA_UPLOAD, "Load flux file")
if (maybeDisabledButton(
fmt::format("{} {}", ICON_TA_UPLOAD, "Setup flux file")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy))
loadFluxFile();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
MaybeDisabledButton(
fmt::format("{} {}", ICON_VS_NEW_FILE, "Create blank image")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy);
if (maybeDisabledButton(
fmt::format(
"{} {}", ICON_VS_NEW_FILE, "Create blank image")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy || !Datastore::canFormat()))
Datastore::createBlankImage();
}
{
@@ -606,7 +615,7 @@ void SummaryView::drawContent()
ImGui::PopStyleColor(3);
};
if (MaybeDisabledButton(
if (maybeDisabledButton(
fmt::format("{} {}",
ICON_TA_CANCEL,
"fluxengine.summary.controls.stop"_lang),

View File

@@ -4,7 +4,7 @@
#include "globals.h"
#include "utils.h"
int MaybeDisabledButton(
int maybeDisabledButton(
const std::string& message, const ImVec2& size, bool isDisabled)
{
ImGui::BeginDisabled(isDisabled);

View File

@@ -9,7 +9,7 @@ struct ImVec2;
using OptionsMap = std::map<std::string, std::string>;
extern int MaybeDisabledButton(
extern int maybeDisabledButton(
const std::string& message, const ImVec2& size, bool isDisabled);
extern std::string shortenString(const std::string& s, size_t len);