use crate::utils::to_wstring;
use windows_sys::Win32::System::Registry::{
RegCreateKeyExW, RegDeleteTreeW, RegSetValueExW, RegCloseKey, RegOpenKeyExW,
HKEY, HKEY_CLASSES_ROOT, KEY_WRITE, KEY_READ, REG_SZ, REG_OPTION_NON_VOLATILE,
};
use windows_sys::Win32::Foundation::ERROR_SUCCESS;
fn get_exe_path() -> Option<String> {
std::env::current_exe().ok()?.to_str().map(|s| s.to_string())
}
unsafe fn create_key(parent: HKEY, subkey: &str) -> Result<HKEY, String> { unsafe {
let wide_subkey = to_wstring(subkey);
let mut hkey: HKEY = std::ptr::null_mut();
let mut disposition = 0u32;
let result = RegCreateKeyExW(
parent,
wide_subkey.as_ptr(),
0,
std::ptr::null(),
REG_OPTION_NON_VOLATILE,
KEY_WRITE,
std::ptr::null(),
&mut hkey,
&mut disposition,
);
if result != ERROR_SUCCESS {
return Err("RegCreateKeyExW failed: ".to_string() + &result.to_string());
}
Ok(hkey)
}}
unsafe fn set_value(hkey: HKEY, name: Option<&str>, value: &str) -> Result<(), String> { unsafe {
let wide_value = to_wstring(value);
let value_bytes_ptr = wide_value.as_ptr() as *const u8;
let value_size = (wide_value.len() * 2) as u32;
if let Some(name) = name {
let wide_name = to_wstring(name);
let result = RegSetValueExW(
hkey,
wide_name.as_ptr(),
0,
REG_SZ,
value_bytes_ptr,
value_size,
);
if result != ERROR_SUCCESS {
return Err("RegSetValueExW (named) failed: ".to_string() + &result.to_string());
}
} else {
let result = RegSetValueExW(
hkey,
std::ptr::null(),
0,
REG_SZ,
value_bytes_ptr,
value_size,
);
if result != ERROR_SUCCESS {
return Err("RegSetValueExW (default) failed: ".to_string() + &result.to_string());
}
}
Ok(())
}}
unsafe fn close_key(hkey: HKEY) { unsafe {
RegCloseKey(hkey);
}}
unsafe fn create_menu_for_root(root_path: &str, exe_path: &str) -> Result<(), String> { unsafe {
let shell_key_path = root_path.to_string() + "\\shell\\CompactRS";
let main_key = create_key(HKEY_CLASSES_ROOT, &shell_key_path)?;
set_value(main_key, Some("MUIVerb"), "CompactRS")?;
set_value(main_key, Some("Icon"), exe_path)?;
set_value(main_key, Some("SubCommands"), "")?;
close_key(main_key);
let submenu_base = root_path.to_string() + "\\shell\\CompactRS\\shell";
let menu_items = [
("01_xpress4k", "Compress as XPRESS4K", "--algo xpress4k"),
("02_xpress8k", "Compress as XPRESS8K", "--algo xpress8k"),
("03_xpress16k", "Compress as XPRESS16K", "--algo xpress16k"),
("04_lzx", "Compress as LZX", "--algo lzx"),
("05_decompress", "Decompress", "--action decompress"),
];
for (id, label, args) in menu_items {
let item_path = submenu_base.clone() + "\\" + id;
let item_key = create_key(HKEY_CLASSES_ROOT, &item_path)?;
set_value(item_key, None, label)?;
close_key(item_key);
let cmd_path = submenu_base.clone() + "\\" + id + "\\command";
let cmd_key = create_key(HKEY_CLASSES_ROOT, &cmd_path)?;
let command = "\"".to_string() + exe_path + "\" --path \"%1\" " + args;
set_value(cmd_key, None, &command)?;
close_key(cmd_key);
}
Ok(())
}}
unsafe fn delete_menu_for_root(root_path: &str) -> Result<(), String> { unsafe {
let key_path = root_path.to_string() + "\\shell\\CompactRS";
let wide_path = to_wstring(&key_path);
let _ = RegDeleteTreeW(HKEY_CLASSES_ROOT, wide_path.as_ptr());
Ok(())
}}
pub fn register_context_menu() -> Result<(), String> {
let exe_path = get_exe_path().ok_or_else(|| {
"Failed to get current executable path".to_string()
})?;
unsafe {
create_menu_for_root("*", &exe_path)?;
create_menu_for_root("Directory", &exe_path)?;
}
Ok(())
}
pub fn unregister_context_menu() -> Result<(), String> {
unsafe {
delete_menu_for_root("*")?;
delete_menu_for_root("Directory")?;
}
Ok(())
}
pub fn is_context_menu_registered() -> bool {
let key_path = to_wstring("*\\shell\\CompactRS");
let mut hkey: HKEY = std::ptr::null_mut();
unsafe {
let result = RegOpenKeyExW(
HKEY_CLASSES_ROOT,
key_path.as_ptr(),
0,
KEY_READ,
&mut hkey,
);
if result == ERROR_SUCCESS {
RegCloseKey(hkey);
true
} else {
false
}
}
}