provider: Fixed applying of IPS patches. Handle applying asynchronously
This commit is contained in:
parent
4e636381fd
commit
d38d261bbc
@ -54,6 +54,8 @@ namespace hex::plugin::builtin {
|
|||||||
hex::EncodingFile m_currEncodingFile;
|
hex::EncodingFile m_currEncodingFile;
|
||||||
u8 m_highlightAlpha = 0x80;
|
u8 m_highlightAlpha = 0x80;
|
||||||
|
|
||||||
|
bool m_processingImportExport = false;
|
||||||
|
|
||||||
void drawSearchPopup();
|
void drawSearchPopup();
|
||||||
void drawGotoPopup();
|
void drawGotoPopup();
|
||||||
void drawEditPopup();
|
void drawEditPopup();
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#undef __STRICT_ANSI__
|
#undef __STRICT_ANSI__
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#if defined(OS_WINDOWS)
|
#if defined(OS_WINDOWS)
|
||||||
@ -426,31 +427,61 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
ImGui::Separator();
|
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) {
|
hex::openFileBrowser("hex.builtin.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||||
auto patchData = File(path, File::Mode::Read).readBytes();
|
this->m_processingImportExport = true;
|
||||||
auto patch = hex::loadIPSPatch(patchData);
|
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;
|
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) {
|
hex::openFileBrowser("hex.builtin.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||||
auto patchData = File(path, File::Mode::Read).readBytes();
|
this->m_processingImportExport = true;
|
||||||
auto patch = hex::loadIPS32Patch(patchData);
|
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;
|
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::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();
|
Patches patches = provider->getPatches();
|
||||||
if (!patches.contains(0x00454F45) && patches.contains(0x00454F46)) {
|
if (!patches.contains(0x00454F45) && patches.contains(0x00454F46)) {
|
||||||
u8 value = 0;
|
u8 value = 0;
|
||||||
@ -473,12 +504,21 @@ namespace hex::plugin::builtin {
|
|||||||
patches[0x00454F45] = value;
|
patches[0x00454F45] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->m_dataToSave = generateIPSPatch(patches);
|
this->m_processingImportExport = true;
|
||||||
hex::openFileBrowser("hex.builtin.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) {
|
std::thread([this, patches]{
|
||||||
this->saveToFile(path, this->m_dataToSave);
|
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();
|
Patches patches = provider->getPatches();
|
||||||
if (!patches.contains(0x00454F45) && patches.contains(0x45454F46)) {
|
if (!patches.contains(0x00454F45) && patches.contains(0x45454F46)) {
|
||||||
u8 value = 0;
|
u8 value = 0;
|
||||||
@ -486,10 +526,19 @@ namespace hex::plugin::builtin {
|
|||||||
patches[0x45454F45] = value;
|
patches[0x45454F45] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->m_dataToSave = generateIPS32Patch(patches);
|
this->m_processingImportExport = true;
|
||||||
hex::openFileBrowser("hex.builtin.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) {
|
std::thread([this, patches]{
|
||||||
this->saveToFile(path, this->m_dataToSave);
|
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();
|
ImGui::EndMenu();
|
||||||
|
@ -46,28 +46,41 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
auto& patches = provider->getPatches();
|
auto& patches = provider->getPatches();
|
||||||
u32 index = 0;
|
u32 index = 0;
|
||||||
for (const auto &[address, patch] : patches) {
|
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGuiListClipper clipper(patches.size());
|
||||||
ImGui::TableNextColumn();
|
|
||||||
if (ImGui::Selectable(("##patchLine" + std::to_string(index)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
while (clipper.Step()) {
|
||||||
EventManager::post<RequestSelectionChange>(Region { address, 1 });
|
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<RequestSelectionChange>(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")) {
|
if (ImGui::BeginPopup("PatchContextMenu")) {
|
||||||
|
@ -188,6 +188,7 @@ namespace hex::plugin::builtin {
|
|||||||
{ "hex.builtin.view.hexeditor.script.script.title", "Loader Script: Open Script" },
|
{ "hex.builtin.view.hexeditor.script.script.title", "Loader Script: Open Script" },
|
||||||
{ "hex.builtin.view.hexeditor.script.file", "File" },
|
{ "hex.builtin.view.hexeditor.script.file", "File" },
|
||||||
{ "hex.builtin.view.hexeditor.script.file.title", "Loader Script: Open 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_file", "Open File..." },
|
||||||
{ "hex.builtin.view.hexeditor.menu.file.open_recent", "Open Recent" },
|
{ "hex.builtin.view.hexeditor.menu.file.open_recent", "Open Recent" },
|
||||||
|
@ -11,6 +11,7 @@ namespace hex {
|
|||||||
Task(const std::string& unlocalizedName, u64 maxValue);
|
Task(const std::string& unlocalizedName, u64 maxValue);
|
||||||
~Task();
|
~Task();
|
||||||
|
|
||||||
|
void setMaxValue(u64 maxValue);
|
||||||
void update(u64 currValue);
|
void update(u64 currValue);
|
||||||
void finish();
|
void finish();
|
||||||
|
|
||||||
@ -20,6 +21,9 @@ namespace hex {
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
const std::string& getName() const;
|
const std::string& getName() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool isPending() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
u64 m_maxValue, m_currValue;
|
u64 m_maxValue, m_currValue;
|
||||||
|
@ -63,7 +63,8 @@ namespace hex::prv {
|
|||||||
[[nodiscard]] virtual bool open() = 0;
|
[[nodiscard]] virtual bool open() = 0;
|
||||||
virtual void close() = 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 undo();
|
||||||
void redo();
|
void redo();
|
||||||
@ -81,7 +82,7 @@ namespace hex::prv {
|
|||||||
u64 m_baseAddress = 0;
|
u64 m_baseAddress = 0;
|
||||||
|
|
||||||
u32 m_patchTreeOffset = 0;
|
u32 m_patchTreeOffset = 0;
|
||||||
std::vector<std::map<u64, u8>> m_patches;
|
std::list<std::map<u64, u8>> m_patches;
|
||||||
std::list<Overlay*> m_overlays;
|
std::list<Overlay*> m_overlays;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,15 +16,26 @@ namespace hex {
|
|||||||
SharedData::runningTasks.remove(this);
|
SharedData::runningTasks.remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Task::setMaxValue(u64 maxValue) {
|
||||||
|
this->m_maxValue = maxValue;
|
||||||
|
}
|
||||||
|
|
||||||
void Task::update(u64 currValue) {
|
void Task::update(u64 currValue) {
|
||||||
if (this->m_currValue < this->m_maxValue)
|
if (this->m_currValue < this->m_maxValue)
|
||||||
this->m_currValue = currValue;
|
this->m_currValue = currValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
double Task::getProgress() const {
|
double Task::getProgress() const {
|
||||||
|
if (this->m_maxValue == 0)
|
||||||
|
return 100;
|
||||||
|
|
||||||
return static_cast<double>(this->m_currValue) / static_cast<double>(this->m_maxValue);
|
return static_cast<double>(this->m_currValue) / static_cast<double>(this->m_maxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Task::isPending() const {
|
||||||
|
return this->m_maxValue == 0;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& Task::getName() const {
|
const std::string& Task::getName() const {
|
||||||
return this->m_name;
|
return this->m_name;
|
||||||
}
|
}
|
||||||
|
@ -50,11 +50,19 @@ namespace hex::prv {
|
|||||||
|
|
||||||
|
|
||||||
std::map<u64, u8>& Provider::getPatches() {
|
std::map<u64, u8>& 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<u64, u8>& Provider::getPatches() const {
|
const std::map<u64, u8>& 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() {
|
void Provider::applyPatches() {
|
||||||
@ -117,18 +125,27 @@ namespace hex::prv {
|
|||||||
return page;
|
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) {
|
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_patchTreeOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->m_patches.push_back(getPatches());
|
if (createUndo)
|
||||||
|
createUndoPoint();
|
||||||
|
|
||||||
for (u64 i = 0; i < size; i++)
|
for (u64 i = 0; i < size; i++)
|
||||||
getPatches()[offset + i] = reinterpret_cast<const u8*>(buffer)[i];
|
getPatches()[offset + i] = reinterpret_cast<const u8*>(buffer)[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Provider::createUndoPoint() {
|
||||||
|
this->m_patches.push_back(getPatches());
|
||||||
|
}
|
||||||
|
|
||||||
void Provider::undo() {
|
void Provider::undo() {
|
||||||
if (canUndo())
|
if (canUndo())
|
||||||
this->m_patchTreeOffset++;
|
this->m_patchTreeOffset++;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user