objdiff/objdiff-gui/src/views/data_diff.rs
Luke Street fbdaa89cc0
Refactor data diffing & expose WASM API (#256)
* Refactor data diffing & expose WASM API

* Update test snapshots
2025-09-07 18:59:46 -06:00

149 lines
5.4 KiB
Rust

use std::default::Default;
use egui::{Label, Sense, Widget, text::LayoutJob};
use objdiff_core::{
diff::{
DataDiffKind, DataDiffRow,
data::BYTES_PER_ROW,
display::{data_row_context, data_row_hover},
},
obj::Object,
};
use super::diff::{context_menu_items_ui, hover_items_ui};
use crate::views::{appearance::Appearance, write_text};
fn data_row_hover_ui(
ui: &mut egui::Ui,
obj: &Object,
diff_row: &DataDiffRow,
appearance: &Appearance,
) {
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
hover_items_ui(ui, data_row_hover(obj, diff_row), appearance);
});
}
fn data_row_context_menu(
ui: &mut egui::Ui,
obj: &Object,
diff_row: &DataDiffRow,
column: usize,
appearance: &Appearance,
) {
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
context_menu_items_ui(ui, data_row_context(obj, diff_row), column, appearance);
});
}
fn get_color_for_diff_kind(diff_kind: DataDiffKind, appearance: &Appearance) -> egui::Color32 {
match diff_kind {
DataDiffKind::None => appearance.text_color,
DataDiffKind::Replace => appearance.replace_color,
DataDiffKind::Delete => appearance.delete_color,
DataDiffKind::Insert => appearance.insert_color,
}
}
pub(crate) fn data_row_ui(
ui: &mut egui::Ui,
obj: Option<&Object>,
base_address: u64,
row_address: u64,
diff_row: &DataDiffRow,
appearance: &Appearance,
column: usize,
) {
if diff_row.segments.iter().any(|dd| dd.kind != DataDiffKind::None)
|| diff_row.relocations.iter().any(|rd| rd.kind != DataDiffKind::None)
{
ui.painter().rect_filled(ui.available_rect_before_wrap(), 0.0, ui.visuals().faint_bg_color);
}
let mut job = LayoutJob::default();
write_text(
format!("{row_address:08x}: ").as_str(),
appearance.text_color,
&mut job,
appearance.code_font.clone(),
);
// The offset shown on the side of the GUI, shifted by insertions/deletions.
let mut cur_addr = 0usize;
// The offset into the actual bytes of the section on this side, ignoring differences.
let mut cur_addr_actual = base_address + row_address;
for diff in diff_row.segments.iter() {
let base_color = get_color_for_diff_kind(diff.kind, appearance);
if diff.data.is_empty() {
let mut str = " ".repeat(diff.size);
let n1 = cur_addr / 8;
let n2 = (diff.size + cur_addr) / 8;
str.push_str(" ".repeat(n2 - n1).as_str());
write_text(str.as_str(), base_color, &mut job, appearance.code_font.clone());
cur_addr += diff.size;
} else {
for byte in &diff.data {
let mut byte_text = format!("{byte:02x} ");
let mut byte_color = base_color;
if let Some(reloc_diff) = diff_row
.relocations
.iter()
.find(|reloc_diff| reloc_diff.range.contains(&cur_addr_actual))
{
if *byte == 0 {
// Display 00 data bytes with a relocation as ?? instead.
byte_text = "?? ".to_string();
}
if reloc_diff.kind != DataDiffKind::None {
byte_color = get_color_for_diff_kind(reloc_diff.kind, appearance);
}
}
write_text(byte_text.as_str(), byte_color, &mut job, appearance.code_font.clone());
cur_addr += 1;
cur_addr_actual += 1;
if cur_addr.is_multiple_of(8) {
write_text(" ", base_color, &mut job, appearance.code_font.clone());
}
}
}
}
if cur_addr < BYTES_PER_ROW {
let n = BYTES_PER_ROW - cur_addr;
let mut str = " ".to_string();
str.push_str(" ".repeat(n).as_str());
str.push_str(" ".repeat(n / 8).as_str());
write_text(str.as_str(), appearance.text_color, &mut job, appearance.code_font.clone());
}
write_text(" ", appearance.text_color, &mut job, appearance.code_font.clone());
for diff in diff_row.segments.iter() {
let base_color = get_color_for_diff_kind(diff.kind, appearance);
if diff.data.is_empty() {
write_text(
" ".repeat(diff.size).as_str(),
base_color,
&mut job,
appearance.code_font.clone(),
);
} else {
let mut text = String::new();
for byte in &diff.data {
let c = char::from(*byte);
if c.is_ascii() && !c.is_ascii_control() {
text.push(c);
} else {
text.push('.');
}
}
write_text(text.as_str(), base_color, &mut job, appearance.code_font.clone());
}
}
let response = Label::new(job).sense(Sense::click()).ui(ui);
if let Some(obj) = obj {
response.context_menu(|ui| data_row_context_menu(ui, obj, diff_row, column, appearance));
response.on_hover_ui_at_pointer(|ui| data_row_hover_ui(ui, obj, diff_row, appearance));
}
}