use std::path::{Path, PathBuf};
use crate::config::Env;
pub fn ida_user_dir() -> PathBuf {
let env = Env::global();
if let Some(ref d) = env.hcli_idausr {
return PathBuf::from(d);
}
if let Some(ref d) = env.idausr {
let sep = if cfg!(windows) { ';' } else { ':' };
return PathBuf::from(d.split(sep).next().unwrap_or(d));
}
if cfg!(target_os = "macos") {
dirs::home_dir().unwrap_or_default().join(".idapro")
} else if cfg!(target_os = "windows") {
dirs::home_dir()
.unwrap_or_default()
.join("AppData")
.join("Hex-Rays")
.join("IDA Pro")
} else {
dirs::home_dir().unwrap_or_default().join(".idapro")
}
}
pub fn default_install_dir() -> PathBuf {
if cfg!(target_os = "macos") {
PathBuf::from("/Applications")
} else if cfg!(target_os = "windows") {
PathBuf::from(r"C:\Program Files\IDA Pro")
} else {
dirs::home_dir().unwrap_or_default().join("ida")
}
}
pub fn ida_binary_path(install_dir: &Path) -> Option<PathBuf> {
if cfg!(target_os = "macos") {
if install_dir.extension().is_some_and(|e| e == "app") {
return macos_bundle_binary(install_dir);
}
let candidates = ["ida64.app", "ida.app", "IDA Professional.app"];
for name in &candidates {
let p = install_dir.join(name);
if p.exists() {
return macos_bundle_binary(&p);
}
}
for name in &["ida64", "ida"] {
let p = install_dir.join(name);
if p.is_file() {
return Some(p);
}
let p = install_dir.join("Contents").join("MacOS").join(name);
if p.is_file() {
return Some(p);
}
}
None
} else if cfg!(target_os = "windows") {
let p = install_dir.join("ida64.exe");
if p.exists() {
Some(p)
} else {
let p = install_dir.join("ida.exe");
p.exists().then_some(p)
}
} else {
let p = install_dir.join("ida64");
if p.exists() {
Some(p)
} else {
let p = install_dir.join("ida");
p.exists().then_some(p)
}
}
}
fn macos_bundle_binary(bundle: &Path) -> Option<PathBuf> {
let macos_dir = bundle.join("Contents").join("MacOS");
for name in &["ida", "ida64", "ida32"] {
let p = macos_dir.join(name);
if p.is_file() {
return Some(p);
}
}
if let Ok(entries) = std::fs::read_dir(&macos_dir) {
for entry in entries.flatten() {
if entry.path().is_file() {
return Some(entry.path());
}
}
}
bundle.exists().then(|| bundle.to_path_buf())
}
pub fn launch_ida(ida_bin: &Path, file: Option<&Path>) -> std::io::Result<()> {
let bin_str = ida_bin.to_string_lossy();
if cfg!(target_os = "macos") {
let app_bundle = if let Some(pos) = bin_str.find("/Contents/MacOS/") {
Some(&bin_str[..pos])
} else if bin_str.ends_with(".app") {
Some(bin_str.as_ref())
} else {
None
};
if let Some(bundle) = app_bundle
&& bundle.ends_with(".app") {
let mut cmd = std::process::Command::new("open");
cmd.arg("-n").arg("-a").arg(bundle);
if let Some(f) = file {
cmd.arg("--args").arg(f);
}
cmd.spawn()?;
return Ok(());
}
}
let mut cmd = std::process::Command::new(ida_bin);
if let Some(f) = file {
cmd.arg(f);
}
cmd.spawn()?;
Ok(())
}
#[allow(dead_code)]
pub fn idat_path(install_dir: &Path) -> Option<PathBuf> {
let name = if cfg!(target_os = "windows") {
"idat64.exe"
} else {
"idat64"
};
let p = install_dir.join(name);
if p.exists() {
Some(p)
} else {
let fallback = if cfg!(target_os = "windows") {
"idat.exe"
} else {
"idat"
};
let p = install_dir.join(fallback);
p.exists().then_some(p)
}
}
pub fn find_standard_installations() -> Vec<PathBuf> {
let mut results = Vec::new();
if cfg!(target_os = "macos") {
if let Ok(entries) = std::fs::read_dir("/Applications") {
for entry in entries.flatten() {
let name = entry.file_name();
let name_str = name.to_string_lossy();
if name_str.starts_with("IDA")
&& name_str.ends_with(".app")
&& entry.path().is_dir()
{
results.push(entry.path());
}
}
}
} else if cfg!(target_os = "windows") {
for base in &[r"C:\Program Files", r"C:\Program Files (x86)"] {
if let Ok(entries) = std::fs::read_dir(base) {
for entry in entries.flatten() {
let name = entry.file_name();
if name.to_string_lossy().contains("IDA") && entry.path().is_dir() {
results.push(entry.path());
}
}
}
}
} else {
if let Some(home) = dirs::home_dir()
&& let Ok(entries) = std::fs::read_dir(&home) {
for entry in entries.flatten() {
let name = entry.file_name();
if name.to_string_lossy().starts_with("ida") && entry.path().is_dir() {
results.push(entry.path());
}
}
}
if let Ok(entries) = std::fs::read_dir("/opt") {
for entry in entries.flatten() {
let name = entry.file_name();
if name.to_string_lossy().starts_with("ida") && entry.path().is_dir() {
results.push(entry.path());
}
}
}
}
results.sort();
results
}
pub fn current_install_dir() -> Option<PathBuf> {
let env = Env::global();
if let Some(ref d) = env.current_ida_install_dir {
return Some(PathBuf::from(d));
}
if let Some(ref d) = env.idadir {
return Some(PathBuf::from(d));
}
let config_path = ida_user_dir().join("ida-config.json");
if config_path.exists()
&& let Ok(text) = std::fs::read_to_string(&config_path)
&& let Ok(val) = serde_json::from_str::<serde_json::Value>(&text)
&& let Some(dir) = val
.get("paths")
.and_then(|p| p.get("ida_install_dir"))
.and_then(|v| v.as_str())
{
let path = PathBuf::from(dir);
if path.exists() {
return Some(path);
}
}
None
}