#include "content/views/view_patches.hpp" #include #include #include #include using namespace std::literals::string_literals; namespace hex::plugin::builtin { ViewPatches::ViewPatches() : View("hex.builtin.view.patches.name") { ProjectFile::registerPerProviderHandler({ .basePath = "patches.json", .required = false, .load = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) { auto json = nlohmann::json::parse(tar.readString(basePath)); provider->getPatches() = json.at("patches").get>(); return true; }, .store = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) { nlohmann::json json; json["patches"] = provider->getPatches(); tar.writeString(basePath, json.dump(4)); return true; } }); ImHexApi::HexEditor::addForegroundHighlightingProvider([](u64 offset, const u8* buffer, size_t, bool) -> std::optional { hex::unused(buffer); if (!ImHexApi::Provider::isValid()) return std::nullopt; auto provider = ImHexApi::Provider::get(); u8 byte = 0x00; provider->read(offset, &byte, sizeof(u8), false); const auto &patches = provider->getPatches(); if (patches.contains(offset) && patches.at(offset) != byte) return ImGuiExt::GetCustomColorU32(ImGuiCustomCol_Patches); else return std::nullopt; }); EventManager::subscribe([](auto *) { EventManager::post(); }); } void ViewPatches::drawContent() { if (ImGui::Begin(View::toWindowName("hex.builtin.view.patches.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { auto provider = ImHexApi::Provider::get(); if (ImHexApi::Provider::isValid() && provider->isReadable()) { if (ImGui::BeginTable("##patchesTable", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) { ImGui::TableSetupScrollFreeze(0, 1); ImGui::TableSetupColumn("hex.builtin.view.patches.offset"_lang); ImGui::TableSetupColumn("hex.builtin.view.patches.orig"_lang); ImGui::TableSetupColumn("hex.builtin.view.patches.patch"_lang); ImGui::TableHeadersRow(); auto &patches = provider->getPatches(); u32 index = 0; ImGuiListClipper clipper; clipper.Begin(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)) { ImHexApi::HexEditor::setSelection(address, 1); } if (ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) { ImGui::OpenPopup("PatchContextMenu"); this->m_selectedPatch = address; } ImGui::SameLine(); ImGuiExt::TextFormatted("0x{0:08X}", address); ImGui::TableNextColumn(); u8 previousValue = 0x00; provider->readRaw(address, &previousValue, sizeof(u8)); ImGuiExt::TextFormatted("0x{0:02X}", previousValue); ImGui::TableNextColumn(); ImGuiExt::TextFormatted("0x{0:02X}", patch); index += 1; iter++; } } if (ImGui::BeginPopup("PatchContextMenu")) { if (ImGui::MenuItem("hex.builtin.view.patches.remove"_lang)) { patches.erase(this->m_selectedPatch); } ImGui::EndPopup(); } ImGui::EndTable(); } } } ImGui::End(); } void ViewPatches::drawAlwaysVisible() { if (auto provider = ImHexApi::Provider::get(); provider != nullptr) { const auto &patches = provider->getPatches(); if (this->m_numPatches.get(provider) != patches.size()) { this->m_numPatches.get(provider) = patches.size(); EventManager::post(); } } } }