1
0
mirror of synced 2024-11-16 03:53:22 +01:00
ImHex/plugins/builtin/source/content/views/view_diff.cpp

234 lines
8.6 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
#include <hex/providers/provider.hpp>
2021-09-21 19:54:13 +02:00
#include <hex/helpers/fmt.hpp>
#include <hex/api/content_registry.hpp>
#include <nlohmann/json.hpp>
2021-12-07 22:47:41 +01:00
namespace hex::plugin::builtin {
2021-09-21 02:48:41 +02:00
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<EventSettingsChanged>(this, [this] {
2021-09-21 19:54:13 +02:00
{
auto columnCount = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.column_count");
if (columnCount.is_number())
this->m_columnCount = static_cast<int>(columnCount);
2021-09-21 19:54:13 +02:00
}
{
auto greyOutZeros = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.grey_zeros");
if (greyOutZeros.is_number())
this->m_greyedOutZeros = static_cast<int>(greyOutZeros);
2021-09-21 19:54:13 +02:00
}
{
auto upperCaseHex = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.uppercase_hex");
if (upperCaseHex.is_number())
this->m_upperCaseHex = static_cast<int>(upperCaseHex);
2021-09-21 19:54:13 +02:00
}
});
}
ViewDiff::~ViewDiff() {
EventManager::unsubscribe<EventSettingsChanged>(this);
}
static void drawProviderSelector(int &provider) {
auto &providers = ImHexApi::Provider::getProviders();
std::string preview;
if (ImHexApi::Provider::isValid() && provider >= 0)
preview = providers[provider]->getName();
ImGui::SetNextItemWidth(200_scaled);
2021-09-21 19:54:13 +02:00
if (ImGui::BeginCombo("", preview.c_str())) {
for (int i = 0; i < providers.size(); i++) {
if (ImGui::Selectable(providers[i]->getName().c_str())) {
provider = i;
}
}
ImGui::EndCombo();
}
}
static u32 getDiffColor(u32 color) {
return (color & 0x00FFFFFF) | 0x40000000;
}
enum class DiffResult {
Same,
Changed,
Added,
Removed
};
2021-09-21 19:54:13 +02:00
struct LineInfo {
std::vector<u8> bytes;
i64 validBytes = 0;
2021-09-21 19:54:13 +02:00
};
static DiffResult diffBytes(u8 index, const LineInfo &a, const LineInfo &b) {
/* Very simple binary diff. Only detects additions and changes */
if (a.validBytes > index) {
if (b.validBytes <= index)
return DiffResult::Added;
else if (a.bytes[index] != b.bytes[index])
return DiffResult::Changed;
}
return DiffResult::Same;
}
void ViewDiff::drawDiffLine(const std::array<int, 2> &providerIds, u64 row) const {
std::array<LineInfo, 2> lineInfo;
u8 addressDigitCount = 0;
for (u8 i = 0; i < 2; i++) {
int id = providerIds[i];
if (id < 0) continue;
2021-09-21 02:48:41 +02:00
2021-09-21 19:54:13 +02:00
auto &provider = ImHexApi::Provider::getProviders()[id];
2021-09-21 02:48:41 +02:00
2021-09-21 19:54:13 +02:00
// Read one line of each provider
lineInfo[i].bytes.resize(this->m_columnCount);
provider->read(row * this->m_columnCount, lineInfo[i].bytes.data(), lineInfo[i].bytes.size());
lineInfo[i].validBytes = std::min<i64>(this->m_columnCount, provider->getSize() - row * this->m_columnCount);
2021-09-21 19:54:13 +02:00
// Calculate address width
u8 addressDigits = 0;
for (size_t n = provider->getSize() - 1; n > 0; n >>= 4)
addressDigits++;
addressDigitCount = std::max(addressDigits, addressDigitCount);
}
ImDrawList *drawList = ImGui::GetWindowDrawList();
2021-09-21 19:54:13 +02:00
2022-02-01 22:09:44 +01:00
auto glyphWidth = ImGui::CalcTextSize("0").x + 1;
2021-09-21 19:54:13 +02:00
static auto highlightSize = ImGui::CalcTextSize("00");
auto startY = ImGui::GetCursorPosY();
ImGui::TextFormatted(this->m_upperCaseHex ? "{:0{}X}:" : "{:0{}x}:", row * this->m_columnCount, addressDigitCount);
2021-09-21 19:54:13 +02:00
ImGui::SetCursorPosY(startY);
ImGui::TableNextColumn();
2022-02-01 22:09:44 +01:00
const ImColor colorText = ImGui::GetColorU32(ImGuiCol_Text);
2021-09-21 19:54:13 +02:00
const ImColor colorDisabled = this->m_greyedOutZeros ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : static_cast<u32>(colorText);
for (i8 curr = 0; curr < 2; curr++) {
2021-09-21 19:54:13 +02:00
auto other = !curr;
std::optional<ImVec2> lastHighlightEnd;
for (i64 col = 0; col < lineInfo[curr].validBytes; col++) {
2021-09-21 19:54:13 +02:00
auto pos = ImGui::GetCursorScreenPos();
// Diff bytes
std::optional<u32> highlightColor;
switch (diffBytes(col, lineInfo[curr], lineInfo[other])) {
2022-02-01 22:09:44 +01:00
default:
case DiffResult::Same:
/* No highlight */
break;
case DiffResult::Changed:
highlightColor = getDiffColor(ImGui::GetCustomColorU32(ImGuiCustomCol_ToolbarYellow));
break;
case DiffResult::Added:
highlightColor = getDiffColor(ImGui::GetCustomColorU32(ImGuiCustomCol_ToolbarGreen));
break;
case DiffResult::Removed:
highlightColor = getDiffColor(ImGui::GetCustomColorU32(ImGuiCustomCol_ToolbarRed));
break;
2021-09-21 02:48:41 +02:00
}
2021-09-21 19:54:13 +02:00
// Draw byte
u8 byte = lineInfo[curr].bytes[col];
2022-01-15 14:14:53 +01:00
ImGui::TextFormattedColored(byte == 0x00 ? colorDisabled : colorText, this->m_upperCaseHex ? "{:02X}" : "{:02x}", byte);
ImGui::SameLine(0.0F, col % 8 == 7 ? glyphWidth * 1.5F : glyphWidth * 0.75F);
2021-09-21 19:54:13 +02:00
ImGui::SetCursorPosY(startY);
// Draw highlighting
if (highlightColor.has_value()) {
drawList->AddRectFilled(lastHighlightEnd.value_or(pos), pos + highlightSize, highlightColor.value());
lastHighlightEnd = pos + ImVec2((glyphWidth - 1) * 2, 0);
} else {
lastHighlightEnd.reset();
}
2021-09-21 02:48:41 +02:00
}
2021-09-21 19:54:13 +02:00
ImGui::TableNextColumn();
}
}
void ViewDiff::drawContent() {
2021-12-07 22:47:41 +01:00
if (ImGui::Begin(View::toWindowName("hex.builtin.view.diff.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
2021-09-21 19:54:13 +02:00
ImGui::SameLine();
ImGui::PushID(1);
drawProviderSelector(this->m_providerA);
ImGui::PopID();
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::TextUnformatted("<=>");
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::PushID(2);
drawProviderSelector(this->m_providerB);
ImGui::PopID();
ImGui::Separator();
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(20, 0));
2021-09-21 19:54:13 +02:00
if (ImGui::BeginTable("diff", 3, ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_SizingFixedFit)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableNextRow();
2021-09-21 19:54:13 +02:00
ImGui::TableNextColumn();
// Draw header line
{
auto glyphWidth = ImGui::CalcTextSize("0").x + 1;
for (u8 i = 0; i < 2; i++) {
2022-01-15 14:14:53 +01:00
for (u32 col = 0; col < this->m_columnCount; col++) {
ImGui::TextFormatted(this->m_upperCaseHex ? "{:02X}" : "{:02x}", col);
ImGui::SameLine(0.0F, col % 8 == 7 ? glyphWidth * 1.5F : glyphWidth * 0.75F);
2021-09-21 19:54:13 +02:00
}
ImGui::TableNextColumn();
}
}
if (this->m_providerA >= 0 && this->m_providerB >= 0) {
auto &providers = ImHexApi::Provider::getProviders();
ImGuiListClipper clipper;
clipper.Begin(std::max(providers[this->m_providerA]->getSize() / this->m_columnCount, providers[this->m_providerB]->getSize() / this->m_columnCount) + 1, ImGui::GetTextLineHeight());
2021-09-21 19:54:13 +02:00
// Draw diff lines
while (clipper.Step()) {
for (u64 row = clipper.DisplayStart; row < clipper.DisplayEnd; row++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
drawDiffLine({ this->m_providerA, this->m_providerB }, row);
2021-09-21 19:54:13 +02:00
}
}
}
ImGui::EndTable();
}
ImGui::PopStyleVar();
2021-09-21 02:48:41 +02:00
}
ImGui::End();
}
}