From d38d261bbcc2899bd199d2db84485398af0aaf7b Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sun, 9 Jan 2022 21:27:59 +0100 Subject: [PATCH] provider: Fixed applying of IPS patches. Handle applying asynchronously --- .../include/content/views/view_hexeditor.hpp | 2 + .../source/content/views/view_hexeditor.cpp | 97 ++++++++++++++----- .../source/content/views/view_patches.cpp | 53 ++++++---- plugins/builtin/source/lang/en_US.cpp | 1 + plugins/libimhex/include/hex/api/task.hpp | 4 + .../include/hex/providers/provider.hpp | 5 +- plugins/libimhex/source/api/task.cpp | 11 +++ .../libimhex/source/providers/provider.cpp | 27 +++++- 8 files changed, 149 insertions(+), 51 deletions(-) diff --git a/plugins/builtin/include/content/views/view_hexeditor.hpp b/plugins/builtin/include/content/views/view_hexeditor.hpp index e6ea73bff..9ce66f2cc 100644 --- a/plugins/builtin/include/content/views/view_hexeditor.hpp +++ b/plugins/builtin/include/content/views/view_hexeditor.hpp @@ -54,6 +54,8 @@ namespace hex::plugin::builtin { hex::EncodingFile m_currEncodingFile; u8 m_highlightAlpha = 0x80; + bool m_processingImportExport = false; + void drawSearchPopup(); void drawGotoPopup(); void drawEditPopup(); diff --git a/plugins/builtin/source/content/views/view_hexeditor.cpp b/plugins/builtin/source/content/views/view_hexeditor.cpp index e51899d6f..8efa75396 100644 --- a/plugins/builtin/source/content/views/view_hexeditor.cpp +++ b/plugins/builtin/source/content/views/view_hexeditor.cpp @@ -19,6 +19,7 @@ #undef __STRICT_ANSI__ #include +#include #include #if defined(OS_WINDOWS) @@ -426,31 +427,61 @@ namespace hex::plugin::builtin { ImGui::Separator(); - if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.import.ips"_lang)) { + if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.import.ips"_lang, nullptr, false, !this->m_processingImportExport)) { hex::openFileBrowser("hex.builtin.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) { - auto patchData = File(path, File::Mode::Read).readBytes(); - auto patch = hex::loadIPSPatch(patchData); + this->m_processingImportExport = true; + std::thread([this, path] { + auto task = ImHexApi::Tasks::createTask("hex.builtin.view.hexeditor.processing", 0); + + auto patchData = File(path, File::Mode::Read).readBytes(); + auto patch = hex::loadIPSPatch(patchData); + + task.setMaxValue(patch.size()); + + auto provider = ImHexApi::Provider::get(); + + u64 progress = 0; + for (auto &[address, value] : patch) { + provider->addPatch(address, &value, 1); + progress++; + task.update(progress); + } + + provider->createUndoPoint(); + this->m_processingImportExport = false; + }).detach(); - auto provider = ImHexApi::Provider::get(); - for (auto &[address, value] : patch) { - provider->write(address, &value, 1); - } this->getWindowOpenState() = true; }); } - if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.import.ips32"_lang)) { + if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.import.ips32"_lang, nullptr, false, !this->m_processingImportExport)) { hex::openFileBrowser("hex.builtin.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) { - auto patchData = File(path, File::Mode::Read).readBytes(); - auto patch = hex::loadIPS32Patch(patchData); + this->m_processingImportExport = true; + std::thread([this, path] { + auto task = ImHexApi::Tasks::createTask("hex.builtin.view.hexeditor.processing", 0); + + auto patchData = File(path, File::Mode::Read).readBytes(); + auto patch = hex::loadIPS32Patch(patchData); + + task.setMaxValue(patch.size()); + + auto provider = ImHexApi::Provider::get(); + + u64 progress = 0; + for (auto &[address, value] : patch) { + provider->addPatch(address, &value, 1); + progress++; + task.update(progress); + } + + provider->createUndoPoint(); + this->m_processingImportExport = false; + }).detach(); - auto provider = ImHexApi::Provider::get(); - for (auto &[address, value] : patch) { - provider->write(address, &value, 1); - } this->getWindowOpenState() = true; }); } @@ -465,7 +496,7 @@ namespace hex::plugin::builtin { } if (ImGui::BeginMenu("hex.builtin.view.hexeditor.menu.file.export"_lang, providerValid && provider->isWritable())) { - if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.export.ips"_lang)) { + if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.export.ips"_lang, nullptr, false, !this->m_processingImportExport)) { Patches patches = provider->getPatches(); if (!patches.contains(0x00454F45) && patches.contains(0x00454F46)) { u8 value = 0; @@ -473,12 +504,21 @@ namespace hex::plugin::builtin { patches[0x00454F45] = value; } - this->m_dataToSave = generateIPSPatch(patches); - hex::openFileBrowser("hex.builtin.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) { - this->saveToFile(path, this->m_dataToSave); - }); + this->m_processingImportExport = true; + std::thread([this, patches]{ + auto task = ImHexApi::Tasks::createTask("hex.builtin.view.hexeditor.processing", 0); + + this->m_dataToSave = generateIPSPatch(patches); + this->m_processingImportExport = false; + + View::doLater([this]{ + hex::openFileBrowser("hex.builtin.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) { + this->saveToFile(path, this->m_dataToSave); + }); + }); + }).detach(); } - if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.export.ips32"_lang)) { + if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.export.ips32"_lang, nullptr, false, !this->m_processingImportExport)) { Patches patches = provider->getPatches(); if (!patches.contains(0x00454F45) && patches.contains(0x45454F46)) { u8 value = 0; @@ -486,10 +526,19 @@ namespace hex::plugin::builtin { patches[0x45454F45] = value; } - this->m_dataToSave = generateIPS32Patch(patches); - hex::openFileBrowser("hex.builtin.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) { - this->saveToFile(path, this->m_dataToSave); - }); + this->m_processingImportExport = true; + std::thread([this, patches]{ + auto task = ImHexApi::Tasks::createTask("hex.builtin.view.hexeditor.processing", 0); + + this->m_dataToSave = generateIPS32Patch(patches); + this->m_processingImportExport = false; + + View::doLater([this]{ + hex::openFileBrowser("hex.builtin.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) { + this->saveToFile(path, this->m_dataToSave); + }); + }); + }).detach(); } ImGui::EndMenu(); diff --git a/plugins/builtin/source/content/views/view_patches.cpp b/plugins/builtin/source/content/views/view_patches.cpp index eb4742cbf..5695c8074 100644 --- a/plugins/builtin/source/content/views/view_patches.cpp +++ b/plugins/builtin/source/content/views/view_patches.cpp @@ -46,28 +46,41 @@ namespace hex::plugin::builtin { auto& patches = provider->getPatches(); u32 index = 0; - for (const auto &[address, patch] : patches) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Selectable(("##patchLine" + std::to_string(index)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) { - EventManager::post(Region { address, 1 }); + ImGuiListClipper clipper(patches.size()); + + while (clipper.Step()) { + auto iter = patches.begin(); + for (auto i = 0; i < clipper.DisplayStart; i++) + iter++; + + for (auto i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + const auto &[address, patch] = *iter; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + if (ImGui::Selectable(("##patchLine" + std::to_string(index)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) { + EventManager::post(Region { address, 1 }); + } + if (ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) { + ImGui::OpenPopup("PatchContextMenu"); + this->m_selectedPatch = address; + } + ImGui::SameLine(); + ImGui::TextFormatted("0x{0:08X}", address); + + ImGui::TableNextColumn(); + u8 previousValue = 0x00; + provider->readRaw(address, &previousValue, sizeof(u8)); + ImGui::TextFormatted("0x{0:02X}", previousValue); + + ImGui::TableNextColumn(); + ImGui::TextFormatted("0x{0:02X}", patch); + index += 1; + + iter++; } - if (ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) { - ImGui::OpenPopup("PatchContextMenu"); - this->m_selectedPatch = address; - } - ImGui::SameLine(); - ImGui::TextFormatted("0x{0:08X}", address); - - ImGui::TableNextColumn(); - u8 previousValue = 0x00; - provider->readRaw(address, &previousValue, sizeof(u8)); - ImGui::TextFormatted("0x{0:02X}", previousValue); - - ImGui::TableNextColumn(); - ImGui::TextFormatted("0x{0:02X}", patch); - index += 1; } if (ImGui::BeginPopup("PatchContextMenu")) { diff --git a/plugins/builtin/source/lang/en_US.cpp b/plugins/builtin/source/lang/en_US.cpp index 2ce66f260..39a2509e8 100644 --- a/plugins/builtin/source/lang/en_US.cpp +++ b/plugins/builtin/source/lang/en_US.cpp @@ -188,6 +188,7 @@ namespace hex::plugin::builtin { { "hex.builtin.view.hexeditor.script.script.title", "Loader Script: Open Script" }, { "hex.builtin.view.hexeditor.script.file", "File" }, { "hex.builtin.view.hexeditor.script.file.title", "Loader Script: Open File" }, + { "hex.builtin.view.hexeditor.processing", "Processing" }, { "hex.builtin.view.hexeditor.menu.file.open_file", "Open File..." }, { "hex.builtin.view.hexeditor.menu.file.open_recent", "Open Recent" }, diff --git a/plugins/libimhex/include/hex/api/task.hpp b/plugins/libimhex/include/hex/api/task.hpp index 89d54121f..9002a1dd7 100644 --- a/plugins/libimhex/include/hex/api/task.hpp +++ b/plugins/libimhex/include/hex/api/task.hpp @@ -11,6 +11,7 @@ namespace hex { Task(const std::string& unlocalizedName, u64 maxValue); ~Task(); + void setMaxValue(u64 maxValue); void update(u64 currValue); void finish(); @@ -20,6 +21,9 @@ namespace hex { [[nodiscard]] const std::string& getName() const; + [[nodiscard]] + bool isPending() const; + private: std::string m_name; u64 m_maxValue, m_currValue; diff --git a/plugins/libimhex/include/hex/providers/provider.hpp b/plugins/libimhex/include/hex/providers/provider.hpp index eaa805af2..efd1af19d 100644 --- a/plugins/libimhex/include/hex/providers/provider.hpp +++ b/plugins/libimhex/include/hex/providers/provider.hpp @@ -63,7 +63,8 @@ namespace hex::prv { [[nodiscard]] virtual bool open() = 0; virtual void close() = 0; - void addPatch(u64 offset, const void *buffer, size_t size); + void addPatch(u64 offset, const void *buffer, size_t size, bool createUndo = false); + void createUndoPoint(); void undo(); void redo(); @@ -81,7 +82,7 @@ namespace hex::prv { u64 m_baseAddress = 0; u32 m_patchTreeOffset = 0; - std::vector> m_patches; + std::list> m_patches; std::list m_overlays; }; diff --git a/plugins/libimhex/source/api/task.cpp b/plugins/libimhex/source/api/task.cpp index 5f9e0c7c3..e74529529 100644 --- a/plugins/libimhex/source/api/task.cpp +++ b/plugins/libimhex/source/api/task.cpp @@ -16,15 +16,26 @@ namespace hex { SharedData::runningTasks.remove(this); } + void Task::setMaxValue(u64 maxValue) { + this->m_maxValue = maxValue; + } + void Task::update(u64 currValue) { if (this->m_currValue < this->m_maxValue) this->m_currValue = currValue; } double Task::getProgress() const { + if (this->m_maxValue == 0) + return 100; + return static_cast(this->m_currValue) / static_cast(this->m_maxValue); } + bool Task::isPending() const { + return this->m_maxValue == 0; + } + const std::string& Task::getName() const { return this->m_name; } diff --git a/plugins/libimhex/source/providers/provider.cpp b/plugins/libimhex/source/providers/provider.cpp index 10e709a49..7f12e9e29 100644 --- a/plugins/libimhex/source/providers/provider.cpp +++ b/plugins/libimhex/source/providers/provider.cpp @@ -50,11 +50,19 @@ namespace hex::prv { std::map& Provider::getPatches() { - return *(this->m_patches.end() - 1 - this->m_patchTreeOffset); + auto iter = this->m_patches.end(); + for (auto i = 0; i < this->m_patchTreeOffset + 1; i++) + iter--; + + return *(iter); } const std::map& Provider::getPatches() const { - return *(this->m_patches.end() - 1 - this->m_patchTreeOffset); + auto iter = this->m_patches.end(); + for (auto i = 0; i < this->m_patchTreeOffset + 1; i++) + iter--; + + return *(iter); } void Provider::applyPatches() { @@ -117,18 +125,27 @@ namespace hex::prv { return page; } - void Provider::addPatch(u64 offset, const void *buffer, size_t size) { + void Provider::addPatch(u64 offset, const void *buffer, size_t size, bool createUndo) { if (this->m_patchTreeOffset > 0) { - this->m_patches.erase(this->m_patches.end() - this->m_patchTreeOffset, this->m_patches.end()); + auto iter = this->m_patches.end(); + for (auto i = 0; i < this->m_patchTreeOffset; i++) + iter--; + + this->m_patches.erase(iter, this->m_patches.end()); this->m_patchTreeOffset = 0; } - this->m_patches.push_back(getPatches()); + if (createUndo) + createUndoPoint(); for (u64 i = 0; i < size; i++) getPatches()[offset + i] = reinterpret_cast(buffer)[i]; } + void Provider::createUndoPoint() { + this->m_patches.push_back(getPatches()); + } + void Provider::undo() { if (canUndo()) this->m_patchTreeOffset++;