mirror of https://github.com/encounter/objdiff.git
256 lines
8.6 KiB
Rust
256 lines
8.6 KiB
Rust
#[cfg(windows)]
|
|
use std::string::FromUtf16Error;
|
|
use std::sync::{Arc, RwLock};
|
|
|
|
#[cfg(windows)]
|
|
use anyhow::{Context, Result};
|
|
use const_format::formatcp;
|
|
use egui::{output::OpenUrl, Color32};
|
|
use self_update::cargo_crate_version;
|
|
|
|
use crate::{
|
|
app::{AppConfig, DiffKind, ViewState},
|
|
jobs::{bindiff::queue_bindiff, objdiff::queue_build, update::queue_update},
|
|
update::RELEASE_URL,
|
|
};
|
|
|
|
#[cfg(windows)]
|
|
fn process_utf16(bytes: &[u8]) -> Result<String, FromUtf16Error> {
|
|
let u16_bytes: Vec<u16> = bytes
|
|
.chunks_exact(2)
|
|
.filter_map(|c| Some(u16::from_ne_bytes(c.try_into().ok()?)))
|
|
.collect();
|
|
String::from_utf16(&u16_bytes)
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
fn wsl_cmd(args: &[&str]) -> Result<String> {
|
|
use std::{os::windows::process::CommandExt, process::Command};
|
|
let output = Command::new("wsl")
|
|
.args(args)
|
|
.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW)
|
|
.output()
|
|
.context("Failed to execute wsl")?;
|
|
process_utf16(&output.stdout).context("Failed to process stdout")
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
fn fetch_wsl2_distros() -> Vec<String> {
|
|
wsl_cmd(&["-l", "-q"])
|
|
.map(|stdout| {
|
|
stdout
|
|
.split('\n')
|
|
.filter(|s| !s.trim().is_empty())
|
|
.map(|s| s.trim().to_string())
|
|
.collect()
|
|
})
|
|
.unwrap_or_default()
|
|
}
|
|
|
|
pub fn config_ui(ui: &mut egui::Ui, config: &Arc<RwLock<AppConfig>>, view_state: &mut ViewState) {
|
|
let mut config_guard = config.write().unwrap();
|
|
let AppConfig {
|
|
custom_make,
|
|
available_wsl_distros,
|
|
selected_wsl_distro,
|
|
project_dir,
|
|
target_obj_dir,
|
|
base_obj_dir,
|
|
obj_path,
|
|
build_target,
|
|
left_obj,
|
|
right_obj,
|
|
project_dir_change,
|
|
queue_update_check,
|
|
auto_update_check,
|
|
} = &mut *config_guard;
|
|
|
|
ui.heading("Updates");
|
|
ui.checkbox(auto_update_check, "Check for updates on startup");
|
|
if ui.button("Check now").clicked() {
|
|
*queue_update_check = true;
|
|
}
|
|
ui.label(format!("Current version: {}", cargo_crate_version!())).on_hover_ui_at_pointer(|ui| {
|
|
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")));
|
|
});
|
|
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.horizontal(|ui| {
|
|
if state.found_binary {
|
|
if ui
|
|
.button("Automatic")
|
|
.on_hover_text_at_pointer(
|
|
"Automatically download and replace the current build",
|
|
)
|
|
.clicked()
|
|
{
|
|
view_state.jobs.push(queue_update());
|
|
}
|
|
}
|
|
if ui
|
|
.button("Manual")
|
|
.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.separator();
|
|
|
|
ui.heading("Build config");
|
|
|
|
#[cfg(windows)]
|
|
{
|
|
if available_wsl_distros.is_none() {
|
|
*available_wsl_distros = Some(fetch_wsl2_distros());
|
|
}
|
|
egui::ComboBox::from_label("Run in WSL2")
|
|
.selected_text(selected_wsl_distro.as_ref().unwrap_or(&"None".to_string()))
|
|
.show_ui(ui, |ui| {
|
|
ui.selectable_value(selected_wsl_distro, None, "None");
|
|
for distro in available_wsl_distros.as_ref().unwrap() {
|
|
ui.selectable_value(selected_wsl_distro, Some(distro.clone()), distro);
|
|
}
|
|
});
|
|
}
|
|
#[cfg(not(windows))]
|
|
{
|
|
let _ = available_wsl_distros;
|
|
let _ = selected_wsl_distro;
|
|
}
|
|
|
|
ui.label("Custom make program:");
|
|
let mut custom_make_str = custom_make.clone().unwrap_or_default();
|
|
if ui.text_edit_singleline(&mut custom_make_str).changed() {
|
|
if custom_make_str.is_empty() {
|
|
*custom_make = None;
|
|
} else {
|
|
*custom_make = Some(custom_make_str);
|
|
}
|
|
}
|
|
|
|
ui.separator();
|
|
|
|
ui.heading("Project config");
|
|
|
|
if view_state.diff_kind == DiffKind::SplitObj {
|
|
if ui.button("Select project dir").clicked() {
|
|
if let Some(path) = rfd::FileDialog::new().pick_folder() {
|
|
*project_dir = Some(path);
|
|
*project_dir_change = true;
|
|
*target_obj_dir = None;
|
|
*base_obj_dir = None;
|
|
*obj_path = None;
|
|
}
|
|
}
|
|
if let Some(dir) = project_dir {
|
|
ui.label(dir.to_string_lossy());
|
|
}
|
|
|
|
ui.separator();
|
|
|
|
if let Some(project_dir) = project_dir {
|
|
if ui.button("Select target build dir").clicked() {
|
|
if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder()
|
|
{
|
|
*target_obj_dir = Some(path);
|
|
*obj_path = None;
|
|
}
|
|
}
|
|
if let Some(dir) = target_obj_dir {
|
|
ui.label(dir.to_string_lossy());
|
|
}
|
|
ui.checkbox(build_target, "Build target");
|
|
|
|
ui.separator();
|
|
|
|
if ui.button("Select base build dir").clicked() {
|
|
if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder()
|
|
{
|
|
*base_obj_dir = Some(path);
|
|
*obj_path = None;
|
|
}
|
|
}
|
|
if let Some(dir) = base_obj_dir {
|
|
ui.label(dir.to_string_lossy());
|
|
}
|
|
|
|
ui.separator();
|
|
}
|
|
|
|
if let Some(base_dir) = base_obj_dir {
|
|
if ui.button("Select obj").clicked() {
|
|
if let Some(path) = rfd::FileDialog::new()
|
|
.set_directory(&base_dir)
|
|
.add_filter("Object file", &["o", "elf"])
|
|
.pick_file()
|
|
{
|
|
let mut new_build_obj: Option<String> = None;
|
|
if let Ok(obj_path) = path.strip_prefix(&base_dir) {
|
|
new_build_obj = Some(obj_path.display().to_string());
|
|
} else if let Some(build_asm_dir) = target_obj_dir {
|
|
if let Ok(obj_path) = path.strip_prefix(&build_asm_dir) {
|
|
new_build_obj = Some(obj_path.display().to_string());
|
|
}
|
|
}
|
|
if let Some(new_build_obj) = new_build_obj {
|
|
*obj_path = Some(new_build_obj);
|
|
view_state
|
|
.jobs
|
|
.push(queue_build(config.clone(), view_state.diff_config.clone()));
|
|
}
|
|
}
|
|
}
|
|
if let Some(obj) = obj_path {
|
|
ui.label(&*obj);
|
|
if ui.button("Build").clicked() {
|
|
view_state
|
|
.jobs
|
|
.push(queue_build(config.clone(), view_state.diff_config.clone()));
|
|
}
|
|
}
|
|
|
|
ui.separator();
|
|
}
|
|
} else if view_state.diff_kind == DiffKind::WholeBinary {
|
|
if ui.button("Select left obj").clicked() {
|
|
if let Some(path) =
|
|
rfd::FileDialog::new().add_filter("Object file", &["o", "elf"]).pick_file()
|
|
{
|
|
*left_obj = Some(path);
|
|
}
|
|
}
|
|
if let Some(obj) = left_obj {
|
|
ui.label(obj.to_string_lossy());
|
|
}
|
|
|
|
if ui.button("Select right obj").clicked() {
|
|
if let Some(path) =
|
|
rfd::FileDialog::new().add_filter("Object file", &["o", "elf"]).pick_file()
|
|
{
|
|
*right_obj = Some(path);
|
|
}
|
|
}
|
|
if let Some(obj) = right_obj {
|
|
ui.label(obj.to_string_lossy());
|
|
}
|
|
|
|
if let (Some(_), Some(_)) = (left_obj, right_obj) {
|
|
if ui.button("Build").clicked() {
|
|
view_state.jobs.push(queue_bindiff(config.clone()));
|
|
}
|
|
}
|
|
}
|
|
|
|
ui.checkbox(&mut view_state.reverse_fn_order, "Reverse function order (deferred)");
|
|
ui.separator();
|
|
}
|