mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-15 08:06:25 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b02e32f2b7 | |||
| c7a326b160 | |||
| 100f8f8ac5 | |||
| 2f778932a4 | |||
| 42601b4750 | |||
| 636a8e00c5 | |||
|
|
cd46be7726 | ||
|
|
019493f944 |
2282
Cargo.lock
generated
2282
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
51
Cargo.toml
51
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "objdiff"
|
||||
version = "0.3.0"
|
||||
version = "0.3.4"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
@@ -11,6 +11,7 @@ description = """
|
||||
A local diffing tool for decompilation projects.
|
||||
"""
|
||||
publish = false
|
||||
build = "build.rs"
|
||||
|
||||
[profile.release]
|
||||
lto = "thin"
|
||||
@@ -21,40 +22,40 @@ default = []
|
||||
wgpu = ["eframe/wgpu"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
bytes = "1.3.0"
|
||||
anyhow = "1.0.71"
|
||||
bytes = "1.4.0"
|
||||
cfg-if = "1.0.0"
|
||||
const_format = "0.2.30"
|
||||
cwdemangle = "0.1.4"
|
||||
eframe = { version = "0.20.1", features = ["persistence"] }
|
||||
egui = "0.20.1"
|
||||
egui_extras = "0.20.0"
|
||||
const_format = "0.2.31"
|
||||
cwdemangle = "0.1.5"
|
||||
eframe = { version = "0.22.0", features = ["persistence"] }
|
||||
egui = "0.22.0"
|
||||
egui_extras = "0.22.0"
|
||||
flagset = "0.4.3"
|
||||
log = "0.4.17"
|
||||
memmap2 = "0.5.8"
|
||||
notify = "5.0.0"
|
||||
object = { version = "0.30.2", features = ["read_core", "std", "elf"], default-features = false }
|
||||
png = "0.17.7"
|
||||
log = "0.4.19"
|
||||
memmap2 = "0.7.1"
|
||||
notify = "6.0.1"
|
||||
object = { version = "0.31.1", features = ["read_core", "std", "elf"], default-features = false }
|
||||
png = "0.17.9"
|
||||
ppc750cl = { git = "https://github.com/terorie/ppc750cl", rev = "9ae36eef34aa6d74e00972c7671f547a2acfd0aa" }
|
||||
rabbitizer = "1.5.8"
|
||||
rfd = { version = "0.10.0" } #, default-features = false, features = ['xdg-portal']
|
||||
rabbitizer = "1.7.4"
|
||||
rfd = { version = "0.11.4" } #, default-features = false, features = ['xdg-portal']
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
tempfile = "3.3.0"
|
||||
thiserror = "1.0.38"
|
||||
time = { version = "0.3.17", features = ["formatting", "local-offset"] }
|
||||
toml = "0.5.11"
|
||||
tempfile = "3.6.0"
|
||||
thiserror = "1.0.41"
|
||||
time = { version = "0.3.22", features = ["formatting", "local-offset"] }
|
||||
toml = "0.7.6"
|
||||
twox-hash = "1.6.3"
|
||||
byteorder = "1.4.3"
|
||||
|
||||
# For Linux static binaries, use rustls
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
reqwest = { version = "0.11.14", default-features = false, features = ["blocking", "json", "rustls"] }
|
||||
self_update = { version = "0.34.0", default-features = false, features = ["rustls"] }
|
||||
reqwest = { version = "0.11.18", default-features = false, features = ["blocking", "json", "rustls"] }
|
||||
self_update = { version = "0.37.0", default-features = false, features = ["rustls"] }
|
||||
|
||||
# For all other platforms, use native TLS
|
||||
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
||||
reqwest = "0.11.14"
|
||||
self_update = "0.34.0"
|
||||
reqwest = "0.11.18"
|
||||
self_update = "0.37.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
path-slash = "0.2.1"
|
||||
@@ -76,5 +77,5 @@ console_error_panic_hook = "0.1.7"
|
||||
tracing-wasm = "0.2"
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.68"
|
||||
vergen = { version = "7.5.0", features = ["build", "cargo", "git"], default-features = false }
|
||||
anyhow = "1.0.71"
|
||||
vergen = { version = "8.2.4", features = ["build", "cargo", "git", "gitcl"] }
|
||||
|
||||
4
build.rs
4
build.rs
@@ -1,10 +1,10 @@
|
||||
use anyhow::Result;
|
||||
use vergen::{vergen, Config};
|
||||
use vergen::EmitBuilder;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
winres::WindowsResource::new().set_icon("assets/icon.ico").compile()?;
|
||||
}
|
||||
vergen(Config::default())
|
||||
EmitBuilder::builder().fail_on_error().all_build().all_cargo().all_git().emit()
|
||||
}
|
||||
|
||||
@@ -48,9 +48,11 @@ notice = "warn"
|
||||
# A list of advisory IDs to ignore. Note that ignored advisories will still
|
||||
# output a note when they are encountered.
|
||||
ignore = [
|
||||
# git2 (build dependency)
|
||||
"RUSTSEC-2023-0002",
|
||||
"RUSTSEC-2023-0003",
|
||||
"RUSTSEC-2023-0022",
|
||||
"RUSTSEC-2023-0023",
|
||||
"RUSTSEC-2023-0024",
|
||||
"RUSTSEC-2023-0034",
|
||||
"RUSTSEC-2023-0044",
|
||||
]
|
||||
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
|
||||
# lower than the range specified will be ignored. Note that ignored advisories
|
||||
@@ -86,6 +88,7 @@ allow = [
|
||||
"OFL-1.1",
|
||||
"LicenseRef-UFL-1.0",
|
||||
"OpenSSL",
|
||||
"GPL-3.0",
|
||||
]
|
||||
# List of explictly disallowed licenses
|
||||
# See https://spdx.org/licenses/ for list of possible licenses
|
||||
|
||||
71
src/app.rs
71
src/app.rs
@@ -68,6 +68,21 @@ pub struct ViewConfig {
|
||||
pub code_font: FontId,
|
||||
pub diff_colors: Vec<Color32>,
|
||||
pub reverse_fn_order: bool,
|
||||
pub theme: eframe::Theme,
|
||||
#[serde(skip)]
|
||||
pub text_color: Color32, // GRAY
|
||||
#[serde(skip)]
|
||||
pub emphasized_text_color: Color32, // LIGHT_GRAY
|
||||
#[serde(skip)]
|
||||
pub deemphasized_text_color: Color32, // DARK_GRAY
|
||||
#[serde(skip)]
|
||||
pub highlight_color: Color32, // WHITE
|
||||
#[serde(skip)]
|
||||
pub replace_color: Color32, // LIGHT_BLUE
|
||||
#[serde(skip)]
|
||||
pub insert_color: Color32, // GREEN
|
||||
#[serde(skip)]
|
||||
pub delete_color: Color32, // RED
|
||||
}
|
||||
|
||||
impl Default for ViewConfig {
|
||||
@@ -77,6 +92,14 @@ impl Default for ViewConfig {
|
||||
code_font: FontId { size: 14.0, family: FontFamily::Monospace },
|
||||
diff_colors: DEFAULT_COLOR_ROTATION.to_vec(),
|
||||
reverse_fn_order: false,
|
||||
theme: eframe::Theme::Dark,
|
||||
text_color: Color32::GRAY,
|
||||
emphasized_text_color: Color32::LIGHT_GRAY,
|
||||
deemphasized_text_color: Color32::DARK_GRAY,
|
||||
highlight_color: Color32::WHITE,
|
||||
replace_color: Color32::LIGHT_BLUE,
|
||||
insert_color: Color32::GREEN,
|
||||
delete_color: Color32::from_rgb(200, 40, 41),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,7 +272,7 @@ impl eframe::App for App {
|
||||
let Self { config, view_state, .. } = self;
|
||||
|
||||
{
|
||||
let config = &view_state.view_config;
|
||||
let config = &mut view_state.view_config;
|
||||
let mut style = (*ctx.style()).clone();
|
||||
style.text_styles.insert(TextStyle::Body, FontId {
|
||||
size: (config.ui_font.size * 0.75).floor(),
|
||||
@@ -262,6 +285,28 @@ impl eframe::App for App {
|
||||
family: config.ui_font.family.clone(),
|
||||
});
|
||||
style.text_styles.insert(TextStyle::Monospace, config.code_font.clone());
|
||||
match config.theme {
|
||||
eframe::Theme::Dark => {
|
||||
style.visuals = egui::Visuals::dark();
|
||||
config.text_color = Color32::GRAY;
|
||||
config.emphasized_text_color = Color32::LIGHT_GRAY;
|
||||
config.deemphasized_text_color = Color32::DARK_GRAY;
|
||||
config.highlight_color = Color32::WHITE;
|
||||
config.replace_color = Color32::LIGHT_BLUE;
|
||||
config.insert_color = Color32::GREEN;
|
||||
config.delete_color = Color32::from_rgb(200, 40, 41);
|
||||
}
|
||||
eframe::Theme::Light => {
|
||||
style.visuals = egui::Visuals::light();
|
||||
config.text_color = Color32::GRAY;
|
||||
config.emphasized_text_color = Color32::DARK_GRAY;
|
||||
config.deemphasized_text_color = Color32::LIGHT_GRAY;
|
||||
config.highlight_color = Color32::BLACK;
|
||||
config.replace_color = Color32::DARK_BLUE;
|
||||
config.insert_color = Color32::DARK_GREEN;
|
||||
config.delete_color = Color32::from_rgb(200, 40, 41);
|
||||
}
|
||||
}
|
||||
ctx.set_style(style);
|
||||
}
|
||||
|
||||
@@ -323,6 +368,20 @@ impl eframe::App for App {
|
||||
}
|
||||
|
||||
egui::Window::new("Config").open(&mut view_state.show_config).show(ctx, |ui| {
|
||||
egui::ComboBox::from_label("Theme")
|
||||
.selected_text(format!("{:?}", view_state.view_config.theme))
|
||||
.show_ui(ui, |ui| {
|
||||
ui.selectable_value(
|
||||
&mut view_state.view_config.theme,
|
||||
eframe::Theme::Dark,
|
||||
"Dark",
|
||||
);
|
||||
ui.selectable_value(
|
||||
&mut view_state.view_config.theme,
|
||||
eframe::Theme::Light,
|
||||
"Light",
|
||||
);
|
||||
});
|
||||
ui.label("UI font:");
|
||||
egui::introspection::font_id_ui(ui, &mut view_state.view_config.ui_font);
|
||||
ui.separator();
|
||||
@@ -359,15 +418,15 @@ impl eframe::App for App {
|
||||
{
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Monospace);
|
||||
ui.colored_label(Color32::LIGHT_BLUE, &demangled);
|
||||
ui.colored_label(view_state.view_config.replace_color, &demangled);
|
||||
});
|
||||
if ui.button("Copy").clicked() {
|
||||
ui.output().copied_text = demangled;
|
||||
ui.output_mut(|output| output.copied_text = demangled);
|
||||
}
|
||||
} else {
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Monospace);
|
||||
ui.colored_label(Color32::LIGHT_RED, "[invalid]");
|
||||
ui.colored_label(view_state.view_config.replace_color, "[invalid]");
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -481,7 +540,7 @@ impl eframe::App for App {
|
||||
if let Some(project_dir) = &config.project_dir {
|
||||
match create_watcher(self.modified.clone(), project_dir) {
|
||||
Ok(watcher) => self.watcher = Some(watcher),
|
||||
Err(e) => eprintln!("Failed to create watcher: {e}"),
|
||||
Err(e) => log::error!("Failed to create watcher: {e}"),
|
||||
}
|
||||
config.project_dir_change = false;
|
||||
self.modified.store(true, Ordering::Relaxed);
|
||||
@@ -532,7 +591,7 @@ fn create_watcher(
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => println!("watch error: {e:?}"),
|
||||
Err(e) => log::error!("watch error: {e:?}"),
|
||||
})?;
|
||||
watcher.watch(project_dir, RecursiveMode::Recursive)?;
|
||||
Ok(watcher)
|
||||
|
||||
67
src/diff.rs
67
src/diff.rs
@@ -133,7 +133,6 @@ pub fn diff_code(
|
||||
right_diff.push(ObjInsDiff::default());
|
||||
cur_left = left_iter.next();
|
||||
}
|
||||
LevEditType::Keep => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
@@ -363,20 +362,26 @@ fn compare_ins(
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn find_symbol<'a>(symbols: &'a mut [ObjSymbol], name: &str) -> Option<&'a mut ObjSymbol> {
|
||||
symbols.iter_mut().find(|s| s.name == name)
|
||||
fn find_section_and_symbol(obj: &ObjInfo, name: &str) -> Option<(usize, usize)> {
|
||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||
let symbol_idx = match section.symbols.iter().position(|symbol| symbol.name == name) {
|
||||
Some(symbol_idx) => symbol_idx,
|
||||
None => continue,
|
||||
};
|
||||
return Some((section_idx, symbol_idx));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn diff_objs(left: &mut ObjInfo, right: &mut ObjInfo, _diff_config: &DiffConfig) -> Result<()> {
|
||||
for left_section in &mut left.sections {
|
||||
let Some(right_section) = right.sections.iter_mut().find(|s| s.name == left_section.name) else {
|
||||
continue;
|
||||
};
|
||||
if left_section.kind == ObjSectionKind::Code {
|
||||
for left_symbol in &mut left_section.symbols {
|
||||
if let Some(right_symbol) =
|
||||
find_symbol(&mut right_section.symbols, &left_symbol.name)
|
||||
if let Some((right_section_idx, right_symbol_idx)) =
|
||||
find_section_and_symbol(right, &left_symbol.name)
|
||||
{
|
||||
let right_section = &mut right.sections[right_section_idx];
|
||||
let right_symbol = &mut right_section.symbols[right_symbol_idx];
|
||||
left_symbol.diff_symbol = Some(right_symbol.name.clone());
|
||||
right_symbol.diff_symbol = Some(left_symbol.name.clone());
|
||||
diff_code(
|
||||
@@ -400,22 +405,31 @@ pub fn diff_objs(left: &mut ObjInfo, right: &mut ObjInfo, _diff_config: &DiffCon
|
||||
)?;
|
||||
}
|
||||
}
|
||||
for right_symbol in &mut right_section.symbols {
|
||||
if right_symbol.instructions.is_empty() {
|
||||
no_diff_code(
|
||||
left.architecture,
|
||||
&right_section.data,
|
||||
right_symbol,
|
||||
&right_section.relocations,
|
||||
&left.line_info,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
let Some(right_section) =
|
||||
right.sections.iter_mut().find(|s| s.name == left_section.name)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if left_section.kind == ObjSectionKind::Data {
|
||||
diff_data(left_section, right_section);
|
||||
// diff_data_symbols(left_section, right_section)?;
|
||||
} else if left_section.kind == ObjSectionKind::Bss {
|
||||
diff_bss_symbols(&mut left_section.symbols, &mut right_section.symbols)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
for right_section in right.sections.iter_mut().filter(|s| s.kind == ObjSectionKind::Code) {
|
||||
for right_symbol in &mut right_section.symbols {
|
||||
if right_symbol.instructions.is_empty() {
|
||||
no_diff_code(
|
||||
right.architecture,
|
||||
&right_section.data,
|
||||
right_symbol,
|
||||
&right_section.relocations,
|
||||
&right.line_info,
|
||||
)?;
|
||||
}
|
||||
} else if left_section.kind == ObjSectionKind::Data {
|
||||
diff_data(left_section, right_section);
|
||||
// diff_data_symbols(left_section, right_section)?;
|
||||
} else if left_section.kind == ObjSectionKind::Bss {
|
||||
diff_bss_symbols(&mut left_section.symbols, &mut right_section.symbols)?;
|
||||
}
|
||||
}
|
||||
diff_bss_symbols(&mut left.common, &mut right.common)?;
|
||||
@@ -424,7 +438,7 @@ pub fn diff_objs(left: &mut ObjInfo, right: &mut ObjInfo, _diff_config: &DiffCon
|
||||
|
||||
fn diff_bss_symbols(left_symbols: &mut [ObjSymbol], right_symbols: &mut [ObjSymbol]) -> Result<()> {
|
||||
for left_symbol in left_symbols {
|
||||
if let Some(right_symbol) = find_symbol(right_symbols, &left_symbol.name) {
|
||||
if let Some(right_symbol) = right_symbols.iter_mut().find(|s| s.name == left_symbol.name) {
|
||||
left_symbol.diff_symbol = Some(right_symbol.name.clone());
|
||||
right_symbol.diff_symbol = Some(left_symbol.name.clone());
|
||||
let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
|
||||
@@ -511,13 +525,12 @@ fn diff_data(left: &mut ObjSection, right: &mut ObjSection) {
|
||||
let mut right_diff = Vec::<ObjDataDiff>::new();
|
||||
let mut left_cur = 0usize;
|
||||
let mut right_cur = 0usize;
|
||||
let mut cur_op = LevEditType::Keep;
|
||||
let mut cur_op = LevEditType::Replace;
|
||||
let mut cur_left_data = Vec::<u8>::new();
|
||||
let mut cur_right_data = Vec::<u8>::new();
|
||||
for op in edit_ops {
|
||||
if cur_op != op.op_type || left_cur < op.first_start || right_cur < op.second_start {
|
||||
match cur_op {
|
||||
LevEditType::Keep => {}
|
||||
LevEditType::Replace => {
|
||||
let left_data = take(&mut cur_left_data);
|
||||
let right_data = take(&mut cur_right_data);
|
||||
@@ -603,7 +616,6 @@ fn diff_data(left: &mut ObjSection, right: &mut ObjSection) {
|
||||
cur_left_data.push(left.data[left_cur]);
|
||||
left_cur += 1;
|
||||
}
|
||||
LevEditType::Keep => unreachable!(),
|
||||
}
|
||||
cur_op = op.op_type;
|
||||
}
|
||||
@@ -627,7 +639,6 @@ fn diff_data(left: &mut ObjSection, right: &mut ObjSection) {
|
||||
|
||||
// TODO: merge with above
|
||||
match cur_op {
|
||||
LevEditType::Keep => {}
|
||||
LevEditType::Replace => {
|
||||
let left_data = take(&mut cur_left_data);
|
||||
let right_data = take(&mut cur_right_data);
|
||||
|
||||
117
src/editops.rs
117
src/editops.rs
@@ -27,7 +27,6 @@
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub enum LevEditType {
|
||||
Keep,
|
||||
Replace,
|
||||
Insert,
|
||||
Delete,
|
||||
@@ -77,98 +76,66 @@ where T: PartialEq {
|
||||
cache_matrix[current + 1 + p] = x;
|
||||
}
|
||||
}
|
||||
editops_from_cost_matrix(
|
||||
first_string,
|
||||
second_string,
|
||||
matrix_columns,
|
||||
matrix_rows,
|
||||
prefix_len,
|
||||
cache_matrix,
|
||||
)
|
||||
editops_from_cost_matrix(matrix_columns, matrix_rows, prefix_len, cache_matrix)
|
||||
}
|
||||
|
||||
fn editops_from_cost_matrix<T>(
|
||||
string1: &[T],
|
||||
string2: &[T],
|
||||
fn editops_from_cost_matrix(
|
||||
len1: usize,
|
||||
len2: usize,
|
||||
prefix_len: usize,
|
||||
cache_matrix: Vec<usize>,
|
||||
) -> Vec<LevEditOp>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
) -> Vec<LevEditOp> {
|
||||
let mut ops = Vec::with_capacity(cache_matrix[len1 * len2 - 1]);
|
||||
let mut dir = 0;
|
||||
|
||||
let mut ops: Vec<LevEditOp> = vec![];
|
||||
ops.reserve(cache_matrix[len1 * len2 - 1]);
|
||||
|
||||
let mut i = len1 - 1;
|
||||
let mut j = len2 - 1;
|
||||
let mut p = len1 * len2 - 1;
|
||||
|
||||
// let string1_chars: Vec<char> = string1.chars().collect();
|
||||
// let string2_chars: Vec<char> = string2.chars().collect();
|
||||
|
||||
//TODO this is still pretty ugly
|
||||
while i > 0 || j > 0 {
|
||||
let current_value = cache_matrix[p];
|
||||
|
||||
let op_type;
|
||||
// More than one operation can be possible at a time. We use `dir` to
|
||||
// decide when ambiguous.
|
||||
let is_insert = j > 0 && current_value == cache_matrix[p - 1] + 1;
|
||||
let is_delete = i > 0 && current_value == cache_matrix[p - len2] + 1;
|
||||
let is_replace = i > 0 && j > 0 && current_value == cache_matrix[p - len2 - 1] + 1;
|
||||
|
||||
if dir == -1 && j > 0 && current_value == cache_matrix[p - 1] + 1 {
|
||||
op_type = LevEditType::Insert;
|
||||
} else if dir == 1 && i > 0 && current_value == cache_matrix[p - len2] + 1 {
|
||||
op_type = LevEditType::Delete;
|
||||
} else if i > 0
|
||||
&& j > 0
|
||||
&& current_value == cache_matrix[p - len2 - 1]
|
||||
&& string1[i - 1] == string2[j - 1]
|
||||
{
|
||||
op_type = LevEditType::Keep;
|
||||
} else if i > 0 && j > 0 && current_value == cache_matrix[p - len2 - 1] + 1 {
|
||||
op_type = LevEditType::Replace;
|
||||
}
|
||||
/* we can't turn directly from -1 to 1, in this case it would be better
|
||||
* to go diagonally, but check it (dir == 0) */
|
||||
else if dir == 0 && j > 0 && current_value == cache_matrix[p - 1] + 1 {
|
||||
op_type = LevEditType::Insert;
|
||||
} else if dir == 0 && i > 0 && current_value == cache_matrix[p - len2] + 1 {
|
||||
op_type = LevEditType::Delete;
|
||||
} else {
|
||||
panic!("something went terribly wrong");
|
||||
}
|
||||
|
||||
match op_type {
|
||||
LevEditType::Insert => {
|
||||
j -= 1;
|
||||
p -= 1;
|
||||
dir = -1;
|
||||
}
|
||||
LevEditType::Delete => {
|
||||
i -= 1;
|
||||
p -= len2;
|
||||
dir = 1;
|
||||
}
|
||||
LevEditType::Replace => {
|
||||
i -= 1;
|
||||
j -= 1;
|
||||
p -= len2 + 1;
|
||||
dir = 0;
|
||||
}
|
||||
LevEditType::Keep => {
|
||||
i -= 1;
|
||||
j -= 1;
|
||||
p -= len2 + 1;
|
||||
dir = 0;
|
||||
/* LevEditKeep does not has to be stored */
|
||||
continue;
|
||||
}
|
||||
let (op_type, new_dir) = match (dir, is_insert, is_delete, is_replace) {
|
||||
(_, false, false, false) => (None, 0),
|
||||
(-1, true, _, _) => (Some(LevEditType::Insert), -1),
|
||||
(1, _, true, _) => (Some(LevEditType::Delete), 1),
|
||||
(_, _, _, true) => (Some(LevEditType::Replace), 0),
|
||||
(0, true, _, _) => (Some(LevEditType::Insert), -1),
|
||||
(0, _, true, _) => (Some(LevEditType::Delete), 1),
|
||||
_ => panic!("something went terribly wrong"),
|
||||
};
|
||||
|
||||
let edit_op =
|
||||
LevEditOp { op_type, first_start: i + prefix_len, second_start: j + prefix_len };
|
||||
ops.insert(0, edit_op);
|
||||
match new_dir {
|
||||
-1 => {
|
||||
j -= 1;
|
||||
p -= 1;
|
||||
}
|
||||
1 => {
|
||||
i -= 1;
|
||||
p -= len2;
|
||||
}
|
||||
0 => {
|
||||
i -= 1;
|
||||
j -= 1;
|
||||
p -= len2 + 1;
|
||||
}
|
||||
_ => panic!("something went terribly wrong"),
|
||||
};
|
||||
dir = new_dir;
|
||||
|
||||
if let Some(op_type) = op_type {
|
||||
ops.insert(0, LevEditOp {
|
||||
op_type,
|
||||
first_start: i + prefix_len,
|
||||
second_start: j + prefix_len,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ops
|
||||
|
||||
10
src/main.rs
10
src/main.rs
@@ -37,7 +37,8 @@ fn main() {
|
||||
|
||||
let exec_path: Rc<Mutex<Option<PathBuf>>> = Rc::new(Mutex::new(None));
|
||||
let exec_path_clone = exec_path.clone();
|
||||
let mut native_options = eframe::NativeOptions::default();
|
||||
let mut native_options =
|
||||
eframe::NativeOptions { follow_system_theme: false, ..Default::default() };
|
||||
match load_icon() {
|
||||
Ok(data) => {
|
||||
native_options.icon_data = Some(data);
|
||||
@@ -54,7 +55,8 @@ fn main() {
|
||||
"objdiff",
|
||||
native_options,
|
||||
Box::new(move |cc| Box::new(objdiff::App::new(cc, utc_offset, exec_path_clone))),
|
||||
);
|
||||
)
|
||||
.expect("Failed to run eframe application");
|
||||
|
||||
// Attempt to relaunch application from the updated path
|
||||
if let Ok(mut guard) = exec_path.lock() {
|
||||
@@ -64,7 +66,7 @@ fn main() {
|
||||
let result = exec::Command::new(path)
|
||||
.args(&std::env::args().collect::<Vec<String>>())
|
||||
.exec();
|
||||
eprintln!("Failed to relaunch: {result:?}");
|
||||
log::error!("Failed to relaunch: {result:?}");
|
||||
} else {
|
||||
let result = std::process::Command::new(path)
|
||||
.args(std::env::args())
|
||||
@@ -72,7 +74,7 @@ fn main() {
|
||||
.unwrap()
|
||||
.wait();
|
||||
if let Err(e) = result {
|
||||
eprintln!("Failed to relaunch: {:?}", e);
|
||||
log::error!("Failed to relaunch: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ fn to_obj_section_kind(kind: SectionKind) -> Option<ObjSectionKind> {
|
||||
fn to_obj_symbol(obj_file: &File<'_>, symbol: &Symbol<'_, '_>, addend: i64) -> Result<ObjSymbol> {
|
||||
let mut name = symbol.name().context("Failed to process symbol name")?;
|
||||
if name.is_empty() {
|
||||
println!("Found empty sym: {symbol:?}");
|
||||
log::warn!("Found empty sym: {symbol:?}");
|
||||
name = "?";
|
||||
}
|
||||
let mut flags = ObjSymbolFlagSet(ObjSymbolFlags::none());
|
||||
@@ -126,13 +126,11 @@ fn symbols_by_section(obj_file: &File<'_>, section: &ObjSection) -> Result<Vec<O
|
||||
}
|
||||
|
||||
fn common_symbols(obj_file: &File<'_>) -> Result<Vec<ObjSymbol>> {
|
||||
let mut result = Vec::<ObjSymbol>::new();
|
||||
for symbol in obj_file.symbols() {
|
||||
if symbol.is_common() {
|
||||
result.push(to_obj_symbol(obj_file, &symbol, 0)?);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
obj_file
|
||||
.symbols()
|
||||
.filter(Symbol::is_common)
|
||||
.map(|symbol| to_obj_symbol(obj_file, &symbol, 0))
|
||||
.collect::<Result<Vec<ObjSymbol>>>()
|
||||
}
|
||||
|
||||
fn find_section_symbol(
|
||||
@@ -296,7 +294,7 @@ fn line_info(obj_file: &File<'_>) -> Result<Option<BTreeMap<u32, u32>>> {
|
||||
let address_delta = reader.read_u32::<BigEndian>()?;
|
||||
map.insert(base_address + address_delta, line_number);
|
||||
}
|
||||
println!("Line info: {map:#X?}");
|
||||
log::debug!("Line info: {map:#X?}");
|
||||
return Ok(Some(map));
|
||||
}
|
||||
Ok(None)
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::sync::{Arc, RwLock};
|
||||
#[cfg(windows)]
|
||||
use anyhow::{Context, Result};
|
||||
use const_format::formatcp;
|
||||
use egui::{output::OpenUrl, Color32};
|
||||
use egui::output::OpenUrl;
|
||||
use self_update::cargo_crate_version;
|
||||
|
||||
use crate::{
|
||||
@@ -74,12 +74,12 @@ pub fn config_ui(ui: &mut egui::Ui, config: &Arc<RwLock<AppConfig>>, view_state:
|
||||
ui.label(formatcp!("Git branch: {}", env!("VERGEN_GIT_BRANCH")));
|
||||
ui.label(formatcp!("Git commit: {}", env!("VERGEN_GIT_SHA")));
|
||||
ui.label(formatcp!("Build target: {}", env!("VERGEN_CARGO_TARGET_TRIPLE")));
|
||||
ui.label(formatcp!("Build type: {}", env!("VERGEN_CARGO_PROFILE")));
|
||||
ui.label(formatcp!("Debug: {}", env!("VERGEN_CARGO_DEBUG")));
|
||||
});
|
||||
if let Some(state) = &view_state.check_update {
|
||||
ui.label(format!("Latest version: {}", state.latest_release.version));
|
||||
if state.update_available {
|
||||
ui.colored_label(Color32::LIGHT_GREEN, "Update available");
|
||||
ui.colored_label(view_state.view_config.insert_color, "Update available");
|
||||
ui.horizontal(|ui| {
|
||||
if state.found_binary
|
||||
&& ui
|
||||
@@ -96,8 +96,10 @@ pub fn config_ui(ui: &mut egui::Ui, config: &Arc<RwLock<AppConfig>>, view_state:
|
||||
.on_hover_text_at_pointer("Open a link to the latest release on GitHub")
|
||||
.clicked()
|
||||
{
|
||||
ui.output().open_url =
|
||||
Some(OpenUrl { url: RELEASE_URL.to_string(), new_tab: true });
|
||||
ui.output_mut(|output| {
|
||||
output.open_url =
|
||||
Some(OpenUrl { url: RELEASE_URL.to_string(), new_tab: true })
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{cmp::min, default::Default, mem::take};
|
||||
|
||||
use egui::{text::LayoutJob, Align, Color32, Label, Layout, Sense, Vec2};
|
||||
use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2};
|
||||
use egui_extras::{Column, TableBuilder};
|
||||
use time::format_description;
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
app::{SymbolReference, View, ViewConfig, ViewState},
|
||||
jobs::Job,
|
||||
obj::{ObjDataDiff, ObjDataDiffKind, ObjInfo, ObjSection},
|
||||
views::{write_text, COLOR_RED},
|
||||
views::write_text,
|
||||
};
|
||||
|
||||
const BYTES_PER_ROW: usize = 16;
|
||||
@@ -24,17 +24,17 @@ fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], config:
|
||||
let mut job = LayoutJob::default();
|
||||
write_text(
|
||||
format!("{address:08X}: ").as_str(),
|
||||
Color32::GRAY,
|
||||
config.text_color,
|
||||
&mut job,
|
||||
config.code_font.clone(),
|
||||
);
|
||||
let mut cur_addr = 0usize;
|
||||
for diff in diffs {
|
||||
let base_color = match diff.kind {
|
||||
ObjDataDiffKind::None => Color32::GRAY,
|
||||
ObjDataDiffKind::Replace => Color32::LIGHT_BLUE,
|
||||
ObjDataDiffKind::Delete => COLOR_RED,
|
||||
ObjDataDiffKind::Insert => Color32::GREEN,
|
||||
ObjDataDiffKind::None => config.text_color,
|
||||
ObjDataDiffKind::Replace => config.replace_color,
|
||||
ObjDataDiffKind::Delete => config.delete_color,
|
||||
ObjDataDiffKind::Insert => config.insert_color,
|
||||
};
|
||||
if diff.data.is_empty() {
|
||||
let mut str = " ".repeat(diff.len);
|
||||
@@ -58,15 +58,15 @@ fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], config:
|
||||
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(), Color32::GRAY, &mut job, config.code_font.clone());
|
||||
write_text(str.as_str(), config.text_color, &mut job, config.code_font.clone());
|
||||
}
|
||||
write_text(" ", Color32::GRAY, &mut job, config.code_font.clone());
|
||||
write_text(" ", config.text_color, &mut job, config.code_font.clone());
|
||||
for diff in diffs {
|
||||
let base_color = match diff.kind {
|
||||
ObjDataDiffKind::None => Color32::GRAY,
|
||||
ObjDataDiffKind::Replace => Color32::LIGHT_BLUE,
|
||||
ObjDataDiffKind::Delete => COLOR_RED,
|
||||
ObjDataDiffKind::Insert => Color32::GREEN,
|
||||
ObjDataDiffKind::None => config.text_color,
|
||||
ObjDataDiffKind::Replace => config.replace_color,
|
||||
ObjDataDiffKind::Delete => config.delete_color,
|
||||
ObjDataDiffKind::Insert => config.insert_color,
|
||||
};
|
||||
if diff.data.is_empty() {
|
||||
write_text(
|
||||
@@ -163,7 +163,8 @@ fn data_table_ui(
|
||||
|
||||
pub fn data_diff_ui(ui: &mut egui::Ui, view_state: &mut ViewState) -> bool {
|
||||
let mut rebuild = false;
|
||||
let (Some(result), Some(selected_symbol)) = (&view_state.build, &view_state.selected_symbol) else {
|
||||
let (Some(result), Some(selected_symbol)) = (&view_state.build, &view_state.selected_symbol)
|
||||
else {
|
||||
return rebuild;
|
||||
};
|
||||
|
||||
@@ -188,7 +189,10 @@ pub fn data_diff_ui(ui: &mut egui::Ui, view_state: &mut ViewState) -> bool {
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap = Some(false);
|
||||
ui.colored_label(Color32::WHITE, &selected_symbol.symbol_name);
|
||||
ui.colored_label(
|
||||
view_state.view_config.highlight_color,
|
||||
&selected_symbol.symbol_name,
|
||||
);
|
||||
ui.label("Diff target:");
|
||||
});
|
||||
},
|
||||
|
||||
@@ -14,12 +14,18 @@ use crate::{
|
||||
ObjInfo, ObjIns, ObjInsArg, ObjInsArgDiff, ObjInsDiff, ObjInsDiffKind, ObjReloc,
|
||||
ObjRelocKind, ObjSymbol,
|
||||
},
|
||||
views::{symbol_diff::match_color_for_symbol, write_text, COLOR_RED},
|
||||
views::{symbol_diff::match_color_for_symbol, write_text},
|
||||
};
|
||||
|
||||
fn write_reloc_name(reloc: &ObjReloc, color: Color32, job: &mut LayoutJob, font_id: FontId) {
|
||||
fn write_reloc_name(
|
||||
reloc: &ObjReloc,
|
||||
color: Color32,
|
||||
job: &mut LayoutJob,
|
||||
font_id: FontId,
|
||||
config: &ViewConfig,
|
||||
) {
|
||||
let name = reloc.target.demangled_name.as_ref().unwrap_or(&reloc.target.name);
|
||||
write_text(name, Color32::LIGHT_GRAY, job, font_id.clone());
|
||||
write_text(name, config.emphasized_text_color, job, font_id.clone());
|
||||
match reloc.target.addend.cmp(&0i64) {
|
||||
Ordering::Greater => {
|
||||
write_text(&format!("+{:#X}", reloc.target.addend), color, job, font_id)
|
||||
@@ -31,51 +37,57 @@ fn write_reloc_name(reloc: &ObjReloc, color: Color32, job: &mut LayoutJob, font_
|
||||
}
|
||||
}
|
||||
|
||||
fn write_reloc(reloc: &ObjReloc, color: Color32, job: &mut LayoutJob, font_id: FontId) {
|
||||
fn write_reloc(
|
||||
reloc: &ObjReloc,
|
||||
color: Color32,
|
||||
job: &mut LayoutJob,
|
||||
font_id: FontId,
|
||||
config: &ViewConfig,
|
||||
) {
|
||||
match reloc.kind {
|
||||
ObjRelocKind::PpcAddr16Lo => {
|
||||
write_reloc_name(reloc, color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone(), config);
|
||||
write_text("@l", color, job, font_id);
|
||||
}
|
||||
ObjRelocKind::PpcAddr16Hi => {
|
||||
write_reloc_name(reloc, color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone(), config);
|
||||
write_text("@h", color, job, font_id);
|
||||
}
|
||||
ObjRelocKind::PpcAddr16Ha => {
|
||||
write_reloc_name(reloc, color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone(), config);
|
||||
write_text("@ha", color, job, font_id);
|
||||
}
|
||||
ObjRelocKind::PpcEmbSda21 => {
|
||||
write_reloc_name(reloc, color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone(), config);
|
||||
write_text("@sda21", color, job, font_id);
|
||||
}
|
||||
ObjRelocKind::MipsHi16 => {
|
||||
write_text("%hi(", color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone(), config);
|
||||
write_text(")", color, job, font_id);
|
||||
}
|
||||
ObjRelocKind::MipsLo16 => {
|
||||
write_text("%lo(", color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone(), config);
|
||||
write_text(")", color, job, font_id);
|
||||
}
|
||||
ObjRelocKind::MipsGot16 => {
|
||||
write_text("%got(", color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone(), config);
|
||||
write_text(")", color, job, font_id);
|
||||
}
|
||||
ObjRelocKind::MipsCall16 => {
|
||||
write_text("%call16(", color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone(), config);
|
||||
write_text(")", color, job, font_id);
|
||||
}
|
||||
ObjRelocKind::MipsGpRel16 => {
|
||||
write_text("%gp_rel(", color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone());
|
||||
write_reloc_name(reloc, color, job, font_id.clone(), config);
|
||||
write_text(")", color, job, font_id);
|
||||
}
|
||||
ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 | ObjRelocKind::Mips26 => {
|
||||
write_reloc_name(reloc, color, job, font_id);
|
||||
write_reloc_name(reloc, color, job, font_id, config);
|
||||
}
|
||||
ObjRelocKind::Absolute | ObjRelocKind::MipsGpRel32 => {
|
||||
write_text("[INVALID]", color, job, font_id);
|
||||
@@ -93,16 +105,16 @@ fn write_ins(
|
||||
) {
|
||||
let base_color = match diff_kind {
|
||||
ObjInsDiffKind::None | ObjInsDiffKind::OpMismatch | ObjInsDiffKind::ArgMismatch => {
|
||||
Color32::GRAY
|
||||
config.text_color
|
||||
}
|
||||
ObjInsDiffKind::Replace => Color32::LIGHT_BLUE,
|
||||
ObjInsDiffKind::Delete => COLOR_RED,
|
||||
ObjInsDiffKind::Insert => Color32::GREEN,
|
||||
ObjInsDiffKind::Replace => config.replace_color,
|
||||
ObjInsDiffKind::Delete => config.delete_color,
|
||||
ObjInsDiffKind::Insert => config.insert_color,
|
||||
};
|
||||
write_text(
|
||||
&format!("{:<11}", ins.mnemonic),
|
||||
match diff_kind {
|
||||
ObjInsDiffKind::OpMismatch => Color32::LIGHT_BLUE,
|
||||
ObjInsDiffKind::OpMismatch => config.replace_color,
|
||||
_ => base_color,
|
||||
},
|
||||
job,
|
||||
@@ -137,10 +149,22 @@ fn write_ins(
|
||||
}
|
||||
},
|
||||
ObjInsArg::Reloc => {
|
||||
write_reloc(ins.reloc.as_ref().unwrap(), base_color, job, config.code_font.clone());
|
||||
write_reloc(
|
||||
ins.reloc.as_ref().unwrap(),
|
||||
base_color,
|
||||
job,
|
||||
config.code_font.clone(),
|
||||
config,
|
||||
);
|
||||
}
|
||||
ObjInsArg::RelocWithBase => {
|
||||
write_reloc(ins.reloc.as_ref().unwrap(), base_color, job, config.code_font.clone());
|
||||
write_reloc(
|
||||
ins.reloc.as_ref().unwrap(),
|
||||
base_color,
|
||||
job,
|
||||
config.code_font.clone(),
|
||||
config,
|
||||
);
|
||||
write_text("(", base_color, job, config.code_font.clone());
|
||||
writing_offset = true;
|
||||
continue;
|
||||
@@ -176,7 +200,7 @@ fn write_ins(
|
||||
}
|
||||
}
|
||||
|
||||
fn ins_hover_ui(ui: &mut egui::Ui, ins: &ObjIns) {
|
||||
fn ins_hover_ui(ui: &mut egui::Ui, ins: &ObjIns, config: &ViewConfig) {
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap = Some(false);
|
||||
@@ -202,13 +226,16 @@ fn ins_hover_ui(ui: &mut egui::Ui, ins: &ObjIns) {
|
||||
|
||||
if let Some(reloc) = &ins.reloc {
|
||||
ui.label(format!("Relocation type: {:?}", reloc.kind));
|
||||
ui.colored_label(Color32::WHITE, format!("Name: {}", reloc.target.name));
|
||||
ui.colored_label(config.highlight_color, format!("Name: {}", reloc.target.name));
|
||||
if let Some(section) = &reloc.target_section {
|
||||
ui.colored_label(Color32::WHITE, format!("Section: {section}"));
|
||||
ui.colored_label(Color32::WHITE, format!("Address: {:x}", reloc.target.address));
|
||||
ui.colored_label(Color32::WHITE, format!("Size: {:x}", reloc.target.size));
|
||||
ui.colored_label(config.highlight_color, format!("Section: {section}"));
|
||||
ui.colored_label(
|
||||
config.highlight_color,
|
||||
format!("Address: {:x}", reloc.target.address),
|
||||
);
|
||||
ui.colored_label(config.highlight_color, format!("Size: {:x}", reloc.target.size));
|
||||
} else {
|
||||
ui.colored_label(Color32::WHITE, "Extern".to_string());
|
||||
ui.colored_label(config.highlight_color, "Extern".to_string());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -226,31 +253,31 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
|
||||
match arg {
|
||||
Argument::Uimm(v) => {
|
||||
if ui.button(format!("Copy \"{v}\"")).clicked() {
|
||||
ui.output().copied_text = format!("{v}");
|
||||
ui.output_mut(|output| output.copied_text = format!("{v}"));
|
||||
ui.close_menu();
|
||||
}
|
||||
if ui.button(format!("Copy \"{}\"", v.0)).clicked() {
|
||||
ui.output().copied_text = format!("{}", v.0);
|
||||
ui.output_mut(|output| output.copied_text = format!("{}", v.0));
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
Argument::Simm(v) => {
|
||||
if ui.button(format!("Copy \"{v}\"")).clicked() {
|
||||
ui.output().copied_text = format!("{v}");
|
||||
ui.output_mut(|output| output.copied_text = format!("{v}"));
|
||||
ui.close_menu();
|
||||
}
|
||||
if ui.button(format!("Copy \"{}\"", v.0)).clicked() {
|
||||
ui.output().copied_text = format!("{}", v.0);
|
||||
ui.output_mut(|output| output.copied_text = format!("{}", v.0));
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
Argument::Offset(v) => {
|
||||
if ui.button(format!("Copy \"{v}\"")).clicked() {
|
||||
ui.output().copied_text = format!("{v}");
|
||||
ui.output_mut(|output| output.copied_text = format!("{v}"));
|
||||
ui.close_menu();
|
||||
}
|
||||
if ui.button(format!("Copy \"{}\"", v.0)).clicked() {
|
||||
ui.output().copied_text = format!("{}", v.0);
|
||||
ui.output_mut(|output| output.copied_text = format!("{}", v.0));
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
@@ -261,12 +288,12 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
|
||||
if let Some(reloc) = &ins.reloc {
|
||||
if let Some(name) = &reloc.target.demangled_name {
|
||||
if ui.button(format!("Copy \"{name}\"")).clicked() {
|
||||
ui.output().copied_text = name.clone();
|
||||
ui.output_mut(|output| output.copied_text = name.clone());
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
if ui.button(format!("Copy \"{}\"", reloc.target.name)).clicked() {
|
||||
ui.output().copied_text = reloc.target.name.clone();
|
||||
ui.output_mut(|output| output.copied_text = reloc.target.name.clone());
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
@@ -291,16 +318,16 @@ fn asm_row_ui(ui: &mut egui::Ui, ins_diff: &ObjInsDiff, symbol: &ObjSymbol, conf
|
||||
|
||||
let base_color = match ins_diff.kind {
|
||||
ObjInsDiffKind::None | ObjInsDiffKind::OpMismatch | ObjInsDiffKind::ArgMismatch => {
|
||||
Color32::GRAY
|
||||
config.text_color
|
||||
}
|
||||
ObjInsDiffKind::Replace => Color32::LIGHT_BLUE,
|
||||
ObjInsDiffKind::Delete => COLOR_RED,
|
||||
ObjInsDiffKind::Insert => Color32::GREEN,
|
||||
ObjInsDiffKind::Replace => config.replace_color,
|
||||
ObjInsDiffKind::Delete => config.delete_color,
|
||||
ObjInsDiffKind::Insert => config.insert_color,
|
||||
};
|
||||
let mut pad = 6;
|
||||
if let Some(line) = ins.line {
|
||||
let line_str = format!("{line} ");
|
||||
write_text(&line_str, Color32::DARK_GRAY, &mut job, config.code_font.clone());
|
||||
write_text(&line_str, config.deemphasized_text_color, &mut job, config.code_font.clone());
|
||||
pad = 12 - line_str.len();
|
||||
}
|
||||
write_text(
|
||||
@@ -329,7 +356,7 @@ fn asm_row_ui(ui: &mut egui::Ui, ins_diff: &ObjInsDiff, symbol: &ObjSymbol, conf
|
||||
);
|
||||
}
|
||||
ui.add(Label::new(job).sense(Sense::click()))
|
||||
.on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, ins))
|
||||
.on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, ins, config))
|
||||
.context_menu(|ui| ins_context_menu(ui, ins));
|
||||
}
|
||||
|
||||
@@ -362,7 +389,8 @@ fn asm_table_ui(
|
||||
|
||||
pub fn function_diff_ui(ui: &mut egui::Ui, view_state: &mut ViewState) -> bool {
|
||||
let mut rebuild = false;
|
||||
let (Some(result), Some(selected_symbol)) = (&view_state.build, &view_state.selected_symbol) else {
|
||||
let (Some(result), Some(selected_symbol)) = (&view_state.build, &view_state.selected_symbol)
|
||||
else {
|
||||
return rebuild;
|
||||
};
|
||||
|
||||
@@ -389,7 +417,7 @@ pub fn function_diff_ui(ui: &mut egui::Ui, view_state: &mut ViewState) -> bool {
|
||||
let mut job = LayoutJob::simple(
|
||||
name.to_string(),
|
||||
view_state.view_config.code_font.clone(),
|
||||
Color32::WHITE,
|
||||
view_state.view_config.highlight_color,
|
||||
column_width,
|
||||
);
|
||||
job.wrap.break_anywhere = true;
|
||||
@@ -443,7 +471,7 @@ pub fn function_diff_ui(ui: &mut egui::Ui, view_state: &mut ViewState) -> bool {
|
||||
.and_then(|symbol| symbol.match_percent)
|
||||
{
|
||||
ui.colored_label(
|
||||
match_color_for_symbol(match_percent),
|
||||
match_color_for_symbol(match_percent, &view_state.view_config),
|
||||
&format!("{match_percent:.0}%"),
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use egui::{Color32, ProgressBar, Widget};
|
||||
use egui::{ProgressBar, Widget};
|
||||
|
||||
use crate::app::ViewState;
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn jobs_ui(ui: &mut egui::Ui, view_state: &mut ViewState) {
|
||||
if job.handle.is_some() {
|
||||
job.should_remove = true;
|
||||
if let Err(e) = job.cancel.send(()) {
|
||||
eprintln!("Failed to cancel job: {e:?}");
|
||||
log::error!("Failed to cancel job: {e:?}");
|
||||
}
|
||||
} else {
|
||||
remove_job = Some(idx);
|
||||
@@ -33,7 +33,7 @@ pub fn jobs_ui(ui: &mut egui::Ui, view_state: &mut ViewState) {
|
||||
if let Some(err) = &status.error {
|
||||
let err_string = err.to_string();
|
||||
ui.colored_label(
|
||||
Color32::from_rgb(255, 0, 0),
|
||||
view_state.view_config.delete_color,
|
||||
if err_string.len() > STATUS_LENGTH - 10 {
|
||||
format!("Error: {}...", &err_string[0..STATUS_LENGTH - 10])
|
||||
} else {
|
||||
|
||||
@@ -6,8 +6,6 @@ pub(crate) mod function_diff;
|
||||
pub(crate) mod jobs;
|
||||
pub(crate) mod symbol_diff;
|
||||
|
||||
const COLOR_RED: Color32 = Color32::from_rgb(200, 40, 41);
|
||||
|
||||
fn write_text(str: &str, color: Color32, job: &mut LayoutJob, font_id: FontId) {
|
||||
job.append(str, 0.0, TextFormat::simple(font_id, color));
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ use crate::{
|
||||
views::write_text,
|
||||
};
|
||||
|
||||
pub fn match_color_for_symbol(match_percent: f32) -> Color32 {
|
||||
pub fn match_color_for_symbol(match_percent: f32, config: &ViewConfig) -> Color32 {
|
||||
if match_percent == 100.0 {
|
||||
Color32::GREEN
|
||||
config.insert_color
|
||||
} else if match_percent >= 50.0 {
|
||||
Color32::LIGHT_BLUE
|
||||
config.replace_color
|
||||
} else {
|
||||
Color32::RED
|
||||
config.delete_color
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,28 +28,28 @@ fn symbol_context_menu_ui(ui: &mut Ui, symbol: &ObjSymbol) {
|
||||
|
||||
if let Some(name) = &symbol.demangled_name {
|
||||
if ui.button(format!("Copy \"{name}\"")).clicked() {
|
||||
ui.output().copied_text = name.clone();
|
||||
ui.output_mut(|output| output.copied_text = name.clone());
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
if ui.button(format!("Copy \"{}\"", symbol.name)).clicked() {
|
||||
ui.output().copied_text = symbol.name.clone();
|
||||
ui.output_mut(|output| output.copied_text = symbol.name.clone());
|
||||
ui.close_menu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn symbol_hover_ui(ui: &mut Ui, symbol: &ObjSymbol) {
|
||||
fn symbol_hover_ui(ui: &mut Ui, symbol: &ObjSymbol, config: &ViewConfig) {
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap = Some(false);
|
||||
|
||||
ui.colored_label(Color32::WHITE, format!("Name: {}", symbol.name));
|
||||
ui.colored_label(Color32::WHITE, format!("Address: {:x}", symbol.address));
|
||||
ui.colored_label(config.highlight_color, format!("Name: {}", symbol.name));
|
||||
ui.colored_label(config.highlight_color, format!("Address: {:x}", symbol.address));
|
||||
if symbol.size_known {
|
||||
ui.colored_label(Color32::WHITE, format!("Size: {:x}", symbol.size));
|
||||
ui.colored_label(config.highlight_color, format!("Size: {:x}", symbol.size));
|
||||
} else {
|
||||
ui.colored_label(Color32::WHITE, format!("Size: {:x} (assumed)", symbol.size));
|
||||
ui.colored_label(config.highlight_color, format!("Size: {:x} (assumed)", symbol.size));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -70,33 +70,38 @@ fn symbol_ui(
|
||||
if let Some(sym) = highlighted_symbol {
|
||||
selected = sym == &symbol.name;
|
||||
}
|
||||
write_text("[", Color32::GRAY, &mut job, config.code_font.clone());
|
||||
write_text("[", config.text_color, &mut job, config.code_font.clone());
|
||||
if symbol.flags.0.contains(ObjSymbolFlags::Common) {
|
||||
write_text("c", Color32::from_rgb(0, 255, 255), &mut job, config.code_font.clone());
|
||||
} else if symbol.flags.0.contains(ObjSymbolFlags::Global) {
|
||||
write_text("g", Color32::GREEN, &mut job, config.code_font.clone());
|
||||
} else if symbol.flags.0.contains(ObjSymbolFlags::Local) {
|
||||
write_text("l", Color32::GRAY, &mut job, config.code_font.clone());
|
||||
}
|
||||
if symbol.flags.0.contains(ObjSymbolFlags::Weak) {
|
||||
write_text("w", Color32::GRAY, &mut job, config.code_font.clone());
|
||||
}
|
||||
write_text("] ", Color32::GRAY, &mut job, config.code_font.clone());
|
||||
if let Some(match_percent) = symbol.match_percent {
|
||||
write_text("(", Color32::GRAY, &mut job, config.code_font.clone());
|
||||
write_text(
|
||||
&format!("{match_percent:.0}%"),
|
||||
match_color_for_symbol(match_percent),
|
||||
"c",
|
||||
config.replace_color, /* Color32::from_rgb(0, 255, 255) */
|
||||
&mut job,
|
||||
config.code_font.clone(),
|
||||
);
|
||||
write_text(") ", Color32::GRAY, &mut job, config.code_font.clone());
|
||||
} else if symbol.flags.0.contains(ObjSymbolFlags::Global) {
|
||||
write_text("g", config.insert_color, &mut job, config.code_font.clone());
|
||||
} else if symbol.flags.0.contains(ObjSymbolFlags::Local) {
|
||||
write_text("l", config.text_color, &mut job, config.code_font.clone());
|
||||
}
|
||||
write_text(name, Color32::WHITE, &mut job, config.code_font.clone());
|
||||
if symbol.flags.0.contains(ObjSymbolFlags::Weak) {
|
||||
write_text("w", config.text_color, &mut job, config.code_font.clone());
|
||||
}
|
||||
write_text("] ", config.text_color, &mut job, config.code_font.clone());
|
||||
if let Some(match_percent) = symbol.match_percent {
|
||||
write_text("(", config.text_color, &mut job, config.code_font.clone());
|
||||
write_text(
|
||||
&format!("{match_percent:.0}%"),
|
||||
match_color_for_symbol(match_percent, config),
|
||||
&mut job,
|
||||
config.code_font.clone(),
|
||||
);
|
||||
write_text(") ", config.text_color, &mut job, config.code_font.clone());
|
||||
}
|
||||
write_text(name, config.highlight_color, &mut job, config.code_font.clone());
|
||||
let response = SelectableLabel::new(selected, job)
|
||||
.ui(ui)
|
||||
.context_menu(|ui| symbol_context_menu_ui(ui, symbol))
|
||||
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, symbol));
|
||||
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, symbol, config));
|
||||
if response.clicked() {
|
||||
if let Some(section) = section {
|
||||
if section.kind == ObjSectionKind::Code {
|
||||
@@ -200,13 +205,13 @@ fn symbol_list_ui(
|
||||
});
|
||||
}
|
||||
|
||||
fn build_log_ui(ui: &mut Ui, status: &BuildStatus) {
|
||||
fn build_log_ui(ui: &mut Ui, status: &BuildStatus, config: &ViewConfig) {
|
||||
ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap = Some(false);
|
||||
|
||||
ui.colored_label(Color32::from_rgb(255, 0, 0), &status.log);
|
||||
ui.colored_label(config.replace_color, &status.log);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -282,9 +287,9 @@ pub fn symbol_diff_ui(ui: &mut Ui, view_state: &mut ViewState) {
|
||||
strip.strip(|builder| {
|
||||
builder.sizes(Size::remainder(), 2).horizontal(|mut strip| {
|
||||
strip.cell(|ui| {
|
||||
if result.first_status.success {
|
||||
if let Some(obj) = &result.first_obj {
|
||||
ui.push_id("left", |ui| {
|
||||
ui.push_id("left", |ui| {
|
||||
if result.first_status.success {
|
||||
if let Some(obj) = &result.first_obj {
|
||||
symbol_list_ui(
|
||||
ui,
|
||||
obj,
|
||||
@@ -294,16 +299,16 @@ pub fn symbol_diff_ui(ui: &mut Ui, view_state: &mut ViewState) {
|
||||
&lower_search,
|
||||
&view_state.view_config,
|
||||
);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
build_log_ui(ui, &result.first_status, &view_state.view_config);
|
||||
}
|
||||
} else {
|
||||
build_log_ui(ui, &result.first_status);
|
||||
}
|
||||
});
|
||||
});
|
||||
strip.cell(|ui| {
|
||||
if result.second_status.success {
|
||||
if let Some(obj) = &result.second_obj {
|
||||
ui.push_id("right", |ui| {
|
||||
ui.push_id("right", |ui| {
|
||||
if result.second_status.success {
|
||||
if let Some(obj) = &result.second_obj {
|
||||
symbol_list_ui(
|
||||
ui,
|
||||
obj,
|
||||
@@ -313,11 +318,11 @@ pub fn symbol_diff_ui(ui: &mut Ui, view_state: &mut ViewState) {
|
||||
&lower_search,
|
||||
&view_state.view_config,
|
||||
);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
build_log_ui(ui, &result.second_status, &view_state.view_config);
|
||||
}
|
||||
} else {
|
||||
build_log_ui(ui, &result.second_status);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user