Compare commits

...

7 Commits

Author SHA1 Message Date
David Given
dcae381973 Add some more GUI for the disk exerciser. 2025-10-24 01:00:11 +02:00
David Given
2142bc7cce Add a Disk menu. 2025-10-23 01:12:40 +02:00
David Given
ae3f82264a Add the boilerplate for the exerciser. 2025-10-22 01:13:36 +02:00
David Given
710e83c098 Try to fix OSX build failure. 2025-10-18 13:03:14 +02:00
David Given
4f46fff3be Remove a bunch of extraneous Providers. 2025-10-18 01:03:39 +02:00
David Given
58ea21a9a2 Add a menu option to allow resetting the workspace. 2025-10-18 00:36:52 +02:00
David Given
0fd1aa82a6 Crudely bodge image writes into working. 2025-10-17 23:41:16 +02:00
19 changed files with 583 additions and 64 deletions

View File

@@ -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<const OptionGroupProto*> appliedOptionGroups;

View File

@@ -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();

View File

@@ -540,12 +540,13 @@ findAllPossibleProtoFields(const google::protobuf::Descriptor* descriptor)
return fields;
}
std::vector<ProtoField> findAllProtoFields(google::protobuf::Message* message)
std::vector<ProtoField> findAllProtoFields(
const google::protobuf::Message* message)
{
std::vector<ProtoField> allFields;
std::function<void(google::protobuf::Message*, const std::string&)>
recurse = [&](auto* message, const auto& name)
std::function<void(const google::protobuf::Message*, const std::string&)>
recurse = [&](const auto* message, const auto& name)
{
const auto* reflection = message->GetReflection();
std::vector<const google::protobuf::FieldDescriptor*> fields;
@@ -565,18 +566,19 @@ std::vector<ProtoField> 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<ProtoField> 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();
}

View File

@@ -72,7 +72,9 @@ extern std::map<std::string, const google::protobuf::FieldDescriptor*>
findAllPossibleProtoFields(const google::protobuf::Descriptor* descriptor);
extern std::vector<ProtoField> findAllProtoFields(
google::protobuf::Message* message);
const google::protobuf::Message* message);
extern std::string renderProtoAsConfig(
const google::protobuf::Message* message);
template <class T>
static inline const T parseProtoBytes(const std::string_view& bytes)

View File

@@ -4,6 +4,7 @@
#include "lib/core/bytes.h"
#include "protocol.h"
#include "fmt/format.h"
#include <mutex>
class RawBits;

View File

@@ -11,9 +11,7 @@ std::unique_ptr<FluxSource> 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> FluxSource::create(const FluxSourceProto& config)

View File

