fix: Processing and drawing of diffs

This commit is contained in:
WerWolv 2024-02-11 19:29:02 +01:00
parent 5cfcca0bc4
commit 0aae605ac4
5 changed files with 66 additions and 35 deletions

View File

@ -397,6 +397,8 @@ namespace hex::plugin::builtin {
provider->close();
if (!provider->open())
ImHexApi::Provider::remove(provider, true);
EventDataChanged::post(provider);
}, noRunningTaskAndValidProvider);

View File

@ -187,7 +187,7 @@ namespace hex::plugin::builtin {
if (progress < 0)
progressString = "";
else
progressString = hex::format("[ {}/{} ({:.1f}%) ] ", frontTask->getValue(), frontTask->getMaxValue(), progress * 100.0F);
progressString = hex::format("[ {}/{} ({:.1f}%) ] ", frontTask->getValue(), frontTask->getMaxValue(), std::min(progress, 1.0F) * 100.0F);
ImGuiExt::InfoTooltip(hex::format("{}{}", progressString, Lang(frontTask->getUnlocalizedName())).c_str());

View File

@ -24,6 +24,7 @@ namespace hex::plugin::diffing {
struct Column {
ui::HexEditor hexEditor;
ContentRegistry::Diffing::DiffTree diffTree;
std::vector<ContentRegistry::Diffing::DiffTree::Data> differences;
int provider = -1;
i32 scrollLock = 0;
@ -33,6 +34,8 @@ namespace hex::plugin::diffing {
std::function<std::optional<color_t>(u64, const u8*, size_t)> createCompareFunction(size_t otherIndex) const;
void analyze(prv::Provider *providerA, prv::Provider *providerB);
void reset();
private:
std::array<Column, 2> m_columns;

View File

@ -88,31 +88,45 @@ namespace hex::plugin::diffing {
edlibConfig.mode = EdlibAlignMode::EDLIB_MODE_NW;
edlibConfig.task = EdlibAlignTask::EDLIB_TASK_PATH;
const auto windowStart = std::min(providerA->getBaseAddress(), providerB->getBaseAddress());
const auto windowEnd = std::max(providerA->getBaseAddress() + providerA->getActualSize(), providerB->getBaseAddress() + providerB->getActualSize());
const auto providerAStart = providerA->getBaseAddress();
const auto providerBStart = providerB->getBaseAddress();
const auto providerAEnd = providerAStart + providerA->getActualSize();
const auto providerBEnd = providerBStart + providerB->getActualSize();
const auto windowStart = std::max(providerAStart, providerBStart);
const auto windowEnd = std::min(providerAEnd, providerBEnd);
auto &task = TaskManager::getCurrentTask();
if (providerAStart > providerBStart) {
differencesA.insert({ providerBStart, providerAStart }, DifferenceType::Deletion);
differencesB.insert({ providerBStart, providerAStart }, DifferenceType::Deletion);
} else if (providerAStart < providerBStart) {
differencesA.insert({ providerAStart, providerBStart }, DifferenceType::Insertion);
differencesB.insert({ providerAStart, providerBStart }, DifferenceType::Insertion);
}
for (u64 address = windowStart; address < windowEnd; address += m_windowSize) {
if (task.wasInterrupted())
break;
auto currWindowSizeA = std::min<u64>(m_windowSize, providerA->getActualSize() - address);
auto currWindowSizeB = std::min<u64>(m_windowSize, providerB->getActualSize() - address);
std::vector<u8> dataA(currWindowSizeA), dataB(currWindowSizeB);
std::vector<u8> dataA(currWindowSizeA, 0x00), dataB(currWindowSizeB, 0x00);
providerA->read(address, dataA.data(), dataA.size());
providerB->read(address, dataB.data(), dataB.size());
const auto commonSize = std::min(dataA.size(), dataB.size());
EdlibAlignResult result = edlibAlign(
reinterpret_cast<const char*>(dataA.data()), dataA.size(),
reinterpret_cast<const char*>(dataB.data()), dataB.size(),
reinterpret_cast<const char*>(dataA.data()), commonSize,
reinterpret_cast<const char*>(dataB.data()), commonSize,
edlibConfig
);
auto currentOperation = DifferenceType(0xFF);
Region regionA = {}, regionB = {};
u64 currentAddressA = 0x00, currentAddressB = 0x00;
u64 currentAddressA = address, currentAddressB = address;
const auto insertDifference = [&] {
switch (currentOperation) {
@ -166,6 +180,13 @@ namespace hex::plugin::diffing {
task.update(address);
}
if (providerAEnd > providerBEnd) {
differencesA.insert({ providerBEnd, providerAEnd }, DifferenceType::Insertion);
differencesB.insert({ providerBEnd, providerAEnd }, DifferenceType::Insertion);
} else if (providerAEnd < providerBEnd) {
differencesA.insert({ providerAEnd, providerBEnd }, DifferenceType::Deletion);
differencesB.insert({ providerAEnd, providerBEnd }, DifferenceType::Deletion);
}
return { differencesA, differencesB };
}

View File

@ -15,12 +15,10 @@ namespace hex::plugin::diffing {
ViewDiff::ViewDiff() : View::Window("hex.diffing.view.diff.name", ICON_VS_DIFF_SIDEBYSIDE) {
// Clear the selected diff providers when a provider is closed
EventProviderClosed::subscribe(this, [this](prv::Provider *) {
for (auto &column : m_columns) {
column.provider = -1;
column.hexEditor.setSelectionUnchecked(std::nullopt, std::nullopt);
column.diffTree.clear();
}
this->reset();
});
EventDataChanged::subscribe(this, [this](prv::Provider *) {
m_analyzed = false;
});
// Set the background highlight callbacks for the two hex editor columns
@ -30,6 +28,7 @@ namespace hex::plugin::diffing {
ViewDiff::~ViewDiff() {
EventProviderClosed::unsubscribe(this);
EventDataChanged::unsubscribe(this);
}
namespace {
@ -93,19 +92,37 @@ namespace hex::plugin::diffing {
}
void ViewDiff::analyze(prv::Provider *providerA, prv::Provider *providerB) {
auto commonSize = std::min(providerA->getActualSize(), providerB->getActualSize());
auto commonSize = std::max(providerA->getActualSize(), providerB->getActualSize());
m_diffTask = TaskManager::createTask("Diffing...", commonSize, [this, providerA, providerB](Task &) {
auto differences = m_algorithm->analyze(providerA, providerB);
// Move the calculated differences over so they can be displayed
for (size_t i = 0; i < m_columns.size(); i++) {
auto &column = m_columns[i];
auto &provider = ImHexApi::Provider::getProviders()[column.provider];
column.differences = differences[i].overlapping({ provider->getBaseAddress(), provider->getBaseAddress() + provider->getActualSize() });
std::ranges::sort(
column.differences,
std::less(),
[](const auto &a) { return a.interval; }
);
column.diffTree = std::move(differences[i]);
}
m_analyzed = true;
});
}
void ViewDiff::reset() {
for (auto &column : m_columns) {
column.provider = -1;
column.hexEditor.setSelectionUnchecked(std::nullopt, std::nullopt);
column.diffTree.clear();
}
}
std::function<std::optional<color_t>(u64, const u8*, size_t)> ViewDiff::createCompareFunction(size_t otherIndex) const {
const auto currIndex = otherIndex == 0 ? 1 : 0;
return [=, this](u64 address, const u8 *, size_t size) -> std::optional<color_t> {
@ -265,45 +282,33 @@ namespace hex::plugin::diffing {
if (m_analyzed) {
ImGuiListClipper clipper;
auto &diffTreeA = m_columns[0].diffTree;
auto &diffTreeB = m_columns[1].diffTree;
clipper.Begin(int(diffTreeA.size()));
auto &differencesA = m_columns[0].differences;
auto &differencesB = m_columns[1].differences;
clipper.Begin(int(std::min(differencesA.size(), differencesB.size())));
auto diffIterA = diffTreeA.begin();
auto diffIterB = diffTreeB.begin();
while (clipper.Step()) {
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
ImGui::TableNextRow();
// Prevent the list from trying to access non-existing diffs
if (size_t(i) >= diffTreeA.size())
break;
ImGui::PushID(i);
const auto &[startA, restA] = *diffIterA;
const auto &[endA, typeA] = restA;
const auto &[startB, restB] = *diffIterB;
const auto &[endB, typeB] = restB;
std::advance(diffIterA, 1);
std::advance(diffIterB, 1);
const auto &[regionA, typeA] = differencesA[i];
const auto &[regionB, typeB] = differencesB[i];
// Draw a clickable row for each difference that will select the difference in both hex editors
// Draw start address
ImGui::TableNextColumn();
if (ImGui::Selectable(hex::format("0x{:02X}", startA).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
a.hexEditor.setSelection({ startA, ((endA - startA) + 1) });
if (ImGui::Selectable(hex::format("0x{:02X}", regionA.start).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
a.hexEditor.setSelection({ regionA.start, ((regionA.end - regionA.start) + 1) });
a.hexEditor.jumpToSelection();
b.hexEditor.setSelection({ startB, ((endB - startB) + 1) });
b.hexEditor.setSelection({ regionB.start, ((regionB.end - regionB.start) + 1) });
b.hexEditor.jumpToSelection();
}
// Draw end address
ImGui::TableNextColumn();
ImGui::TextUnformatted(hex::format("0x{:02X}", endA).c_str());
ImGui::TextUnformatted(hex::format("0x{:02X}", regionA.start).c_str());
// Draw difference type
ImGui::TableNextColumn();