impr: Handle provider opening more centrally, switch to existing provider if same file is being opened again

(cherry picked from commit 89004574d3)
This commit is contained in:
WerWolv
2025-12-17 12:55:24 +01:00
parent cae063985a
commit 51d773cf14
46 changed files with 208 additions and 155 deletions

View File

@@ -358,10 +358,10 @@ namespace hex::plugin::builtin {
std::fs::path filePath = reinterpret_cast<const char8_t*>(args[0].data());
FileProvider provider;
provider.setPath(filePath);
if (!provider.open()) {
log::println("Failed to open file '{}'", args[0]);
auto result = provider.open();
if (!result.isFailure()) {
log::println("Failed to open file '{}': {}", args[0], result.getErrorMessage());
std::exit(EXIT_FAILURE);
}

View File

@@ -52,15 +52,10 @@ namespace hex::plugin::builtin {
auto provider = ImHexApi::Provider::createProvider("hex.builtin.provider.file", true);
if (auto *fileProvider = dynamic_cast<FileProvider*>(provider.get()); fileProvider != nullptr) {
fileProvider->setPath(path);
if (!provider->open() || !provider->isAvailable()) {
ui::ToastError::open(fmt::format("hex.builtin.provider.error.open"_lang, provider->getErrorMessage()));
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider.get()); });
return;
}
EventProviderOpened::post(fileProvider);
ImHexApi::Provider::openProvider(provider);
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
ImHexApi::Provider::setCurrentProvider(provider.get());
glfwRequestWindowAttention(ImHexApi::System::getMainWindowHandle());
glfwFocusWindow(ImHexApi::System::getMainWindowHandle());
@@ -182,11 +177,8 @@ namespace hex::plugin::builtin {
RequestOpenWindow::subscribe([](const std::string &name) {
if (name == "Create File") {
auto newProvider = hex::ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true);
if (newProvider != nullptr && !newProvider->open())
hex::ImHexApi::Provider::remove(newProvider.get());
else
EventProviderOpened::post(newProvider.get());
auto newProvider = ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true);
ImHexApi::Provider::openProvider(newProvider);
} else if (name == "Open File") {
fs::openFileBrowser(fs::DialogMode::Open, { }, [](const auto &path) {
if (path.extension() == ".hexproj") {
@@ -204,12 +196,8 @@ namespace hex::plugin::builtin {
return;
newProvider->setPath(path);
if (!newProvider->open()) {
hex::ImHexApi::Provider::remove(newProvider);
} else {
EventProviderOpened::post(newProvider);
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
}
ImHexApi::Provider::openProvider(provider);
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
}, {}, true);
} else if (name == "Open Project") {
fs::openFileBrowser(fs::DialogMode::Open, { {"Project File", "hexproj"} },
@@ -226,34 +214,20 @@ namespace hex::plugin::builtin {
});
// Handles the provider initialization, and calls EventProviderOpened if successful
EventProviderCreated::subscribe([](hex::prv::Provider *provider) {
EventProviderCreated::subscribe([](std::shared_ptr<prv::Provider> provider) {
if (provider->shouldSkipLoadInterface())
return;
if (auto *filePickerProvider = dynamic_cast<prv::IProviderFilePicker*>(provider); filePickerProvider != nullptr) {
if (auto *filePickerProvider = dynamic_cast<prv::IProviderFilePicker*>(provider.get()); filePickerProvider != nullptr) {
if (!filePickerProvider->handleFilePicker()) {
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider); });
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider.get()); });
return;
}
TaskManager::createBlockingTask("hex.builtin.provider.opening", TaskManager::NoProgress, [provider]() {
if (!provider->open()) {
ui::ToastError::open(fmt::format("hex.builtin.provider.error.open"_lang, provider->getErrorMessage()));
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider); });
} else {
TaskManager::doLater([provider]{ EventProviderOpened::post(provider); });
}
});
ImHexApi::Provider::openProvider(provider);
}
else if (dynamic_cast<prv::IProviderLoadInterface*>(provider) == nullptr) {
TaskManager::createBlockingTask("hex.builtin.provider.opening", TaskManager::NoProgress, [provider]() {
if (!provider->open() || !provider->isAvailable()) {
ui::ToastError::open(fmt::format("hex.builtin.provider.error.open"_lang, provider->getErrorMessage()));
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider); });
} else {
TaskManager::doLater([provider]{ EventProviderOpened::post(provider); });
}
});
else if (dynamic_cast<prv::IProviderLoadInterface*>(provider.get()) == nullptr) {
ImHexApi::Provider::openProvider(provider);
}
});
@@ -419,6 +393,25 @@ namespace hex::plugin::builtin {
});
});
RequestOpenProvider::subscribe([](std::shared_ptr<prv::Provider> provider) {
TaskManager::createBlockingTask("hex.builtin.provider.opening", TaskManager::NoProgress, [provider]() {
auto result = provider->open();
if (result.isFailure()) {
ui::ToastError::open(fmt::format("hex.builtin.provider.error.open"_lang, result.getErrorMessage()));
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider.get()); });
} else if (result.isRedirecting()) {
TaskManager::doLater([result, provider] {
ImHexApi::Provider::remove(provider.get());
ImHexApi::Provider::setCurrentProvider(result.getRedirectProvider());
});
} else {
if (result.isWarning())
ui::ToastWarning::open(std::string(result.getErrorMessage()));
TaskManager::doLater([provider]{ EventProviderOpened::post(provider.get()); });
}
});
});
fs::setFileBrowserErrorCallback([](const std::string& errMsg){
#if defined(NFD_PORTAL)
ui::PopupError::open(fmt::format("hex.builtin.popup.error.file_dialog.portal"_lang, errMsg));

View File

@@ -377,7 +377,7 @@ namespace hex::plugin::builtin {
/* Create File */
ContentRegistry::UserInterface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.create_file" }, ICON_VS_FILE, 1050, CTRLCMD + Keys::N + AllowWhileTyping + ShowOnWelcomeScreen, [] {
auto newProvider = hex::ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true);
if (newProvider != nullptr && !newProvider->open())
if (newProvider != nullptr && newProvider->open().isFailure())
hex::ImHexApi::Provider::remove(newProvider.get());
else
EventProviderOpened::post(newProvider.get());
@@ -401,7 +401,7 @@ namespace hex::plugin::builtin {
auto provider = ImHexApi::Provider::get();
provider->close();
if (!provider->open())
if (provider->open().isFailure())
ImHexApi::Provider::remove(provider, true);
EventDataChanged::post(provider);

View File

@@ -90,8 +90,9 @@ namespace hex::plugin::builtin {
providerWarnings[newProvider.get()] = e.what();
}
if (loaded) {
if (!newProvider->open() || !newProvider->isAvailable() || !newProvider->isReadable()) {
providerWarnings[newProvider.get()] = newProvider->getErrorMessage();
auto result = newProvider->open();
if (result.isFailure() || !newProvider->isAvailable() || !newProvider->isReadable()) {
providerWarnings[newProvider.get()] = result.getErrorMessage();
} else {
EventProviderOpened::post(newProvider.get());
}

View File

@@ -157,7 +157,7 @@ namespace hex::plugin::builtin {
}
#endif
bool DiskProvider::open() {
prv::Provider::OpenResult DiskProvider::open() {
m_readable = true;
m_writable = true;
@@ -207,18 +207,16 @@ namespace hex::plugin::builtin {
const auto &path = m_path.native();
m_diskHandle = ::open(path.c_str(), O_RDWR);
OpenResult result;
if (m_diskHandle == -1) {
this->setErrorMessage(fmt::format("hex.builtin.provider.disk.error.read_rw"_lang, path, formatSystemError(errno)));
log::warn("{}", this->getErrorMessage());
result = OpenResult::warning(fmt::format("hex.builtin.provider.disk.error.read_rw"_lang, path, formatSystemError(errno)));
m_diskHandle = ::open(path.c_str(), O_RDONLY);
m_writable = false;
}
if (m_diskHandle == -1) {
this->setErrorMessage(fmt::format("hex.builtin.provider.disk.error.read_ro"_lang, path, formatSystemError(errno)));
log::warn("{}", this->getErrorMessage());
m_readable = false;
return false;
return OpenResult::failure(fmt::format("hex.builtin.provider.disk.error.read_ro"_lang, path, formatSystemError(errno)));
}
u64 diskSize = 0;
@@ -228,7 +226,7 @@ namespace hex::plugin::builtin {
#endif
return true;
return result;
}
void DiskProvider::close() {

View File

@@ -198,24 +198,23 @@ namespace hex::plugin::builtin {
m_path.make_preferred();
}
bool FileProvider::open() {
prv::Provider::OpenResult FileProvider::open() {
const size_t maxMemoryFileSize = ContentRegistry::Settings::read<u64>("hex.builtin.setting.general", "hex.builtin.setting.general.max_mem_file_size", 128_MiB);
size_t fileSize = 0x00;
{
wolv::io::File file(m_path, wolv::io::File::Mode::Read);
if (!file.isValid()) {
this->setErrorMessage(fmt::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), formatSystemError(file.getOpenError().value_or(0))));
return false;
return OpenResult::failure(fmt::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), formatSystemError(file.getOpenError().value_or(0))));
}
fileSize = file.getSize();
}
const bool directAccess = fileSize >= maxMemoryFileSize;
const bool result = open(directAccess);
const auto result = open(directAccess);
if (result && directAccess) {
if (result.isSuccess() && directAccess) {
m_writable = false;
ui::BannerButton::open(ICON_VS_WARNING, "hex.builtin.provider.file.too_large", ImColor(135, 116, 66), "hex.builtin.provider.file.too_large.allow_write", [this]{
@@ -227,7 +226,7 @@ namespace hex::plugin::builtin {
return result;
}
bool FileProvider::open(bool directAccess) {
prv::Provider::OpenResult FileProvider::open(bool directAccess) {
m_readable = true;
m_writable = true;
@@ -238,8 +237,7 @@ namespace hex::plugin::builtin {
file = wolv::io::File(m_path, wolv::io::File::Mode::Read);
if (!file.isValid()) {
m_readable = false;
this->setErrorMessage(fmt::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), formatSystemError(file.getOpenError().value_or(0))));
return false;
return OpenResult::failure(fmt::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), formatSystemError(file.getOpenError().value_or(0))));
}
ui::ToastInfo::open("hex.builtin.popup.error.read_only"_lang);
@@ -258,8 +256,7 @@ namespace hex::plugin::builtin {
});
if (alreadyOpenedFileProvider != s_openedFiles.end()) {
ImHexApi::Provider::setCurrentProvider(*alreadyOpenedFileProvider);
return false;
return OpenResult::redirect(*alreadyOpenedFileProvider);
} else {
s_openedFiles.insert(this);
}
@@ -281,7 +278,7 @@ namespace hex::plugin::builtin {
m_changeEventAcknowledgementPending = false;
return true;
return {};
}
@@ -313,10 +310,6 @@ namespace hex::plugin::builtin {
if (!wolv::io::fs::exists(fullPath))
fullPath = path;
if (!wolv::io::fs::exists(fullPath)) {
this->setErrorMessage(fmt::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), formatSystemError(ENOENT)));
}
path = std::move(fullPath);
}

View File

@@ -254,7 +254,7 @@ namespace hex::plugin::builtin {
};
}
bool GDBProvider::open() {
prv::Provider::OpenResult GDBProvider::open() {
std::scoped_lock lock(m_mutex);
CachedProvider::open();
@@ -264,11 +264,11 @@ namespace hex::plugin::builtin {
gdb::sendReceivePackage(m_socket, gdb::createPacket("!"));
gdb::sendReceivePackage(m_socket, gdb::createPacket("Hg0"));
if (m_socket.isConnected()) {
return true;
} else {
return false;
if (!m_socket.isConnected()) {
return OpenResult::failure("hex.builtin.provider.gdb.server.error.not_connected"_lang);
}
return {};
}
void GDBProvider::close() {

View File

@@ -245,21 +245,19 @@ namespace hex::plugin::builtin {
});
}
bool IntelHexProvider::open() {
prv::Provider::OpenResult IntelHexProvider::open() {
auto file = wolv::io::File(m_sourceFilePath, wolv::io::File::Mode::Read);
if (!file.isValid()) {
this->setErrorMessage(fmt::format("hex.builtin.provider.file.error.open"_lang, m_sourceFilePath.string(), formatSystemError(errno)));
return false;
return OpenResult::failure(fmt::format("hex.builtin.provider.file.error.open"_lang, m_sourceFilePath.string(), formatSystemError(errno)));
}
auto data = intel_hex::parseIntelHex(file.readString());
if (!data.has_value()) {
this->setErrorMessage(data.error());
return false;
return OpenResult::failure(data.error());
}
processMemoryRegions(data);
return true;
return {};
}
void IntelHexProvider::close() {

View File

@@ -16,12 +16,12 @@
namespace hex::plugin::builtin {
bool MemoryFileProvider::open() {
prv::Provider::OpenResult MemoryFileProvider::open() {
if (m_data.empty()) {
m_data.resize(1);
}
return true;
return {};
}
void MemoryFileProvider::readRaw(u64 offset, void *buffer, size_t size) {
@@ -54,7 +54,7 @@ namespace hex::plugin::builtin {
if (auto fileProvider = dynamic_cast<FileProvider*>(newProvider.get()); fileProvider != nullptr) {
fileProvider->setPath(path);
if (!fileProvider->open()) {
if (fileProvider->open().isFailure()) {
ImHexApi::Provider::remove(newProvider.get());
} else {
MovePerProviderData::post(this, fileProvider);

View File

@@ -170,21 +170,19 @@ namespace hex::plugin::builtin {
}
bool MotorolaSRECProvider::open() {
prv::Provider::OpenResult MotorolaSRECProvider::open() {
auto file = wolv::io::File(m_sourceFilePath, wolv::io::File::Mode::Read);
if (!file.isValid()) {
this->setErrorMessage(fmt::format("hex.builtin.provider.file.error.open"_lang, m_sourceFilePath.string(), formatSystemError(errno)));
return false;
return OpenResult::failure(fmt::format("hex.builtin.provider.file.error.open"_lang, m_sourceFilePath.string(), formatSystemError(errno)));
}
auto data = motorola_srec::parseMotorolaSREC(file.readString());
if (!data.has_value()) {
this->setErrorMessage(data.error());
return false;
return OpenResult::failure(data.error());
}
processMemoryRegions(data);
return true;
return {};
}
void MotorolaSRECProvider::close() {

View File

@@ -33,21 +33,21 @@
namespace hex::plugin::builtin {
bool ProcessMemoryProvider::open() {
prv::Provider::OpenResult ProcessMemoryProvider::open() {
if (m_selectedProcess == nullptr)
return false;
return OpenResult::failure("hex.builtin.provider.process_memory.error.no_process_selected"_lang);
#if defined(OS_WINDOWS)
m_processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_selectedProcess->id);
if (m_processHandle == nullptr)
return false;
return OpenResult::failure("hex.builtin.provider.process_memory.error.open_process"_lang);
#else
m_processId = pid_t(m_selectedProcess->id);
#endif
this->reloadProcessModules();
return true;
return {};
}
void ProcessMemoryProvider::close() {

View File

@@ -7,13 +7,13 @@
namespace hex::plugin::builtin {
bool UDPProvider::open() {
prv::Provider::OpenResult UDPProvider::open() {
m_udpServer = UDPServer(m_port, [this](std::span<const u8> data) {
this->receive(data);
});
m_udpServer.start();
return true;
return {};
}
void UDPProvider::close() {

View File

@@ -48,9 +48,9 @@ namespace hex::plugin::builtin {
m_provider->save();
}
[[nodiscard]] bool ViewProvider::open() {
[[nodiscard]] prv::Provider::OpenResult ViewProvider::open() {
if (m_provider == this)
return false;
return OpenResult::failure("hex.builtin.provider.view.error.no_provider"_lang);
EventProviderClosing::subscribe(this, [this](const prv::Provider *provider, bool*) {
if (m_provider == provider) {
@@ -59,7 +59,7 @@ namespace hex::plugin::builtin {
}
});
return true;
return {};
}
void ViewProvider::close() {
EventProviderClosing::unsubscribe(this);

View File

@@ -283,14 +283,7 @@ namespace hex::plugin::builtin::recent {
if (provider != nullptr) {
provider->loadSettings(recentEntry.data);
TaskManager::createBlockingTask("hex.builtin.provider.opening", TaskManager::NoProgress, [provider]() {
if (!provider->open() || !provider->isAvailable()) {
ui::ToastError::open(fmt::format("hex.builtin.provider.error.open"_lang, provider->getErrorMessage()));
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider.get()); });
} else {
TaskManager::doLater([provider]{ EventProviderOpened::post(provider.get()); });
}
});
ImHexApi::Provider::openProvider(provider);
updateRecentEntries();
}

View File

@@ -524,7 +524,6 @@ namespace hex::plugin::builtin {
flags |= ImGuiTabItemFlags_UnsavedDocument;
if (i64(i) == selectedProviderIndex && providerJustChanged) {
flags |= ImGuiTabItemFlags_SetSelected;
providerJustChanged = false;
}
static size_t lastSelectedProvider = 0;
@@ -535,7 +534,7 @@ namespace hex::plugin::builtin {
ImGui::EndTabItem();
}
if (isSelected && lastSelectedProvider != i) {
if (isSelected && lastSelectedProvider != i && !providerJustChanged) {
ImHexApi::Provider::setCurrentProvider(i);
lastSelectedProvider = i;
}
@@ -555,6 +554,8 @@ namespace hex::plugin::builtin {
}
}
ImGui::EndTabBar();
providerJustChanged = false;
}
}
ImGui::EndDisabled();

View File

@@ -15,8 +15,9 @@ namespace hex::plugin::builtin {
ViewFullScreenFileInfo::ViewFullScreenFileInfo(std::fs::path filePath) : m_filePath(std::move(filePath)) {
this->m_provider.setPath(m_filePath);
if (!this->m_provider.open()) {
if (this->m_provider.open().isFailure()) {
ui::ToastError::open("hex.builtin.view.fullscreen.file_info.error.file_not_readable"_lang);
return;
}
m_analysisTask = TaskManager::createBlockingTask("hex.builtin.view.fullscreen.file_info.analyzing", TaskManager::NoProgress, [this](Task &task) {

View File

@@ -67,8 +67,9 @@ namespace hex::plugin::builtin {
if (ImGuiExt::DimmedButton(fmt::format("{} {}", ICON_VS_OPEN_PREVIEW, "hex.builtin.view.fullscreen.save_editor.select_file"_lang).c_str(), ImVec2(-1, 0))) {
fs::openFileBrowser(fs::DialogMode::Open, {}, [this](const std::fs::path &path) {
this->m_provider.setPath(path);
if (!this->m_provider.open()) {
if (this->m_provider.open().isFailure()) {
ui::ToastError::open("hex.builtin.view.fullscreen.save_editor.error.not_readable"_lang);
return;
}
ContentRegistry::PatternLanguage::configureRuntime(m_runtime, &m_provider);

View File

@@ -394,10 +394,9 @@ namespace hex::plugin::builtin {
viewProvider->setProvider(region.getStartAddress(), region.getSize(), provider);
viewProvider->setName(fmt::format("'{}' View", name));
if (viewProvider->open()) {
EventProviderOpened::post(viewProvider);
AchievementManager::unlockAchievement("hex.builtin.achievement.hex_editor", "hex.builtin.achievement.hex_editor.open_new_view.name");
}
ImHexApi::Provider::openProvider(newProvider);
AchievementManager::unlockAchievement("hex.builtin.achievement.hex_editor", "hex.builtin.achievement.hex_editor.open_new_view.name");
}
});
}

View File

@@ -1170,8 +1170,7 @@ namespace hex::plugin::builtin {
auto newProvider = ImHexApi::Provider::createProvider("hex.builtin.provider.view", true);
if (auto *viewProvider = dynamic_cast<ViewProvider*>(newProvider.get()); viewProvider != nullptr) {
viewProvider->setProvider(selection->getStartAddress(), selection->getSize(), selection->getProvider());
if (viewProvider->open())
EventProviderOpened::post(viewProvider);
ImHexApi::Provider::openProvider(newProvider);
}
},
[] { return ImHexApi::HexEditor::isSelectionValid() && ImHexApi::Provider::isValid(); },

View File

@@ -11,8 +11,8 @@
namespace hex::plugin::builtin {
ViewProviderSettings::ViewProviderSettings() : View::Modal("hex.builtin.view.provider_settings.name", ICON_VS_SETTINGS) {
EventProviderCreated::subscribe(this, [this](const hex::prv::Provider *provider) {
if (dynamic_cast<const prv::IProviderLoadInterface*>(provider) != nullptr && !provider->shouldSkipLoadInterface())
EventProviderCreated::subscribe(this, [this](std::shared_ptr<prv::Provider> provider) {
if (dynamic_cast<const prv::IProviderLoadInterface*>(provider.get()) != nullptr && !provider->shouldSkipLoadInterface())
this->getWindowOpenState() = true;
});
@@ -43,7 +43,8 @@ namespace hex::plugin::builtin {
ImGui::BeginDisabled(!settingsValid);
if (ImGui::Button("hex.ui.common.open"_lang)) {
if (provider->open()) {
auto result = provider->open();
if (result.isSuccess()) {
EventProviderOpened::post(provider);
this->getWindowOpenState() = false;
@@ -52,7 +53,7 @@ namespace hex::plugin::builtin {
else {
this->getWindowOpenState() = false;
ImGui::CloseCurrentPopup();
auto errorMessage = provider->getErrorMessage();
auto errorMessage = result.getErrorMessage();
if (errorMessage.empty()) {
ui::ToastError::open("hex.builtin.view.provider_settings.load_error"_lang);
} else {

View File

@@ -346,7 +346,7 @@ namespace hex::plugin::builtin {
if (ImGuiExt::BeginSubWindow("hex.builtin.welcome.header.start"_lang, nullptr, ImVec2(), ImGuiChildFlags_AutoResizeX)) {
if (ImGuiExt::IconHyperlink(ICON_VS_NEW_FILE, "hex.builtin.welcome.start.create_file"_lang)) {
auto newProvider = hex::ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true);
if (newProvider != nullptr && !newProvider->open())
if (newProvider != nullptr && newProvider->open().isFailure())
hex::ImHexApi::Provider::remove(newProvider.get());
else
EventProviderOpened::post(newProvider.get());