@@ -111,6 +111,8 @@ public:
}
}
_extraConfig.mutable_layout()->add_layoutdata()->set_sector_size(524);
image->setGeometry({.numCylinders = numCylinders,
.numHeads = numHeads,
.numSectors = 12,

View File

@@ -54,6 +54,7 @@ int mainWrite(int argc, const char* argv[])
auto reader = ImageReader::create(globalConfig());
std::shared_ptr<Image> image = reader->readImage();
globalConfig().overrides()->MergeFrom(reader->getExtraConfig());
auto diskLayout = createDiskLayout();
auto encoder = Arch::createEncoder(globalConfig());

View File

@@ -416,16 +416,17 @@ plugin(
srcs=sources_from(
"dep/imhex/plugins/builtin/source",
except_for=[
"dep/imhex/plugins/builtin/source/content/views/view_achievements.cpp",
"dep/imhex/plugins/builtin/source/content/achievements.cpp",
"dep/imhex/plugins/builtin/source/content/data_processor_nodes.cpp",
"dep/imhex/plugins/builtin/source/content/main_menu_items.cpp",
"dep/imhex/plugins/builtin/source/content/welcome_screen.cpp",
"dep/imhex/plugins/builtin/source/content/out_of_box_experience.cpp",
"dep/imhex/plugins/builtin/source/content/providers.cpp",
"dep/imhex/plugins/builtin/source/content/ui_items.cpp",
"dep/imhex/plugins/builtin/source/content/views.cpp",
"dep/imhex/plugins/builtin/source/content/views/view_achievements.cpp",
"dep/imhex/plugins/builtin/source/content/views/view_data_processor.cpp",
"dep/imhex/plugins/builtin/source/content/views/view_tutorials.cpp",
"dep/imhex/plugins/builtin/source/content/data_processor_nodes.cpp",
"dep/imhex/plugins/builtin/source/content/welcome_screen.cpp",
]
+ glob(
"dep/imhex/plugins/builtin/source/content/data_processor_nodes/*"
@@ -433,11 +434,12 @@ plugin(
+ glob("dep/imhex/plugins/builtin/source/content/tutorials/*"),
)
+ [
"./imhex_overrides/welcome.cc",
"./imhex_overrides/main_menu_items.cpp",
"./imhex_overrides/providers.cpp",
"./imhex_overrides/stubs.cc",
"./imhex_overrides/ui_items.cc",
"./imhex_overrides/views.cpp",
"./imhex_overrides/main_menu_items.cpp",
"./imhex_overrides/welcome.cc",
],
hdrs=headers_from("dep/imhex/plugins/builtin/include"),
romfsdir="dep/imhex/plugins/builtin/romfs",
@@ -508,6 +510,8 @@ plugin(
"./controlpanelview.h",
"./datastore.cc",
"./datastore.h",
"./exerciserview.cc",
"./exerciserview.h",
"./diskprovider.cc",
"./diskprovider.h",
"./fluxengine.cc",

View File

@@ -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<std::string>("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<std::string>& setting,
@@ -336,11 +352,33 @@ void ConfigView::drawContent()
ImGui::SeparatorText("fluxengine.view.config.customProperties"_lang);
auto customSetting = settings.get<std::string>("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<std::string>("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<std::string>("systemProperties");
std::string buffer = setting;
if (ImGui::InputTextMultiline("##systemProperties",
buffer,
ImGui::GetContentRegionAvail(),
ImGuiInputTextFlags_None))
setting = buffer;
}
}
}

View File

@@ -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"
@@ -34,7 +35,7 @@
using hex::operator""_lang;
static std::shared_ptr<const Disk> disk;
static std::shared_ptr<const Disk> disk = std::make_shared<Disk>();
static std::shared_ptr<Image> wtImage;
static std::deque<std::function<void()>> pendingTasks;
@@ -50,7 +51,7 @@ static bool formattingSupported;
static std::map<std::string, Datastore::Device> devices;
static std::shared_ptr<const DiskLayout> 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<std::string>("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<std::string>("systemProperties", ""));
globalConfig().applyOptionsFile(
readSettingFromUiThread<std::string>("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> image =
ImageReader::create(globalConfig())->readImage();
auto imageReader = ImageReader::create(globalConfig());
std::shared_ptr<Image> 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<void()>)[=] {
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;
});
}

70
src/gui2/exerciserview.cc Normal file
View File

@@ -0,0 +1,70 @@
#include <hex/api/content_registry/user_interface.hpp>
#include <hex/api/theme_manager.hpp>
#include <hex/helpers/logger.hpp>
#include <fonts/vscode_icons.hpp>
#include <fonts/tabler_icons.hpp>
#include <fmt/format.h>
#include "lib/core/globals.h"
#include "lib/config/config.h"
#include "lib/data/disk.h"
#include "lib/data/sector.h"
#include "lib/config/proto.h"
#include "globals.h"
#include "exerciserview.h"
#include "datastore.h"
#include "utils.h"
#include <implot.h>
#include <implot_internal.h>
using namespace hex;
ExerciserView::ExerciserView():
View::Modal("fluxengine.view.exerciser.name", ICON_VS_DEBUG)
{
}
void ExerciserView::drawContent()
{
static int selectedDrive = 0;
static int selectedTrack = 0;
const float label_width = ImGui::GetFontSize() * 6;
ImGui::PushItemWidth(-label_width);
DEFER(ImGui::PopItemWidth());
ImGui::SliderInt("fluxengine.view.exerciser.drive"_lang,
&selectedDrive,
0,
1,
"%d",
ImGuiSliderFlags_None);
ImGui::SliderInt("fluxengine.view.exerciser.cylinder"_lang,
&selectedTrack,
0,
82,
"%d",
ImGuiSliderFlags_None);
if (ImGui::BeginTable("nudgeTable",
3,
ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_SizingStretchProp,
{ImGui::GetContentRegionAvail().x - label_width, 0}))
{
DEFER(ImGui::EndTable());
ImGui::TableNextColumn();
if (ImGui::Button("fluxengine.view.exerciser.nudgeDown"_lang,
{ImGui::GetContentRegionAvail().x, 0}))
selectedTrack--;
ImGui::TableNextColumn();
if (ImGui::Button("fluxengine.view.exerciser.reset"_lang,
{ImGui::GetContentRegionAvail().x, 0}))
selectedTrack = 0;
ImGui::TableNextColumn();
if (ImGui::Button("fluxengine.view.exerciser.nudgeUp"_lang,
{ImGui::GetContentRegionAvail().x, 0}))
selectedTrack++;
}
selectedTrack = std::clamp(selectedTrack, 0, 82);
}

22
src/gui2/exerciserview.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <hex/ui/view.hpp>
class ExerciserView : public hex::View::Modal
{
public:
ExerciserView();
~ExerciserView() override = default;
void drawContent() override;
[[nodiscard]] bool shouldDraw() const override
{
return true;
}
[[nodiscard]] bool hasViewMenuItemEntry() const override
{
return false;
}
};

View File

@@ -1,7 +1,13 @@
#include <hex/plugin.hpp>
#include <hex/api/content_registry/views.hpp>
#include <hex/api/content_registry/user_interface.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/api/content_registry/provider.hpp>
#include <hex/api/workspace_manager.hpp>
#include <hex/helpers/default_paths.hpp>
#include <hex/helpers/fs.hpp>
#include <fonts/vscode_icons.hpp>
#include <fonts/tabler_icons.hpp>
#include <romfs/romfs.hpp>
#include "globals.h"
#include "imageview.h"
@@ -11,6 +17,7 @@
#include "controlpanelview.h"
#include "logview.h"
#include "visualiserview.h"
#include "exerciserview.h"
#include "diskprovider.h"
#include "datastore.h"
@@ -24,6 +31,150 @@ IMHEX_PLUGIN_SETUP("FluxEngine", "David Given", "FluxEngine integration")
return romfs::get(path).string();
});
hex::ContentRegistry::UserInterface::addMenuItem(
{"hex.builtin.menu.workspace", "fluxengine.menu.workspace.reset"},
ICON_TA_CANCEL,
10000,
hex::Shortcut::None,
[]
{
static const std::string extractFolder = "auto_extract/workspaces";
for (const auto& romfsPath : romfs::list(extractFolder))
{
for (const auto& imhexPath : hex::paths::getDataPaths(false))
{
const auto path =
imhexPath / std::fs::relative(romfsPath, extractFolder);
hex::log::info("Extracting {} to {}",
romfsPath.string(),
path.string());
wolv::io::File file(path, wolv::io::File::Mode::Create);
if (!file.isValid())
continue;
auto data = romfs::get(romfsPath).span<u8>();
file.writeBuffer(data.data(), data.size());
if (file.getSize() == data.size())
break;
}
}
auto currentWorkspaceName =
hex::WorkspaceManager::getCurrentWorkspace()->first;
hex::WorkspaceManager::reload();
hex::WorkspaceManager::switchWorkspace(currentWorkspaceName);
});
hex::ContentRegistry::UserInterface::registerMainMenuItem(
"fluxengine.menu.name", 4999);
auto isReady = []
{
return !Datastore::isBusy();
};
auto isReadyAndHasImage = []
{
return !Datastore::isBusy() && Datastore::getDisk()->image;
};
hex::ContentRegistry::UserInterface::addMenuItem(
{"fluxengine.menu.name", "fluxengine.view.controlpanel.readDevice"},
ICON_TA_DEVICE_FLOPPY,
1000,
hex::Shortcut::None,
[]
{
Datastore::beginRead(false);
},
isReady);
hex::ContentRegistry::UserInterface::addMenuItem(
{"fluxengine.menu.name", "fluxengine.view.controlpanel.rereadBad"},
ICON_TA_REPEAT,
1100,
hex::Shortcut::None,
[]
{
Datastore::beginRead(true);
},
isReadyAndHasImage);
hex::ContentRegistry::UserInterface::addMenuItem(
{"fluxengine.menu.name", "fluxengine.view.controlpanel.readImage"},
ICON_VS_FOLDER_OPENED,
1200,
hex::Shortcut::None,
[]
{
hex::fs::openFileBrowser(
hex::fs::DialogMode::Open, {}, Datastore::readImage);
},
isReady);
hex::ContentRegistry::UserInterface::addMenuItem(
{"fluxengine.menu.name", "fluxengine.view.controlpanel.createBlank"},
ICON_VS_FOLDER_OPENED,
1250,
hex::Shortcut::None,
Datastore::createBlankImage,
[]
{
return !Datastore::isBusy() && Datastore::canFormat();
});
hex::ContentRegistry::UserInterface::addMenuItemSeparator(
{"fluxengine.menu.name"}, 1300);
hex::ContentRegistry::UserInterface::addMenuItem(
{"fluxengine.menu.name", "fluxengine.view.controlpanel.writeDevice"},
ICON_TA_DEVICE_FLOPPY,
1400,
hex::Shortcut::None,
Datastore::beginWrite,
isReadyAndHasImage);
hex::ContentRegistry::UserInterface::addMenuItem(
{"fluxengine.menu.name", "fluxengine.view.controlpanel.writeFlux"},
ICON_TA_DOWNLOAD,
1500,
hex::Shortcut::None,
[]
{
hex::fs::openFileBrowser(
hex::fs::DialogMode::Save, {}, Datastore::writeFluxFile);
},
isReady);
hex::ContentRegistry::UserInterface::addMenuItem(
{"fluxengine.menu.name", "fluxengine.view.controlpanel.writeImage"},
ICON_VS_SAVE_ALL,
1600,
hex::Shortcut::None,
[]
{
hex::fs::openFileBrowser(
hex::fs::DialogMode::Save, {}, Datastore::writeImage);
},
isReadyAndHasImage);
hex::ContentRegistry::UserInterface::addMenuItemSeparator(
{"fluxengine.menu.name"}, 9999);
hex::ContentRegistry::UserInterface::addMenuItem(
{"fluxengine.menu.name", "fluxengine.menu.disk.exerciser"},
ICON_TA_TOOLS,
10000,
hex::Shortcut::None,
[]
{
hex::ContentRegistry::Views::getViewByName(
"fluxengine.view.exerciser.name")
->getWindowOpenState() = true;
});
hex::ContentRegistry::Provider::add<DiskProvider>();
hex::ContentRegistry::Views::add<ConfigView>();
@@ -33,6 +184,7 @@ IMHEX_PLUGIN_SETUP("FluxEngine", "David Given", "FluxEngine integration")
hex::ContentRegistry::Views::add<PhysicalView>();
hex::ContentRegistry::Views::add<SummaryView>();
hex::ContentRegistry::Views::add<VisualiserView>();
hex::ContentRegistry::Views::add<ExerciserView>();
Datastore::init();
}

View File

@@ -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) \

View File

@@ -0,0 +1,136 @@
#include <hex/api/content_registry/provider.hpp>
#include "content/providers/gdb_provider.hpp"
#include "content/providers/file_provider.hpp"
#include "content/providers/null_provider.hpp"
#include "content/providers/disk_provider.hpp"
#include "content/providers/intel_hex_provider.hpp"
#include "content/providers/motorola_srec_provider.hpp"
#include "content/providers/memory_file_provider.hpp"
#include "content/providers/view_provider.hpp"
#include <content/providers/process_memory_provider.hpp>
#include <content/providers/base64_provider.hpp>
#include <content/providers/udp_provider.hpp>
#include <popups/popup_notification.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/helpers/fmt.hpp>
#include <nlohmann/json.hpp>
#include <toasts/toast_notification.hpp>
#include <wolv/utils/guards.hpp>
namespace hex::plugin::builtin {
void registerProviders() {
ContentRegistry::Provider::add<FileProvider>(false);
ContentRegistry::Provider::add<NullProvider>(false);
ContentRegistry::Provider::add<MemoryFileProvider>(false);
ContentRegistry::Provider::add<ViewProvider>(false);
ProjectFile::registerHandler({
.basePath = "providers",
.required = true,
.load = [](const std::fs::path &basePath, const Tar &tar) {
auto json = nlohmann::json::parse(tar.readString(basePath / "providers.json"));
auto providerIds = json.at("providers").get<std::vector<int>>();
bool success = true;
std::map<hex::prv::Provider*, std::string> providerWarnings;
for (const auto &id : providerIds) {
auto providerSettings = nlohmann::json::parse(tar.readString(basePath / fmt::format("{}.json", id)));
auto providerType = providerSettings.at("type").get<std::string>();
auto newProvider = ImHexApi::Provider::createProvider(providerType, true, false);
ON_SCOPE_EXIT {
if (!success) {
for (auto &task : TaskManager::getRunningTasks())
task->interrupt();
TaskManager::runWhenTasksFinished([]{
for (const auto &provider : ImHexApi::Provider::getProviders())
ImHexApi::Provider::remove(provider, true);
});
}
};
if (newProvider == nullptr) {
// If a provider is not created, it will be overwritten when saving the project,
// so we should prevent the project from loading at all
ui::ToastError::open(
fmt::format("hex.builtin.popup.error.project.load"_lang,
fmt::format("hex.builtin.popup.error.project.load.create_provider"_lang, providerType)
)
);
success = false;
break;
}
newProvider->setID(id);
bool loaded = false;
try {
newProvider->loadSettings(providerSettings.at("settings"));
loaded = true;
} catch (const std::exception &e){
providerWarnings[newProvider] = e.what();
}
if (loaded) {
if (!newProvider->open() || !newProvider->isAvailable() || !newProvider->isReadable()) {
providerWarnings[newProvider] = newProvider->getErrorMessage();
} else {
EventProviderOpened::post(newProvider);
}
}
}
std::string warningMessage;
for (const auto &warning : providerWarnings){
ImHexApi::Provider::remove(warning.first);
warningMessage.append(
fmt::format("\n - {} : {}", warning.first->getName(), warning.second));
}
// If no providers were opened, display an error with
// the warnings that happened when opening them
if (ImHexApi::Provider::getProviders().empty()) {
ui::ToastError::open(fmt::format("{}{}", "hex.builtin.popup.error.project.load"_lang, "hex.builtin.popup.error.project.load.no_providers"_lang, warningMessage));
return false;
} else {
// Else, if there are warnings, still display them
if (warningMessage.empty()) {
return true;
} else {
ui::ToastWarning::open(fmt::format("hex.builtin.popup.error.project.load.some_providers_failed"_lang, warningMessage));
}
return success;
}
},
.store = [](const std::fs::path &basePath, const Tar &tar) {
std::vector<int> providerIds;
for (const auto &provider : ImHexApi::Provider::getProviders()) {
auto id = provider->getID();
providerIds.push_back(id);
nlohmann::json json;
json["type"] = provider->getTypeName();
json["settings"] = provider->storeSettings({});
tar.writeString(basePath / fmt::format("{}.json", id), json.dump(4));
}
tar.writeString(basePath / "providers.json",
nlohmann::json({ { "providers", providerIds } }).dump(4)
);
return true;
}
});
}
}

View File

File diff suppressed because one or more lines are too long

View File

@@ -33,13 +33,13 @@
"fluxengine.view.physical.name": "FluxEngine physical sector map",
"fluxengine.view.controlpanel.name": "FluxEngine controls",
"fluxengine.view.controlpanel.readDevice": "Read",
"fluxengine.view.controlpanel.writeDevice": "Write",
"fluxengine.view.controlpanel.readDevice": "Read from device",
"fluxengine.view.controlpanel.writeDevice": "Write to device",
"fluxengine.view.controlpanel.writeFlux": "Write flux file",
"fluxengine.view.controlpanel.readImage": "Load disk image",
"fluxengine.view.controlpanel.writeImage": "Save disk image",
"fluxengine.view.controlpanel.rereadBad": "Re-read bad tracks",
"fluxengine.view.controlpanel.createBlank": "Empty filesystem",
"fluxengine.view.controlpanel.createBlank": "Make empty image",
"fluxengine.view.controlpanel.stop": "Stop",
"fluxengine.view.log.name": "FluxEngine log viewer",
@@ -47,6 +47,13 @@
"fluxengine.view.visualiser.name": "FluxEngine disk visualiser",
"fluxengine.view.visualiser.missingData": "Partial visualisation",
"fluxengine.view.exerciser.name": "FluxEngine disk exerciser",
"fluxengine.view.exerciser.drive": "Drive",
"fluxengine.view.exerciser.cylinder": "Cylinder",
"fluxengine.view.exerciser.nudgeDown": "Nudge down",
"fluxengine.view.exerciser.reset": "Reset",
"fluxengine.view.exerciser.nudgeUp": "Nudge up",
"fluxengine.view.status.runningSuffix": "...",
"fluxengine.view.status.succeededSuffix": "... done",
"fluxengine.view.status.failedSuffix": "... failed",
@@ -57,5 +64,9 @@
"fluxengine.view.status.writeImage": "Writing image file to disk",
"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?"
"fluxengine.messages.writingFluxToFile": "The current configuration is to write to a flux file rather than to hardware. Is this what you intended?",
"fluxengine.menu.name": "Disk",
"fluxengine.menu.disk.exerciser": "Disk exerciser",
"fluxengine.menu.workspace.reset": "Reset workspace"
}

View File

@@ -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)
{