From 760b8c7a88e815697c11d50cb005b5709880b38d Mon Sep 17 00:00:00 2001 From: WerWolv Date: Tue, 5 Dec 2023 14:32:28 +0100 Subject: [PATCH] impr: Make long running tasks not freeze ImHex, fix saving non-continuous providers Fixes #1454 --- lib/libimhex/source/api/task_manager.cpp | 3 ++ lib/libimhex/source/providers/provider.cpp | 17 +++----- .../content/popups/popup_blocking_task.hpp | 41 +++++++++++++++++++ plugins/builtin/romfs/lang/en_US.json | 2 + .../source/content/views/view_hex_editor.cpp | 6 ++- plugins/builtin/source/ui/hex_editor.cpp | 4 +- 6 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 plugins/builtin/include/content/popups/popup_blocking_task.hpp diff --git a/lib/libimhex/source/api/task_manager.cpp b/lib/libimhex/source/api/task_manager.cpp index 6bdef9fd9..731e6797a 100644 --- a/lib/libimhex/source/api/task_manager.cpp +++ b/lib/libimhex/source/api/task_manager.cpp @@ -207,6 +207,9 @@ namespace hex { return 0; auto task = this->m_task.lock(); + if (task->getMaxValue() == 0) + return 0; + return u32((task->getValue() * 100) / task->getMaxValue()); } diff --git a/lib/libimhex/source/providers/provider.cpp b/lib/libimhex/source/providers/provider.cpp index c61993c42..ff02eb659 100644 --- a/lib/libimhex/source/providers/provider.cpp +++ b/lib/libimhex/source/providers/provider.cpp @@ -9,11 +9,14 @@ #include #include +#include #include namespace hex::prv { + using namespace wolv::literals; + namespace { u32 s_idCounter = 0; @@ -53,21 +56,13 @@ namespace hex::prv { wolv::io::File file(path, wolv::io::File::Mode::Create); if (file.isValid()) { - std::vector buffer(std::min(0xFF'FFFF, this->getActualSize()), 0x00); + std::vector buffer(std::min(2_MiB, this->getActualSize()), 0x00); size_t bufferSize = 0; for (u64 offset = 0; offset < this->getActualSize(); offset += bufferSize) { - bufferSize = buffer.size(); + bufferSize = std::min(buffer.size(), this->getActualSize() - offset); - auto [region, valid] = this->getRegionValidity(offset + this->getBaseAddress()); - if (!valid) - offset = region.getEndAddress() + 1; - - auto [newRegion, newValid] = this->getRegionValidity(offset + this->getBaseAddress()); - bufferSize = std::min(bufferSize, (newRegion.getEndAddress() - offset) + 1); - bufferSize = std::min(bufferSize, this->getActualSize() - offset); - - this->read(offset + this->getBaseAddress(), buffer.data(), bufferSize, true); + this->read(this->getBaseAddress() + offset, buffer.data(), bufferSize, true); file.writeBuffer(buffer.data(), bufferSize); } diff --git a/plugins/builtin/include/content/popups/popup_blocking_task.hpp b/plugins/builtin/include/content/popups/popup_blocking_task.hpp new file mode 100644 index 000000000..297f6b087 --- /dev/null +++ b/plugins/builtin/include/content/popups/popup_blocking_task.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include + +#include + +namespace hex::plugin::builtin { + + class PopupBlockingTask : public Popup { + public: + PopupBlockingTask(TaskHolder &&task) : hex::Popup("hex.builtin.popup.blocking_task.title", false), m_task(std::move(task)) { } + + void drawContent() override { + ImGui::TextUnformatted("hex.builtin.popup.blocking_task.desc"_lang); + ImGui::Separator(); + + if (this->m_task.getProgress() == 0) + ImGuiExt::TextSpinner(""); + else + ImGui::ProgressBar(this->m_task.getProgress() / 100.0F); + + ImGui::NewLine(); + if (ImGui::ButtonEx("hex.builtin.common.cancel"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0)) || ImGui::IsKeyDown(ImGuiKey_Escape)) + this->m_task.interrupt(); + + if (!this->m_task.isRunning()) { + ImGui::CloseCurrentPopup(); + } + } + + [[nodiscard]] ImGuiWindowFlags getFlags() const override { + return ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove; + } + + private: + TaskHolder m_task; + }; + +} \ No newline at end of file diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index e9ac90cd8..bc9e7050b 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -490,6 +490,8 @@ "hex.builtin.popup.exit_application.desc": "You have unsaved changes made to your Project.\nAre you sure you want to exit?", "hex.builtin.popup.exit_application.title": "Exit Application?", "hex.builtin.popup.waiting_for_tasks.title": "Waiting for Tasks", + "hex.builtin.popup.blocking_task.title": "Running Task", + "hex.builtin.popup.blocking_task.desc": "A task is currently executing.", "hex.builtin.popup.save_layout.title": "Save Layout", "hex.builtin.popup.save_layout.desc": "Enter a name under which to save the current layout.", "hex.builtin.popup.waiting_for_tasks.desc": "There are still tasks running in the background.\nImHex will close after they are finished.", diff --git a/plugins/builtin/source/content/views/view_hex_editor.cpp b/plugins/builtin/source/content/views/view_hex_editor.cpp index 90b08ac63..831175ab0 100644 --- a/plugins/builtin/source/content/views/view_hex_editor.cpp +++ b/plugins/builtin/source/content/views/view_hex_editor.cpp @@ -16,6 +16,7 @@ #include #include +#include using namespace std::literals::string_literals; @@ -692,7 +693,10 @@ namespace hex::plugin::builtin { static void saveAs() { fs::openFileBrowser(fs::DialogMode::Save, {}, [](const auto &path) { - ImHexApi::Provider::get()->saveAs(path); + auto provider = ImHexApi::Provider::get(); + PopupBlockingTask::open(TaskManager::createTask("Saving...", TaskManager::NoProgress, [=](Task &){ + provider->saveAs(path); + })); }); } diff --git a/plugins/builtin/source/ui/hex_editor.cpp b/plugins/builtin/source/ui/hex_editor.cpp index 7e5df3c77..276a75632 100644 --- a/plugins/builtin/source/ui/hex_editor.cpp +++ b/plugins/builtin/source/ui/hex_editor.cpp @@ -759,8 +759,8 @@ namespace hex::plugin::builtin::ui { ImGuiExt::TextFormatted("{}:", "hex.builtin.hex_editor.data_size"_lang); ImGui::SameLine(); ImGuiExt::TextFormattedSelectable("0x{0:08X} (0x{1:X} | {2})", - this->m_provider->getActualSize(), - this->m_provider->getActualSize(), + this->m_provider->getBaseAddress(), + this->m_provider->getBaseAddress() + this->m_provider->getActualSize(), this->m_showHumanReadableUnits ? hex::toByteString(this->m_provider->getActualSize()) : hex::format("{}", this->m_provider->getActualSize())