1
0
mirror of synced 2024-11-27 00:50:51 +01:00
ImHex/plugins/builtin/source/content/views/view_diff.cpp

281 lines
11 KiB
C++
Raw Normal View History

2021-12-07 22:47:41 +01:00
#include "content/views/view_diff.hpp"
2021-09-21 02:48:41 +02:00
2021-09-21 19:54:13 +02:00
#include <hex/api/imhex_api.hpp>
2021-09-21 02:48:41 +02:00
2021-09-21 19:54:13 +02:00
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/logger.hpp>
2021-09-21 19:54:13 +02:00
2021-12-07 22:47:41 +01:00
namespace hex::plugin::builtin {
2021-09-21 02:48:41 +02:00
namespace {
u32 getDiffColor(u32 color) {
return (color & 0x00FFFFFF) | 0x40000000;
}
}
2021-12-07 22:47:41 +01:00
ViewDiff::ViewDiff() : View("hex.builtin.view.diff.name") {
2021-09-21 19:54:13 +02:00
EventManager::subscribe<EventProviderClosed>(this, [this](prv::Provider *) {
for (u8 i = 0; i < 2; i++) {
this->m_columns[i].provider = -1;
this->m_columns[i].hexEditor.setSelectionUnchecked(std::nullopt, std::nullopt);
}
this->m_diffs.clear();
});
2021-09-21 19:54:13 +02:00
auto compareFunction = [this](int otherIndex) {
return [this, otherIndex](u64 address, const u8 *data, size_t) -> std::optional<color_t> {
const auto &providers = ImHexApi::Provider::getProviders();
auto otherId = this->m_columns[otherIndex].provider;
if (otherId < 0 || size_t(otherId) >= providers.size())
return std::nullopt;
2021-09-21 19:54:13 +02:00
auto &otherProvider = providers[otherId];
2021-09-21 19:54:13 +02:00
if (address > otherProvider->getActualSize()) {
if (otherIndex == 1)
return getDiffColor(ImGui::GetCustomColorU32(ImGuiCustomCol_ToolbarGreen));
else
return getDiffColor(ImGui::GetCustomColorU32(ImGuiCustomCol_ToolbarRed));
}
2021-09-21 19:54:13 +02:00
u8 otherByte = 0x00;
otherProvider->read(address, &otherByte, 1);
2021-09-21 19:54:13 +02:00
if (otherByte != *data)
return getDiffColor(ImGui::GetCustomColorU32(ImGuiCustomCol_ToolbarYellow));
return std::nullopt;
};
};
this->m_columns[0].hexEditor.setBackgroundHighlightCallback(compareFunction(1));
this->m_columns[1].hexEditor.setBackgroundHighlightCallback(compareFunction(0));
2021-09-21 19:54:13 +02:00
}
ViewDiff::~ViewDiff() {
2023-02-16 08:53:05 +01:00
EventManager::unsubscribe<EventProviderClosed>(this);
2021-09-21 19:54:13 +02:00
}
2023-02-16 08:53:05 +01:00
namespace {
2023-02-16 08:53:05 +01:00
bool drawDiffColumn(ViewDiff::Column &column, float height) {
bool scrolled = false;
ImGui::PushID(&column);
2023-02-16 08:53:05 +01:00
float prevScroll = column.hexEditor.getScrollPosition();
column.hexEditor.draw(height);
float currScroll = column.hexEditor.getScrollPosition();
2023-02-16 08:53:05 +01:00
if (prevScroll != currScroll) {
scrolled = true;
column.scrollLock = 5;
}
2023-02-16 08:53:05 +01:00
ImGui::PopID();
2023-02-16 08:53:05 +01:00
return scrolled;
}
bool drawProviderSelector(ViewDiff::Column &column) {
bool shouldReanalyze = false;
2023-02-16 08:53:05 +01:00
ImGui::PushID(&column);
2021-09-21 19:54:13 +02:00
2023-02-16 08:53:05 +01:00
auto &providers = ImHexApi::Provider::getProviders();
auto &providerIndex = column.provider;
2021-09-21 19:54:13 +02:00
2023-02-16 08:53:05 +01:00
std::string preview;
if (ImHexApi::Provider::isValid() && providerIndex >= 0)
preview = providers[providerIndex]->getName();
2021-09-21 19:54:13 +02:00
2023-02-16 08:53:05 +01:00
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("", preview.c_str())) {
for (size_t i = 0; i < providers.size(); i++) {
ImGui::PushID(i + 1);
2023-02-16 08:53:05 +01:00
if (ImGui::Selectable(providers[i]->getName().c_str())) {
providerIndex = i;
shouldReanalyze = true;
}
ImGui::PopID();
2021-09-21 19:54:13 +02:00
}
2023-02-16 08:53:05 +01:00
ImGui::EndCombo();
2021-09-21 19:54:13 +02:00
}
2023-02-16 08:53:05 +01:00
ImGui::PopID();
2023-02-16 08:53:05 +01:00
return shouldReanalyze;
}
2021-09-21 19:54:13 +02:00
}
void ViewDiff::drawContent() {
if (ImGui::Begin(View::toWindowName("hex.builtin.view.diff.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
2021-09-21 19:54:13 +02:00
auto &[a, b] = this->m_columns;
2021-09-21 19:54:13 +02:00
a.hexEditor.enableSyncScrolling(false);
b.hexEditor.enableSyncScrolling(false);
2021-09-21 19:54:13 +02:00
if (a.scrollLock > 0) a.scrollLock--;
if (b.scrollLock > 0) b.scrollLock--;
2021-09-21 19:54:13 +02:00
{
const auto &providers = ImHexApi::Provider::getProviders();
if (a.provider >= 0 && size_t(a.provider) < providers.size())
a.hexEditor.setProvider(providers[a.provider]);
else
a.hexEditor.setProvider(nullptr);
if (b.provider >= 0 && size_t(b.provider) < providers.size())
b.hexEditor.setProvider(providers[b.provider]);
else
b.hexEditor.setProvider(nullptr);
}
2021-09-21 02:48:41 +02:00
if (!this->m_analyzed && a.provider != -1 && b.provider != -1 && !this->m_diffTask.isRunning()) {
const auto &providers = ImHexApi::Provider::getProviders();
auto providerA = providers[a.provider];
auto providerB = providers[b.provider];
2021-09-21 02:48:41 +02:00
auto commonSize = std::min(providerA->getActualSize(), providerB->getActualSize());
this->m_diffTask = TaskManager::createTask("Diffing...", commonSize, [this, providerA, providerB](Task &task) {
std::vector<Diff> differences;
2021-09-21 19:54:13 +02:00
auto readerA = prv::ProviderReader(providerA);
auto readerB = prv::ProviderReader(providerB);
2021-09-21 19:54:13 +02:00
for (auto itA = readerA.begin(), itB = readerB.begin(); itA < readerA.end() && itB < readerB.end(); itA++, itB++) {
if (task.wasInterrupted())
break;
2021-09-21 19:54:13 +02:00
if (*itA != *itB) {
u64 start = itA.getAddress();
size_t end = 0;
2021-09-21 19:54:13 +02:00
while (itA != readerA.end() && itB != readerB.end() && *itA != *itB) {
itA++;
itB++;
end++;
}
2021-09-21 19:54:13 +02:00
2023-02-16 08:53:05 +01:00
differences.push_back(Diff { Region{ start, end }, ViewDiff::DifferenceType::Modified });
}
2023-02-16 08:53:05 +01:00
task.update(itA.getAddress());
}
2021-09-21 19:54:13 +02:00
if (providerA->getActualSize() != providerB->getActualSize()) {
2023-04-20 13:46:44 +02:00
auto endA = providerA->getActualSize() + 1;
auto endB = providerB->getActualSize() + 1;
2021-09-21 19:54:13 +02:00
if (endA > endB)
differences.push_back(Diff { Region{ endB, endA - endB }, ViewDiff::DifferenceType::Added });
else
differences.push_back(Diff { Region{ endA, endB - endA }, ViewDiff::DifferenceType::Removed });
}
2021-09-21 19:54:13 +02:00
this->m_diffs = std::move(differences);
this->m_analyzed = true;
});
}
2021-09-21 19:54:13 +02:00
const auto height = ImGui::GetContentRegionAvail().y;
2021-09-21 19:54:13 +02:00
if (ImGui::BeginTable("##binary_diff", 2, ImGuiTableFlags_None, ImVec2(0, height - 250_scaled))) {
ImGui::TableSetupColumn("hex.builtin.view.diff.provider_a"_lang);
ImGui::TableSetupColumn("hex.builtin.view.diff.provider_b"_lang);
ImGui::TableHeadersRow();
2021-09-21 19:54:13 +02:00
2023-02-16 08:53:05 +01:00
ImGui::BeginDisabled(this->m_diffTask.isRunning());
ImGui::TableNextColumn();
2023-02-16 08:53:05 +01:00
if (drawProviderSelector(a)) this->m_analyzed = false;
2021-09-21 19:54:13 +02:00
ImGui::TableNextColumn();
2023-02-16 08:53:05 +01:00
if (drawProviderSelector(b)) this->m_analyzed = false;
ImGui::EndDisabled();
2021-09-21 19:54:13 +02:00
ImGui::TableNextRow();
ImGui::TableNextColumn();
bool scrollB = drawDiffColumn(a, height - 250_scaled);
2021-09-21 19:54:13 +02:00
ImGui::TableNextColumn();
bool scrollA = drawDiffColumn(b, height - 250_scaled);
if (scrollA && a.scrollLock == 0) {
a.hexEditor.setScrollPosition(b.hexEditor.getScrollPosition());
a.hexEditor.forceUpdateScrollPosition();
2021-09-21 19:54:13 +02:00
}
if (scrollB && b.scrollLock == 0) {
b.hexEditor.setScrollPosition(a.hexEditor.getScrollPosition());
b.hexEditor.forceUpdateScrollPosition();
}
ImGui::EndTable();
2021-09-21 02:48:41 +02:00
}
2021-09-21 19:54:13 +02:00
if (ImGui::BeginTable("##differences", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Resizable, ImVec2(0, 200_scaled))) {
2021-09-21 19:54:13 +02:00
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.builtin.common.begin"_lang);
ImGui::TableSetupColumn("hex.builtin.common.end"_lang);
ImGui::TableSetupColumn("hex.builtin.common.type"_lang);
ImGui::TableHeadersRow();
2021-09-21 19:54:13 +02:00
if (this->m_analyzed) {
2021-09-21 19:54:13 +02:00
ImGuiListClipper clipper;
2023-02-16 08:53:05 +01:00
clipper.Begin(int(this->m_diffs.size()));
2021-09-21 19:54:13 +02:00
while (clipper.Step())
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
2021-09-21 19:54:13 +02:00
ImGui::TableNextRow();
if (size_t(i) >= this->m_diffs.size())
break;
ImGui::PushID(i);
const auto &diff = this->m_diffs[i];
ImGui::TableNextColumn();
if (ImGui::Selectable(hex::format("0x{:02X}", diff.region.getStartAddress()).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
a.hexEditor.setSelection(diff.region);
a.hexEditor.jumpToSelection();
b.hexEditor.setSelection(diff.region);
b.hexEditor.jumpToSelection();
}
ImGui::TableNextColumn();
ImGui::TextUnformatted(hex::format("0x{:02X}", diff.region.getEndAddress()).c_str());
ImGui::TableNextColumn();
switch (diff.type) {
case DifferenceType::Modified:
2023-02-15 22:22:13 +01:00
ImGui::TextFormattedColored(ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarYellow), "hex.builtin.view.diff.modified"_lang);
break;
case DifferenceType::Added:
2023-02-15 22:22:13 +01:00
ImGui::TextFormattedColored(ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen), "hex.builtin.view.diff.added"_lang);
break;
case DifferenceType::Removed:
2023-02-15 22:22:13 +01:00
ImGui::TextFormattedColored(ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed), "hex.builtin.view.diff.removed"_lang);
break;
}
ImGui::PopID();
2021-09-21 19:54:13 +02:00
}
}
2021-09-21 19:54:13 +02:00
ImGui::EndTable();
}
2021-09-21 02:48:41 +02:00
}
ImGui::End();
}
}