From 0fd1aa82a6d8bb0348340b15a65c9ba5f1b38b07 Mon Sep 17 00:00:00 2001 From: David Given Date: Fri, 17 Oct 2025 23:41:16 +0200 Subject: [PATCH] Crudely bodge image writes into working. --- lib/config/config.cc | 23 +++++++++ lib/config/config.h | 1 + lib/config/proto.cc | 27 +++++++---- lib/config/proto.h | 4 +- lib/fluxsource/fluxsource.cc | 4 +- lib/imagereader/diskcopyimagereader.cc | 2 + src/fe-write.cc | 1 + src/gui2/configview.cc | 52 +++++++++++++++++--- src/gui2/datastore.cc | 66 +++++++++++++------------- src/gui2/globals.h | 1 + tests/proto.cc | 44 +++++++++++++++++ 11 files changed, 174 insertions(+), 51 deletions(-) diff --git a/lib/config/config.cc b/lib/config/config.cc index c96fbe44..e45e2ccb 100644 --- a/lib/config/config.cc +++ b/lib/config/config.cc @@ -451,6 +451,29 @@ bool Config::applyOption(const std::string& name, const std::string value) return optionInfo.usesValue; } +void Config::applyOptionsFile(const std::string& data) +{ + if (!data.empty()) + { + for (auto setting : split(data, '\n')) + { + setting = trimWhitespace(setting); + if (setting.size() == 0) + continue; + if (setting[0] == '#') + continue; + + auto equals = setting.find('='); + if (equals == std::string::npos) + error("Malformed setting line '{}'", setting); + + auto key = setting.substr(0, equals); + auto value = setting.substr(equals + 1); + globalConfig().set(key, value); + } + } +} + void Config::applyDefaultOptions() { std::set appliedOptionGroups; diff --git a/lib/config/config.h b/lib/config/config.h index acf41496..dd8ca3b3 100644 --- a/lib/config/config.h +++ b/lib/config/config.h @@ -142,6 +142,7 @@ public: bool isOptionValid(const OptionProto& option); void applyOption(const OptionInfo& optionInfo); bool applyOption(const std::string& name, const std::string value = ""); + void applyOptionsFile(const std::string& data); void applyDefaultOptions(); void clearOptions(); diff --git a/lib/config/proto.cc b/lib/config/proto.cc index 59e1f4e8..355d326f 100644 --- a/lib/config/proto.cc +++ b/lib/config/proto.cc @@ -540,12 +540,13 @@ findAllPossibleProtoFields(const google::protobuf::Descriptor* descriptor) return fields; } -std::vector findAllProtoFields(google::protobuf::Message* message) +std::vector findAllProtoFields( + const google::protobuf::Message* message) { std::vector allFields; - std::function - recurse = [&](auto* message, const auto& name) + std::function + recurse = [&](const auto* message, const auto& name) { const auto* reflection = message->GetReflection(); std::vector fields; @@ -565,18 +566,19 @@ std::vector findAllProtoFields(google::protobuf::Message* message) const auto n = fmt::format("{}[{}]", basename, i); if (shouldRecurse(f)) recurse( - reflection->MutableRepeatedMessage(message, f, i), - n); + &reflection->GetRepeatedMessage(*message, f, i), n); else - allFields.push_back(ProtoField(n, message, f, i)); + allFields.push_back(ProtoField( + n, (google::protobuf::Message*)message, f, i)); } } else { if (shouldRecurse(f)) - recurse(reflection->MutableMessage(message, f), basename); + recurse(&reflection->GetMessage(*message, f), basename); else - allFields.push_back(ProtoField(basename, message, f)); + allFields.push_back(ProtoField( + basename, (google::protobuf::Message*)message, f)); } } }; @@ -584,3 +586,12 @@ std::vector findAllProtoFields(google::protobuf::Message* message) recurse(message, ""); return allFields; } + +std::string renderProtoAsConfig(const google::protobuf::Message* message) +{ + auto allFields = findAllProtoFields(message); + std::stringstream ss; + for (const auto& field : allFields) + ss << field.path() << "=" << field.get() << "\n"; + return ss.str(); +} diff --git a/lib/config/proto.h b/lib/config/proto.h index d529a414..3797daa2 100644 --- a/lib/config/proto.h +++ b/lib/config/proto.h @@ -72,7 +72,9 @@ extern std::map findAllPossibleProtoFields(const google::protobuf::Descriptor* descriptor); extern std::vector findAllProtoFields( - google::protobuf::Message* message); + const google::protobuf::Message* message); +extern std::string renderProtoAsConfig( + const google::protobuf::Message* message); template static inline const T parseProtoBytes(const std::string_view& bytes) diff --git a/lib/fluxsource/fluxsource.cc b/lib/fluxsource/fluxsource.cc index d1e46733..67a253c4 100644 --- a/lib/fluxsource/fluxsource.cc +++ b/lib/fluxsource/fluxsource.cc @@ -11,9 +11,7 @@ std::unique_ptr FluxSource::create(Config& config) { if (!config.hasFluxSource()) error("no flux source configured"); - auto fluxSource = create(config->flux_source()); - globalConfig().base()->MergeFrom(fluxSource->getExtraConfig()); - return fluxSource; + return create(config->flux_source()); } std::unique_ptr FluxSource::create(const FluxSourceProto& config) diff --git a/lib/imagereader/diskcopyimagereader.cc b/lib/imagereader/diskcopyimagereader.cc index a6359536..277a33ba 100644 --- a/lib/imagereader/diskcopyimagereader.cc +++ b/lib/imagereader/diskcopyimagereader.cc @@ -111,6 +111,8 @@ public: } } + _extraConfig.mutable_layout()->add_layoutdata()->set_sector_size(524); + image->setGeometry({.numCylinders = numCylinders, .numHeads = numHeads, .numSectors = 12, diff --git a/src/fe-write.cc b/src/fe-write.cc index 97226485..f69713b3 100644 --- a/src/fe-write.cc +++ b/src/fe-write.cc @@ -54,6 +54,7 @@ int mainWrite(int argc, const char* argv[]) auto reader = ImageReader::create(globalConfig()); std::shared_ptr image = reader->readImage(); + globalConfig().overrides()->MergeFrom(reader->getExtraConfig()); auto diskLayout = createDiskLayout(); auto encoder = Arch::createEncoder(globalConfig()); diff --git a/src/gui2/configview.cc b/src/gui2/configview.cc index 85ea32a9..7cadb02e 100644 --- a/src/gui2/configview.cc +++ b/src/gui2/configview.cc @@ -18,9 +18,25 @@ using namespace hex; static DynamicSettingFactory settings("fluxengine.settings"); +static constexpr const char* DEFAULT_CUSTOM_SETTINGS = R"R( +# These settings will override the ones above. +# Here are some useful ones: +# decoder.retries=0 +# drive.revolutions=1.2 +)R"; + ConfigView::ConfigView(): View::Window("fluxengine.view.config.name", ICON_VS_COMPASS) { + Events::SetSystemConfig::subscribe( + [](std::string customConfig) + { + auto customSetting = settings.get("systemProperties"); + customSetting = + "# These settings were set by a file and\n" + "# may contradict the ones above (it's a bug).\n" + + customConfig; + }); } static void emitOptions(DynamicSetting& setting, @@ -336,11 +352,33 @@ void ConfigView::drawContent() ImGui::SeparatorText("fluxengine.view.config.customProperties"_lang); - auto customSetting = settings.get("custom"); - std::string buffer = customSetting; - if (ImGui::InputTextMultiline("##customProperties", - buffer, - ImGui::GetContentRegionAvail(), - ImGuiInputTextFlags_None)) - customSetting = buffer; + if (ImGui::BeginTabBar("customconfig", ImGuiTabBarFlags_None)) + { + DEFER(ImGui::EndTabBar()); + if (ImGui::BeginTabItem("User")) + { + DEFER(ImGui::EndTabItem()); + + auto setting = + settings.get("custom", DEFAULT_CUSTOM_SETTINGS); + std::string buffer = setting; + if (ImGui::InputTextMultiline("##customProperties", + buffer, + ImGui::GetContentRegionAvail(), + ImGuiInputTextFlags_None)) + setting = buffer; + } + if (ImGui::BeginTabItem("System")) + { + DEFER(ImGui::EndTabItem()); + + auto setting = settings.get("systemProperties"); + std::string buffer = setting; + if (ImGui::InputTextMultiline("##systemProperties", + buffer, + ImGui::GetContentRegionAvail(), + ImGuiInputTextFlags_None)) + setting = buffer; + } + } } diff --git a/src/gui2/datastore.cc b/src/gui2/datastore.cc index 6736d8d7..591938f0 100644 --- a/src/gui2/datastore.cc +++ b/src/gui2/datastore.cc @@ -8,6 +8,7 @@ #include "lib/core/globals.h" #include "lib/core/utils.h" #include "lib/config/config.h" +#include "lib/config/proto.h" #include "lib/data/disk.h" #include "lib/data/image.h" #include "lib/fluxsource/fluxsource.h" @@ -50,7 +51,7 @@ static bool formattingSupported; static std::map devices; static std::shared_ptr diskLayout; -static void wtRebuildConfiguration(); +static void wtRebuildConfiguration(bool useCustom); static void workerThread_cb() { @@ -271,7 +272,11 @@ void Datastore::init() hex::Region{startOffset, endOffset - startOffset}); }); - runOnWorkerThread(wtRebuildConfiguration); + runOnWorkerThread( + [] + { + wtRebuildConfiguration(true); + }); } bool Datastore::isBusy() @@ -338,7 +343,7 @@ static void wtClearDiskData() }); } -void wtRebuildConfiguration() +void wtRebuildConfiguration(bool withCustom = false) { /* Reset and apply the format configuration. */ @@ -392,26 +397,10 @@ void wtRebuildConfiguration() /* Custom settings. */ - auto customSettings = readSettingFromUiThread("custom", ""); - if (!customSettings.empty()) - { - for (auto setting : split(customSettings, '\n')) - { - setting = trimWhitespace(setting); - if (setting.size() == 0) - continue; - if (setting[0] == '#') - continue; - - auto equals = setting.find('='); - if (equals == std::string::npos) - error("Malformed setting line '{}'", setting); - - auto key = setting.substr(0, equals); - auto value = setting.substr(equals + 1); - globalConfig().set(key, value); - } - } + globalConfig().applyOptionsFile( + readSettingFromUiThread("systemProperties", "")); + globalConfig().applyOptionsFile( + readSettingFromUiThread("custom", "")); /* Finalise the options. */ @@ -540,7 +529,7 @@ void Datastore::beginRead(bool rereadBadSectors) try { - wtRebuildConfiguration(); + wtRebuildConfiguration(true); if (!rereadBadSectors) wtClearDiskData(); @@ -576,7 +565,7 @@ void Datastore::beginWrite() try { - wtRebuildConfiguration(); + wtRebuildConfiguration(true); wtWaitForUiThreadToCatchUp(); auto fluxSinkFactory = FluxSinkFactory::create(globalConfig()); @@ -642,7 +631,7 @@ void Datastore::reset() try { - wtRebuildConfiguration(); + wtRebuildConfiguration(false); wtClearDiskData(); wtWaitForUiThreadToCatchUp(); } @@ -682,7 +671,7 @@ void Datastore::writeImage(const std::fs::path& path) try { - wtRebuildConfiguration(); + wtRebuildConfiguration(true); wtWaitForUiThreadToCatchUp(); globalConfig().setImageWriter(path.string()); ImageWriter::create(globalConfig()) @@ -708,11 +697,23 @@ void Datastore::readImage(const std::fs::path& path) try { - wtRebuildConfiguration(); + wtRebuildConfiguration(true); wtWaitForUiThreadToCatchUp(); globalConfig().setImageReader(path.string()); - std::shared_ptr image = - ImageReader::create(globalConfig())->readImage(); + auto imageReader = ImageReader::create(globalConfig()); + std::shared_ptr image = imageReader->readImage(); + + const auto& extraConfig = imageReader->getExtraConfig(); + auto customConfig = renderProtoAsConfig(&extraConfig); + + /* Update the setting, and then rebuild the config again as it + * will have changed. */ + + wtRunSynchronouslyOnUiThread((std::function)[=] { + Events::SetSystemConfig::post(customConfig); + }); + wtRebuildConfiguration(true); + wtWaitForUiThreadToCatchUp(); auto disk = wtMakeDiskDataFromImage(image); hex::TaskManager::doLater( @@ -741,7 +742,7 @@ void Datastore::writeFluxFile(const std::fs::path& path) try { - wtRebuildConfiguration(); + wtRebuildConfiguration(true); wtWaitForUiThreadToCatchUp(); if (!disk || !disk->image) @@ -777,7 +778,7 @@ void Datastore::createBlankImage() try { - wtRebuildConfiguration(); + wtRebuildConfiguration(false); wtClearDiskData(); wtWaitForUiThreadToCatchUp(); @@ -794,6 +795,7 @@ void Datastore::createBlankImage() hex::TaskManager::doLater( [=] { + Events::SetSystemConfig::post(""); ::disk = disk; }); } diff --git a/src/gui2/globals.h b/src/gui2/globals.h index a4d98410..28f087fa 100644 --- a/src/gui2/globals.h +++ b/src/gui2/globals.h @@ -39,6 +39,7 @@ namespace Events EVENT_DEF(DiskActivityNotification, DiskActivityType, unsigned, unsigned); EVENT_DEF(OperationStart, std::string); EVENT_DEF(OperationStop, OperationState); + EVENT_DEF(SetSystemConfig, std::string); } #define DEFER(_stmt) \ diff --git a/tests/proto.cc b/tests/proto.cc index 991969ef..b01e53be 100644 --- a/tests/proto.cc +++ b/tests/proto.cc @@ -226,6 +226,49 @@ static void test_findallfields(void) "u64"})); } +static void test_rendering(void) +{ + std::string s = R"M( + i64: -1 + i32: -2 + u64: 3 + u32: 4 + d: 5.5 + f: 6.7 + m { + s: "string" + } + r { + s: "val2" + } + r { + s: "val3" + } + secondoption { + s: "2" + } + )M"; + + TestProto proto; + if (!google::protobuf::TextFormat::MergeFromString(cleanup(s), &proto)) + error("couldn't load test proto"); + + auto config = renderProtoAsConfig(&proto); + + AssertThat(cleanup(config), Equals(cleanup(R"M( + i64=-1 + i32=-2 + u64=3 + u32=4 + d=5.5 + m.s=string + r[0].s=val2 + r[1].s=val3 + secondoption.s=2 + f=6.7 + )M"))); +} + int main(int argc, const char* argv[]) { try @@ -237,6 +280,7 @@ int main(int argc, const char* argv[]) test_fields(); test_options(); test_findallfields(); + test_rendering(); } catch (const ErrorException& e) {