Compare commits

..

No commits in common. "711f40b591245fa9b6846cca422041f09ed1e3f0" and "5bfa47fce9ab4d783951fde4aa57231a28f1d6c4" have entirely different histories.

12 changed files with 163 additions and 445 deletions

4
Cargo.lock generated
View File

@ -2457,7 +2457,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff" name = "objdiff"
version = "0.4.3" version = "0.4.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"byteorder", "byteorder",
@ -2483,9 +2483,7 @@ dependencies = [
"rabbitizer", "rabbitizer",
"reqwest", "reqwest",
"rfd", "rfd",
"ron",
"self_update", "self_update",
"semver",
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "objdiff" name = "objdiff"
version = "0.4.3" version = "0.4.1"
edition = "2021" edition = "2021"
rust-version = "1.65" rust-version = "1.65"
authors = ["Luke Street <luke@street.dev>"] authors = ["Luke Street <luke@street.dev>"]
@ -42,8 +42,6 @@ png = "0.17.9"
ppc750cl = { git = "https://github.com/terorie/ppc750cl", rev = "9ae36eef34aa6d74e00972c7671f547a2acfd0aa" } ppc750cl = { git = "https://github.com/terorie/ppc750cl", rev = "9ae36eef34aa6d74e00972c7671f547a2acfd0aa" }
rabbitizer = "1.7.4" rabbitizer = "1.7.4"
rfd = { version = "0.11.4" } #, default-features = false, features = ['xdg-portal'] rfd = { version = "0.11.4" } #, default-features = false, features = ['xdg-portal']
ron = "0.8.0"
semver = "1.0.17"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1.0.104" serde_json = "1.0.104"
serde_yaml = "0.9.25" serde_yaml = "0.9.25"

View File

@ -14,14 +14,13 @@ use notify::{RecursiveMode, Watcher};
use time::UtcOffset; use time::UtcOffset;
use crate::{ use crate::{
app_config::{deserialize_config, AppConfigVersion},
config::{ config::{
build_globset, load_project_config, ProjectObject, ProjectObjectNode, CONFIG_FILENAMES, build_globset, load_project_config, ProjectObject, ProjectObjectNode, CONFIG_FILENAMES,
}, },
jobs::{objdiff::start_build, Job, JobQueue, JobResult, JobStatus}, jobs::{objdiff::start_build, Job, JobQueue, JobResult, JobStatus},
views::{ views::{
appearance::{appearance_window, Appearance}, appearance::{appearance_window, Appearance},
config::{config_ui, project_window, ConfigViewState, DEFAULT_WATCH_PATTERNS}, config::{config_ui, project_window, ConfigViewState},
data_diff::data_diff_ui, data_diff::data_diff_ui,
demangle::{demangle_window, DemangleViewState}, demangle::{demangle_window, DemangleViewState},
function_diff::function_diff_ui, function_diff::function_diff_ui,
@ -45,21 +44,16 @@ pub struct ViewState {
#[derive(Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[derive(Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct ObjectConfig { pub struct ObjectConfig {
pub name: String, pub name: String,
pub target_path: Option<PathBuf>, pub target_path: PathBuf,
pub base_path: Option<PathBuf>, pub base_path: PathBuf,
pub reverse_fn_order: Option<bool>, pub reverse_fn_order: Option<bool>,
pub complete: Option<bool>,
} }
#[inline] #[inline]
fn bool_true() -> bool { true } fn bool_true() -> bool { true }
#[derive(Clone, serde::Deserialize, serde::Serialize)] #[derive(Default, Clone, serde::Deserialize, serde::Serialize)]
pub struct AppConfig { pub struct AppConfig {
// TODO: https://github.com/ron-rs/ron/pull/455
// #[serde(flatten)]
// pub version: AppConfigVersion,
pub version: u32,
pub custom_make: Option<String>, pub custom_make: Option<String>,
pub selected_wsl_distro: Option<String>, pub selected_wsl_distro: Option<String>,
pub project_dir: Option<PathBuf>, pub project_dir: Option<PathBuf>,
@ -88,31 +82,6 @@ pub struct AppConfig {
pub project_config_loaded: bool, pub project_config_loaded: bool,
} }
impl Default for AppConfig {
fn default() -> Self {
Self {
version: AppConfigVersion::default().version,
custom_make: None,
selected_wsl_distro: None,
project_dir: None,
target_obj_dir: None,
base_obj_dir: None,
selected_obj: None,
build_target: false,
rebuild_on_changes: true,
auto_update_check: true,
watch_patterns: DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect(),
objects: vec![],
object_nodes: vec![],
watcher_change: false,
config_change: false,
obj_change: false,
queue_build: false,
project_config_loaded: false,
}
}
}
impl AppConfig { impl AppConfig {
pub fn set_project_dir(&mut self, path: PathBuf) { pub fn set_project_dir(&mut self, path: PathBuf) {
self.project_dir = Some(path); self.project_dir = Some(path);
@ -164,8 +133,8 @@ pub struct App {
should_relaunch: bool, should_relaunch: bool,
} }
pub const APPEARANCE_KEY: &str = "appearance"; const APPEARANCE_KEY: &str = "appearance";
pub const CONFIG_KEY: &str = "app_config"; const CONFIG_KEY: &str = "app_config";
impl App { impl App {
/// Called once before the first frame. /// Called once before the first frame.
@ -184,7 +153,7 @@ impl App {
if let Some(appearance) = eframe::get_value::<Appearance>(storage, APPEARANCE_KEY) { if let Some(appearance) = eframe::get_value::<Appearance>(storage, APPEARANCE_KEY) {
app.appearance = appearance; app.appearance = appearance;
} }
if let Some(mut config) = deserialize_config(storage) { if let Some(mut config) = eframe::get_value::<AppConfig>(storage, CONFIG_KEY) {
if config.project_dir.is_some() { if config.project_dir.is_some() {
config.config_change = true; config.config_change = true;
config.watcher_change = true; config.watcher_change = true;

View File

@ -1,96 +0,0 @@
use std::path::PathBuf;
use eframe::Storage;
use globset::Glob;
use crate::app::{AppConfig, ObjectConfig, CONFIG_KEY};
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct AppConfigVersion {
pub version: u32,
}
impl Default for AppConfigVersion {
fn default() -> Self { Self { version: 1 } }
}
/// Deserialize the AppConfig from storage, handling upgrades from older versions.
pub fn deserialize_config(storage: &dyn Storage) -> Option<AppConfig> {
let str = storage.get_string(CONFIG_KEY)?;
match ron::from_str::<AppConfigVersion>(&str) {
Ok(version) => match version.version {
1 => from_str::<AppConfig>(&str),
_ => {
log::warn!("Unknown config version: {}", version.version);
None
}
},
Err(e) => {
log::warn!("Failed to decode config version: {e}");
// Try to decode as v0
from_str::<AppConfigV0>(&str).map(|c| c.into_config())
}
}
}
fn from_str<T>(str: &str) -> Option<T>
where T: serde::de::DeserializeOwned {
match ron::from_str(str) {
Ok(config) => Some(config),
Err(err) => {
log::warn!("Failed to decode config: {err}");
None
}
}
}
#[derive(serde::Deserialize, serde::Serialize)]
pub struct ObjectConfigV0 {
pub name: String,
pub target_path: PathBuf,
pub base_path: PathBuf,
pub reverse_fn_order: Option<bool>,
}
impl ObjectConfigV0 {
fn into_config(self) -> ObjectConfig {
ObjectConfig {
name: self.name,
target_path: Some(self.target_path),
base_path: Some(self.base_path),
reverse_fn_order: self.reverse_fn_order,
complete: None,
}
}
}
#[derive(serde::Deserialize, serde::Serialize)]
pub struct AppConfigV0 {
pub custom_make: Option<String>,
pub selected_wsl_distro: Option<String>,
pub project_dir: Option<PathBuf>,
pub target_obj_dir: Option<PathBuf>,
pub base_obj_dir: Option<PathBuf>,
pub selected_obj: Option<ObjectConfigV0>,
pub build_target: bool,
pub auto_update_check: bool,
pub watch_patterns: Vec<Glob>,
}
impl AppConfigV0 {
fn into_config(self) -> AppConfig {
log::info!("Upgrading configuration from v0");
AppConfig {
custom_make: self.custom_make,
selected_wsl_distro: self.selected_wsl_distro,
project_dir: self.project_dir,
target_obj_dir: self.target_obj_dir,
base_obj_dir: self.base_obj_dir,
selected_obj: self.selected_obj.map(|obj| obj.into_config()),
build_target: self.build_target,
auto_update_check: self.auto_update_check,
watch_patterns: self.watch_patterns,
..Default::default()
}
}
}

View File

@ -3,7 +3,7 @@ use std::{
path::{Component, Path, PathBuf}, path::{Component, Path, PathBuf},
}; };
use anyhow::{bail, Context, Result}; use anyhow::{Context, Result};
use globset::{Glob, GlobSet, GlobSetBuilder}; use globset::{Glob, GlobSet, GlobSetBuilder};
use crate::{app::AppConfig, views::config::DEFAULT_WATCH_PATTERNS}; use crate::{app::AppConfig, views::config::DEFAULT_WATCH_PATTERNS};
@ -11,7 +11,6 @@ use crate::{app::AppConfig, views::config::DEFAULT_WATCH_PATTERNS};
#[derive(Default, Clone, serde::Deserialize)] #[derive(Default, Clone, serde::Deserialize)]
#[serde(default)] #[serde(default)]
pub struct ProjectConfig { pub struct ProjectConfig {
pub min_version: Option<String>,
pub custom_make: Option<String>, pub custom_make: Option<String>,
pub target_dir: Option<PathBuf>, pub target_dir: Option<PathBuf>,
pub base_dir: Option<PathBuf>, pub base_dir: Option<PathBuf>,
@ -28,7 +27,6 @@ pub struct ProjectObject {
pub target_path: Option<PathBuf>, pub target_path: Option<PathBuf>,
pub base_path: Option<PathBuf>, pub base_path: Option<PathBuf>,
pub reverse_fn_order: Option<bool>, pub reverse_fn_order: Option<bool>,
pub complete: Option<bool>,
} }
impl ProjectObject { impl ProjectObject {
@ -122,14 +120,6 @@ pub fn load_project_config(config: &mut AppConfig) -> Result<()> {
}; };
if let Some(result) = try_project_config(project_dir) { if let Some(result) = try_project_config(project_dir) {
let project_config = result?; let project_config = result?;
if let Some(min_version) = &project_config.min_version {
let version_str = env!("CARGO_PKG_VERSION");
let version = semver::Version::parse(version_str).unwrap();
let version_req = semver::VersionReq::parse(&format!(">={min_version}"))?;
if !version_req.matches(&version) {
bail!("Project requires objdiff version {} or higher", min_version);
}
}
config.custom_make = project_config.custom_make; config.custom_make = project_config.custom_make;
config.target_obj_dir = project_config.target_dir.map(|p| project_dir.join(p)); config.target_obj_dir = project_config.target_dir.map(|p| project_dir.join(p));
config.base_obj_dir = project_config.base_dir.map(|p| project_dir.join(p)); config.base_obj_dir = project_config.base_dir.map(|p| project_dir.join(p));

View File

@ -372,80 +372,66 @@ fn find_section_and_symbol(obj: &ObjInfo, name: &str) -> Option<(usize, usize)>
None None
} }
pub fn diff_objs(mut left: Option<&mut ObjInfo>, mut right: Option<&mut ObjInfo>) -> Result<()> { pub fn diff_objs(left: &mut ObjInfo, right: &mut ObjInfo) -> Result<()> {
if let Some(left) = left.as_mut() { for left_section in &mut left.sections {
for left_section in &mut left.sections { if left_section.kind == ObjSectionKind::Code {
if left_section.kind == ObjSectionKind::Code { for left_symbol in &mut left_section.symbols {
for left_symbol in &mut left_section.symbols { if let Some((right_section_idx, right_symbol_idx)) =
if let Some((right, (right_section_idx, right_symbol_idx))) = find_section_and_symbol(right, &left_symbol.name)
right.as_mut().and_then(|obj| { {
find_section_and_symbol(obj, &left_symbol.name).map(|s| (obj, s)) 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());
let right_section = &mut right.sections[right_section_idx]; right_symbol.diff_symbol = Some(left_symbol.name.clone());
let right_symbol = &mut right_section.symbols[right_symbol_idx]; diff_code(
left_symbol.diff_symbol = Some(right_symbol.name.clone()); left.architecture,
right_symbol.diff_symbol = Some(left_symbol.name.clone()); &left_section.data,
diff_code( &right_section.data,
left.architecture, left_symbol,
&left_section.data, right_symbol,
&right_section.data, &left_section.relocations,
left_symbol, &right_section.relocations,
right_symbol, &left.line_info,
&left_section.relocations, &right.line_info,
&right_section.relocations, )?;
&left.line_info, } else {
&right.line_info, no_diff_code(
)?; left.architecture,
} else { &left_section.data,
no_diff_code( left_symbol,
left.architecture, &left_section.relocations,
&left_section.data, &left.line_info,
left_symbol, )?;
&left_section.relocations,
&left.line_info,
)?;
}
} }
} else if let Some(right_section) = right }
.as_mut() } else {
.and_then(|obj| obj.sections.iter_mut().find(|s| s.name == left_section.name)) let Some(right_section) =
{ right.sections.iter_mut().find(|s| s.name == left_section.name)
if left_section.kind == ObjSectionKind::Data { else {
diff_data(left_section, right_section); continue;
// diff_data_symbols(left_section, right_section)?; };
} else if left_section.kind == ObjSectionKind::Bss { if left_section.kind == ObjSectionKind::Data {
diff_bss_symbols(&mut left_section.symbols, &mut right_section.symbols)?; diff_data(left_section, right_section);
} // diff_data_symbols(left_section, right_section)?;
} else if left_section.kind == ObjSectionKind::Data { } else if left_section.kind == ObjSectionKind::Bss {
no_diff_data(left_section); diff_bss_symbols(&mut left_section.symbols, &mut right_section.symbols)?;
} }
} }
} }
if let Some(right) = right.as_mut() { for right_section in right.sections.iter_mut().filter(|s| s.kind == ObjSectionKind::Code) {
for right_section in right.sections.iter_mut() { for right_symbol in &mut right_section.symbols {
if right_section.kind == ObjSectionKind::Code { if right_symbol.instructions.is_empty() {
for right_symbol in &mut right_section.symbols { no_diff_code(
if right_symbol.instructions.is_empty() { right.architecture,
no_diff_code( &right_section.data,
right.architecture, right_symbol,
&right_section.data, &right_section.relocations,
right_symbol, &right.line_info,
&right_section.relocations, )?;
&right.line_info,
)?;
}
}
} else if right_section.kind == ObjSectionKind::Data
&& right_section.data_diff.is_empty()
{
no_diff_data(right_section);
} }
} }
} }
if let (Some(left), Some(right)) = (left, right) { diff_bss_symbols(&mut left.common, &mut right.common)?;
diff_bss_symbols(&mut left.common, &mut right.common)?;
}
Ok(()) Ok(())
} }
@ -724,12 +710,3 @@ fn diff_data(left: &mut ObjSection, right: &mut ObjSection) {
left.data_diff = left_diff; left.data_diff = left_diff;
right.data_diff = right_diff; right.data_diff = right_diff;
} }
fn no_diff_data(section: &mut ObjSection) {
section.data_diff = vec![ObjDataDiff {
data: section.data.clone(),
kind: ObjDataDiffKind::None,
len: section.data.len(),
symbol: String::new(),
}];
}

View File

@ -79,101 +79,56 @@ fn run_build(
let obj_config = config.selected_obj.as_ref().ok_or_else(|| Error::msg("Missing obj path"))?; let obj_config = config.selected_obj.as_ref().ok_or_else(|| Error::msg("Missing obj path"))?;
let project_dir = let project_dir =
config.project_dir.as_ref().ok_or_else(|| Error::msg("Missing project dir"))?; config.project_dir.as_ref().ok_or_else(|| Error::msg("Missing project dir"))?;
let target_path_rel = if let Some(target_path) = &obj_config.target_path { let target_path_rel = obj_config.target_path.strip_prefix(project_dir).map_err(|_| {
Some(target_path.strip_prefix(project_dir).map_err(|_| { anyhow!(
anyhow!( "Target path '{}' doesn't begin with '{}'",
"Target path '{}' doesn't begin with '{}'", obj_config.target_path.display(),
target_path.display(), project_dir.display()
project_dir.display() )
) })?;
})?) let base_path_rel = obj_config.base_path.strip_prefix(project_dir).map_err(|_| {
} else { anyhow!(
None "Base path '{}' doesn't begin with '{}'",
}; obj_config.base_path.display(),
let base_path_rel = if let Some(base_path) = &obj_config.base_path { project_dir.display()
Some(base_path.strip_prefix(project_dir).map_err(|_| { )
anyhow!( })?;
"Base path '{}' doesn't begin with '{}'",
base_path.display(),
project_dir.display()
)
})?)
} else {
None
};
let mut total = 3; let total = if config.build_target { 5 } else { 4 };
if config.build_target && target_path_rel.is_some() { let first_status = if config.build_target {
total += 1; update_status(status, format!("Building target {}", target_path_rel.display()), 0, total, &cancel)?;
} run_make(project_dir, target_path_rel, &config)
if base_path_rel.is_some() {
total += 1;
}
let first_status = match target_path_rel {
Some(target_path_rel) if config.build_target => {
update_status(
status,
format!("Building target {}", target_path_rel.display()),
0,
total,
&cancel,
)?;
run_make(project_dir, target_path_rel, &config)
}
_ => BuildStatus { success: true, log: String::new() },
};
let second_status = if let Some(base_path_rel) = base_path_rel {
update_status(
status,
format!("Building base {}", base_path_rel.display()),
1,
total,
&cancel,
)?;
run_make(project_dir, base_path_rel, &config)
} else { } else {
BuildStatus { success: true, log: String::new() } BuildStatus { success: true, log: String::new() }
}; };
update_status(status, format!("Building base {}", base_path_rel.display()), 1, total, &cancel)?;
let second_status = run_make(project_dir, base_path_rel, &config);
let time = OffsetDateTime::now_utc(); let time = OffsetDateTime::now_utc();
let mut first_obj = let mut first_obj = if first_status.success {
match &obj_config.target_path { update_status(status, format!("Loading target {}", target_path_rel.display()), 2, total, &cancel)?;
Some(target_path) if first_status.success => { Some(elf::read(&obj_config.target_path).with_context(|| {
update_status( format!("Failed to read object '{}'", obj_config.target_path.display())
status, })?)
format!("Loading target {}", target_path_rel.unwrap().display()), } else {
2, None
total,
&cancel,
)?;
Some(elf::read(target_path).with_context(|| {
format!("Failed to read object '{}'", target_path.display())
})?)
}
_ => None,
};
let mut second_obj = match &obj_config.base_path {
Some(base_path) if second_status.success => {
update_status(
status,
format!("Loading base {}", base_path_rel.unwrap().display()),
3,
total,
&cancel,
)?;
Some(
elf::read(base_path)
.with_context(|| format!("Failed to read object '{}'", base_path.display()))?,
)
}
_ => None,
}; };
update_status(status, "Performing diff".to_string(), 4, total, &cancel)?; let mut second_obj = if second_status.success {
diff_objs(first_obj.as_mut(), second_obj.as_mut())?; update_status(status, format!("Loading base {}", base_path_rel.display()), 3, total, &cancel)?;
Some(elf::read(&obj_config.base_path).with_context(|| {
format!("Failed to read object '{}'", obj_config.base_path.display())
})?)
} else {
None
};
if let (Some(first_obj), Some(second_obj)) = (&mut first_obj, &mut second_obj) {
update_status(status, "Performing diff".to_string(), 4, total, &cancel)?;
diff_objs(first_obj, second_obj)?;
}
update_status(status, "Complete".to_string(), total, total, &cancel)?; update_status(status, "Complete".to_string(), total, total, &cancel)?;
Ok(Box::new(ObjDiffResult { first_status, second_status, first_obj, second_obj, time })) Ok(Box::new(ObjDiffResult { first_status, second_status, first_obj, second_obj, time }))

View File

@ -3,7 +3,6 @@
pub use app::App; pub use app::App;
mod app; mod app;
mod app_config;
mod config; mod config;
mod diff; mod diff;
mod editops; mod editops;

View File

@ -40,7 +40,6 @@ pub struct ConfigViewState {
pub watch_pattern_text: String, pub watch_pattern_text: String,
pub load_error: Option<String>, pub load_error: Option<String>,
pub object_search: String, pub object_search: String,
pub filter_diffable: bool,
#[cfg(windows)] #[cfg(windows)]
pub available_wsl_distros: Option<Vec<String>>, pub available_wsl_distros: Option<Vec<String>>,
} }
@ -219,19 +218,17 @@ pub fn config_ui(
let target_path = target_dir.join(obj_path); let target_path = target_dir.join(obj_path);
new_selected_obj = Some(ObjectConfig { new_selected_obj = Some(ObjectConfig {
name: obj_path.display().to_string(), name: obj_path.display().to_string(),
target_path: Some(target_path), target_path,
base_path: Some(path), base_path: path,
reverse_fn_order: None, reverse_fn_order: None,
complete: None,
}); });
} else if let Ok(obj_path) = path.strip_prefix(&target_dir) { } else if let Ok(obj_path) = path.strip_prefix(&target_dir) {
let base_path = base_dir.join(obj_path); let base_path = base_dir.join(obj_path);
new_selected_obj = Some(ObjectConfig { new_selected_obj = Some(ObjectConfig {
name: obj_path.display().to_string(), name: obj_path.display().to_string(),
target_path: Some(path), target_path: path,
base_path: Some(base_path), base_path,
reverse_fn_order: None, reverse_fn_order: None,
complete: None,
}); });
} }
} }
@ -269,13 +266,6 @@ pub fn config_ui(
root_open = Some(true); root_open = Some(true);
node_open = NodeOpen::Object; node_open = NodeOpen::Object;
} }
if ui
.selectable_label(state.filter_diffable, "Diffable")
.on_hover_text_at_pointer("Only show objects with a source file")
.clicked()
{
state.filter_diffable = !state.filter_diffable;
}
}); });
if state.object_search.is_empty() { if state.object_search.is_empty() {
if had_search { if had_search {
@ -295,13 +285,10 @@ pub fn config_ui(
.default_open(true) .default_open(true)
.show(ui, |ui| { .show(ui, |ui| {
let mut nodes = Cow::Borrowed(object_nodes); let mut nodes = Cow::Borrowed(object_nodes);
if !state.object_search.is_empty() || state.filter_diffable { if !state.object_search.is_empty() {
let search = state.object_search.to_ascii_lowercase(); let search = state.object_search.to_ascii_lowercase();
nodes = Cow::Owned( nodes = Cow::Owned(
object_nodes object_nodes.iter().filter_map(|node| filter_node(node, &search)).collect(),
.iter()
.filter_map(|node| filter_node(node, &search, state.filter_diffable))
.collect(),
); );
} }
@ -335,18 +322,8 @@ fn display_object(
) { ) {
let object_name = object.name(); let object_name = object.name();
let selected = matches!(selected_obj, Some(obj) if obj.name == object_name); let selected = matches!(selected_obj, Some(obj) if obj.name == object_name);
let color = if selected { let color = if selected { appearance.emphasized_text_color } else { appearance.text_color };
appearance.emphasized_text_color if SelectableLabel::new(
} else if let Some(complete) = object.complete {
if complete {
appearance.insert_color
} else {
appearance.delete_color
}
} else {
appearance.text_color
};
let clicked = SelectableLabel::new(
selected, selected,
RichText::new(name) RichText::new(name)
.font(FontId { .font(FontId {
@ -356,16 +333,13 @@ fn display_object(
.color(color), .color(color),
) )
.ui(ui) .ui(ui)
.clicked(); .clicked()
// Always recreate ObjectConfig if selected, in case the project config changed. {
// ObjectConfig is compared using equality, so this won't unnecessarily trigger a rebuild.
if selected || clicked {
*selected_obj = Some(ObjectConfig { *selected_obj = Some(ObjectConfig {
name: object_name.to_string(), name: object_name.to_string(),
target_path: object.target_path.clone(), target_path: object.target_path.clone().unwrap_or_default(),
base_path: object.base_path.clone(), base_path: object.base_path.clone().unwrap_or_default(),
reverse_fn_order: object.reverse_fn_order, reverse_fn_order: object.reverse_fn_order,
complete: object.complete,
}); });
} }
} }
@ -430,30 +404,21 @@ fn contains_node(node: &ProjectObjectNode, selected_obj: &ObjectConfig) -> bool
} }
} }
fn filter_node( fn filter_node(node: &ProjectObjectNode, search: &str) -> Option<ProjectObjectNode> {
node: &ProjectObjectNode,
search: &str,
filter_diffable: bool,
) -> Option<ProjectObjectNode> {
match node { match node {
ProjectObjectNode::File(name, object) => { ProjectObjectNode::File(name, _) => {
if (search.is_empty() || name.to_ascii_lowercase().contains(search)) if name.to_ascii_lowercase().contains(search) {
&& (!filter_diffable || object.base_path.is_some())
{
Some(node.clone()) Some(node.clone())
} else { } else {
None None
} }
} }
ProjectObjectNode::Dir(name, children) => { ProjectObjectNode::Dir(name, children) => {
if (search.is_empty() || name.to_ascii_lowercase().contains(search)) && !filter_diffable if name.to_ascii_lowercase().contains(search) {
{
return Some(node.clone()); return Some(node.clone());
} }
let new_children = children let new_children =
.iter() children.iter().filter_map(|child| filter_node(child, search)).collect::<Vec<_>>();
.filter_map(|child| filter_node(child, search, filter_diffable))
.collect::<Vec<_>>();
if !new_children.is_empty() { if !new_children.is_empty() {
Some(ProjectObjectNode::Dir(name.clone(), new_children)) Some(ProjectObjectNode::Dir(name.clone(), new_children))
} else { } else {

View File

@ -132,39 +132,31 @@ fn split_diffs(diffs: &[ObjDataDiff]) -> Vec<Vec<ObjDataDiff>> {
fn data_table_ui( fn data_table_ui(
table: TableBuilder<'_>, table: TableBuilder<'_>,
left_obj: Option<&ObjInfo>, left_obj: &ObjInfo,
right_obj: Option<&ObjInfo>, right_obj: &ObjInfo,
selected_symbol: &SymbolReference, selected_symbol: &SymbolReference,
config: &Appearance, config: &Appearance,
) -> Option<()> { ) -> Option<()> {
let left_section = left_obj.and_then(|obj| find_section(obj, selected_symbol)); let left_section = find_section(left_obj, selected_symbol)?;
let right_section = right_obj.and_then(|obj| find_section(obj, selected_symbol)); let right_section = find_section(right_obj, selected_symbol)?;
let total_bytes = left_section let total_bytes = left_section.data_diff.iter().fold(0usize, |accum, item| accum + item.len);
.or(right_section)?
.data_diff
.iter()
.fold(0usize, |accum, item| accum + item.len);
if total_bytes == 0 { if total_bytes == 0 {
return None; return None;
} }
let total_rows = (total_bytes - 1) / BYTES_PER_ROW + 1; let total_rows = (total_bytes - 1) / BYTES_PER_ROW + 1;
let left_diffs = left_section.map(|section| split_diffs(&section.data_diff)); let left_diffs = split_diffs(&left_section.data_diff);
let right_diffs = right_section.map(|section| split_diffs(&section.data_diff)); let right_diffs = split_diffs(&right_section.data_diff);
table.body(|body| { table.body(|body| {
body.rows(config.code_font.size, total_rows, |row_index, mut row| { body.rows(config.code_font.size, total_rows, |row_index, mut row| {
let address = row_index * BYTES_PER_ROW; let address = row_index * BYTES_PER_ROW;
row.col(|ui| { row.col(|ui| {
if let Some(left_diffs) = &left_diffs { data_row_ui(ui, address, &left_diffs[row_index], config);
data_row_ui(ui, address, &left_diffs[row_index], config);
}
}); });
row.col(|ui| { row.col(|ui| {
if let Some(right_diffs) = &right_diffs { data_row_ui(ui, address, &right_diffs[row_index], config);
data_row_ui(ui, address, &right_diffs[row_index], config);
}
}); });
}); });
}); });
@ -251,19 +243,15 @@ pub fn data_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance: &A
ui.separator(); ui.separator();
// Table // Table
let available_height = ui.available_height(); if let (Some(left_obj), Some(right_obj)) = (&result.first_obj, &result.second_obj) {
let table = TableBuilder::new(ui) let available_height = ui.available_height();
.striped(false) let table = TableBuilder::new(ui)
.cell_layout(Layout::left_to_right(Align::Min)) .striped(false)
.columns(Column::exact(column_width).clip(true), 2) .cell_layout(Layout::left_to_right(Align::Min))
.resizable(false) .columns(Column::exact(column_width).clip(true), 2)
.auto_shrink([false, false]) .resizable(false)
.min_scrolled_height(available_height); .auto_shrink([false, false])
data_table_ui( .min_scrolled_height(available_height);
table, data_table_ui(table, left_obj, right_obj, selected_symbol, appearance);
result.first_obj.as_ref(), }
result.second_obj.as_ref(),
selected_symbol,
appearance,
);
} }

View File

@ -377,13 +377,13 @@ fn asm_row_ui(
fn asm_table_ui( fn asm_table_ui(
table: TableBuilder<'_>, table: TableBuilder<'_>,
left_obj: Option<&ObjInfo>, left_obj: &ObjInfo,
right_obj: Option<&ObjInfo>, right_obj: &ObjInfo,
selected_symbol: &SymbolReference, selected_symbol: &SymbolReference,
appearance: &Appearance, appearance: &Appearance,
) -> Option<()> { ) -> Option<()> {
let left_symbol = left_obj.and_then(|obj| find_symbol(obj, selected_symbol)); let left_symbol = find_symbol(left_obj, selected_symbol);
let right_symbol = right_obj.and_then(|obj| find_symbol(obj, selected_symbol)); let right_symbol = find_symbol(right_obj, selected_symbol);
let instructions_len = left_symbol.or(right_symbol).map(|s| s.instructions.len())?; let instructions_len = left_symbol.or(right_symbol).map(|s| s.instructions.len())?;
table.body(|body| { table.body(|body| {
body.rows(appearance.code_font.size, instructions_len, |row_index, mut row| { body.rows(appearance.code_font.size, instructions_len, |row_index, mut row| {
@ -492,7 +492,7 @@ pub fn function_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance
&format!("{match_percent:.0}%"), &format!("{match_percent:.0}%"),
); );
} else { } else {
ui.colored_label(appearance.replace_color, "Missing"); ui.label("");
} }
ui.label("Diff base:"); ui.label("Diff base:");
}); });
@ -503,19 +503,15 @@ pub fn function_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance
ui.separator(); ui.separator();
// Table // Table
let available_height = ui.available_height(); if let (Some(left_obj), Some(right_obj)) = (&result.first_obj, &result.second_obj) {
let table = TableBuilder::new(ui) let available_height = ui.available_height();
.striped(false) let table = TableBuilder::new(ui)
.cell_layout(Layout::left_to_right(Align::Min)) .striped(false)
.columns(Column::exact(column_width).clip(true), 2) .cell_layout(Layout::left_to_right(Align::Min))
.resizable(false) .columns(Column::exact(column_width).clip(true), 2)
.auto_shrink([false, false]) .resizable(false)
.min_scrolled_height(available_height); .auto_shrink([false, false])
asm_table_ui( .min_scrolled_height(available_height);
table, asm_table_ui(table, left_obj, right_obj, selected_symbol, appearance);
result.first_obj.as_ref(), }
result.second_obj.as_ref(),
selected_symbol,
appearance,
);
} }

View File

@ -1,7 +1,7 @@
use std::mem::take; use std::mem::take;
use egui::{ use egui::{
text::LayoutJob, Align, CollapsingHeader, Color32, Layout, ScrollArea, SelectableLabel, text::LayoutJob, Align, CollapsingHeader, Color32, Layout, Rgba, ScrollArea, SelectableLabel,
TextEdit, Ui, Vec2, Widget, TextEdit, Ui, Vec2, Widget,
}; };
use egui_extras::{Size, StripBuilder}; use egui_extras::{Size, StripBuilder};
@ -263,15 +263,6 @@ fn build_log_ui(ui: &mut Ui, status: &BuildStatus, appearance: &Appearance) {
}); });
} }
fn missing_obj_ui(ui: &mut Ui, appearance: &Appearance) {
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap = Some(false);
ui.colored_label(appearance.replace_color, "No object configured");
});
}
pub fn symbol_diff_ui(ui: &mut Ui, state: &mut DiffViewState, appearance: &Appearance) { pub fn symbol_diff_ui(ui: &mut Ui, state: &mut DiffViewState, appearance: &Appearance) {
let DiffViewState { build, current_view, symbol_state, search, .. } = state; let DiffViewState { build, current_view, symbol_state, search, .. } = state;
let Some(result) = build else { let Some(result) = build else {
@ -298,13 +289,9 @@ pub fn symbol_diff_ui(ui: &mut Ui, state: &mut DiffViewState, appearance: &Appea
ui.label("Build target:"); ui.label("Build target:");
if result.first_status.success { if result.first_status.success {
if result.first_obj.is_none() { ui.label("OK");
ui.colored_label(appearance.replace_color, "Missing");
} else {
ui.label("OK");
}
} else { } else {
ui.colored_label(appearance.delete_color, "Fail"); ui.colored_label(Rgba::from_rgb(1.0, 0.0, 0.0), "Fail");
} }
}); });
@ -325,13 +312,9 @@ pub fn symbol_diff_ui(ui: &mut Ui, state: &mut DiffViewState, appearance: &Appea
ui.label("Build base:"); ui.label("Build base:");
if result.second_status.success { if result.second_status.success {
if result.second_obj.is_none() { ui.label("OK");
ui.colored_label(appearance.replace_color, "Missing");
} else {
ui.label("OK");
}
} else { } else {
ui.colored_label(appearance.delete_color, "Fail"); ui.colored_label(Rgba::from_rgb(1.0, 0.0, 0.0), "Fail");
} }
}); });
@ -365,8 +348,6 @@ pub fn symbol_diff_ui(ui: &mut Ui, state: &mut DiffViewState, appearance: &Appea
&lower_search, &lower_search,
appearance, appearance,
)); ));
} else {
missing_obj_ui(ui, appearance);
} }
} else { } else {
build_log_ui(ui, &result.first_status, appearance); build_log_ui(ui, &result.first_status, appearance);
@ -384,8 +365,6 @@ pub fn symbol_diff_ui(ui: &mut Ui, state: &mut DiffViewState, appearance: &Appea
&lower_search, &lower_search,
appearance, appearance,
)); ));
} else {
missing_obj_ui(ui, appearance);
} }
} else { } else {
build_log_ui(ui, &result.second_status, appearance); build_log_ui(ui, &result.second_status, appearance);