From 84a43a2e6afbdfab92c610d47aa75e3876ffacd6 Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Thu, 12 Mar 2026 13:48:25 -0500 Subject: Replace wrestool with native Rust PE icon extraction (pelite) - Add icons.rs to nak crate: parses PE resource sections, extracts RT_GROUP_ICON/RT_ICON and builds ICO files in memory - Expose nak_extract_exe_icon() FFI for C++ consumption - Update iconForExecutable() to use Rust FFI instead of shelling out to wrestool; reads all ICO entries for correct color depth - Fix BasicGamePlugin::gameIcon() to call iconForExecutable() (was returning empty QIcon, causing placeholder icons for BG3/Cyberpunk) - Move app icon setup: setDesktopFileName + setWindowIcon to MOApplication constructor (fixes Wayland taskbar/decoration icon) - Move data/com.fluorine.* to data/icons/, install to XDG paths at launch for Wayland compositor icon resolution - Remove icoutils dependency from Docker image and build output Co-Authored-By: Claude Opus 4.6 --- libs/nak_ffi/CMakeLists.txt | 2 ++ libs/nak_ffi/Cargo.lock | 63 ++++++++++++++++++++++++++++++++++++++++++ libs/nak_ffi/include/nak_ffi.h | 17 ++++++++++++ libs/nak_ffi/src/lib.rs | 51 ++++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+) (limited to 'libs/nak_ffi') diff --git a/libs/nak_ffi/CMakeLists.txt b/libs/nak_ffi/CMakeLists.txt index 6fde501..bdfac9a 100644 --- a/libs/nak_ffi/CMakeLists.txt +++ b/libs/nak_ffi/CMakeLists.txt @@ -18,6 +18,8 @@ add_custom_command( WORKING_DIRECTORY ${NAK_FFI_DIR} COMMENT "Building NaK FFI library (Rust)" DEPENDS ${NAK_FFI_DIR}/Cargo.toml ${NAK_FFI_DIR}/src/lib.rs + ${NAK_FFI_DIR}/../nak/Cargo.toml ${NAK_FFI_DIR}/../nak/src/lib.rs + ${NAK_FFI_DIR}/../nak/src/icons.rs ) add_custom_target(nak_ffi_build DEPENDS ${NAK_FFI_LIB}) diff --git a/libs/nak_ffi/Cargo.lock b/libs/nak_ffi/Cargo.lock index 55ab8db..e8be402 100644 --- a/libs/nak_ffi/Cargo.lock +++ b/libs/nak_ffi/Cargo.lock @@ -79,6 +79,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "dataview" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daba87f72c730b508641c9fb6411fc9bba73939eed2cab611c399500511880d0" +dependencies = [ + "derive_pod", +] + +[[package]] +name = "derive_pod" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ea6706d74fca54e15f1d40b5cf7fe7f764aaec61352a9fcec58fe27e042fc8" + [[package]] name = "displaydoc" version = "0.2.5" @@ -314,12 +329,19 @@ name = "nak_rust" version = "0.1.0" dependencies = [ "chrono", + "pelite", "serde", "serde_json", "ureq", "walkdir", ] +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + [[package]] name = "num-traits" version = "0.2.19" @@ -335,6 +357,25 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "pelite" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88dccf4bd32294364aeb7bd55d749604450e9db54605887551f21baea7617685" +dependencies = [ + "dataview", + "libc", + "no-std-compat", + "pelite-macros", + "winapi", +] + +[[package]] +name = "pelite-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a7cf3f8ecebb0f4895f4892a8be0a0dc81b498f9d56735cb769dc31bf00815b" + [[package]] name = "percent-encoding" version = "2.3.2" @@ -662,6 +703,22 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" @@ -671,6 +728,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.62.2" diff --git a/libs/nak_ffi/include/nak_ffi.h b/libs/nak_ffi/include/nak_ffi.h index cc66d9e..e1a9185 100644 --- a/libs/nak_ffi/include/nak_ffi.h +++ b/libs/nak_ffi/include/nak_ffi.h @@ -175,6 +175,23 @@ char *nak_ensure_dxvk_conf(void); * Returns newly allocated string (free with nak_string_free). */ char *nak_get_dxvk_conf_path(void); +/* ======================================================================== + * Tier 8: PE Icon Extraction + * ======================================================================== */ + +/** Result of icon extraction */ +typedef struct { + uint8_t *data; /**< Raw ICO file bytes (NULL if extraction failed) */ + size_t len; /**< Length in bytes (0 if extraction failed) */ +} NakIconData; + +/** Extract the best icon from a Windows PE executable (.exe/.dll). + * Returns raw ICO bytes. Free with nak_icon_data_free(). */ +NakIconData nak_extract_exe_icon(const char *exe_path); + +/** Free icon data returned by nak_extract_exe_icon */ +void nak_icon_data_free(NakIconData icon); + /* ======================================================================== * General * ======================================================================== */ diff --git a/libs/nak_ffi/src/lib.rs b/libs/nak_ffi/src/lib.rs index 65592fa..c45481b 100644 --- a/libs/nak_ffi/src/lib.rs +++ b/libs/nak_ffi/src/lib.rs @@ -614,6 +614,57 @@ pub extern "C" fn nak_get_dxvk_conf_path() -> *mut c_char { to_cstring(&path.to_string_lossy()) } +// ============================================================================ +// Tier 8: PE Icon Extraction +// ============================================================================ + +/// Result of icon extraction +#[repr(C)] +pub struct NakIconData { + /// Raw ICO file bytes (caller must free with nak_icon_data_free) + pub data: *mut u8, + /// Length of the data in bytes (0 if extraction failed) + pub len: usize, +} + +/// Extract the best icon from a Windows PE executable (.exe/.dll). +/// +/// Returns NakIconData with the raw ICO file bytes. +/// If extraction fails, data is null and len is 0. +/// Caller must free the result with nak_icon_data_free(). +#[no_mangle] +pub unsafe extern "C" fn nak_extract_exe_icon(exe_path: *const c_char) -> NakIconData { + let path_str = unsafe { from_cstr(exe_path) }; + if path_str.is_empty() { + return NakIconData { + data: ptr::null_mut(), + len: 0, + }; + } + + match nak_rust::icons::extract_icon(std::path::Path::new(path_str)) { + Some(bytes) => { + let len = bytes.len(); + let mut boxed = bytes.into_boxed_slice(); + let ptr = boxed.as_mut_ptr(); + std::mem::forget(boxed); + NakIconData { data: ptr, len } + } + None => NakIconData { + data: ptr::null_mut(), + len: 0, + }, + } +} + +/// Free icon data returned by nak_extract_exe_icon +#[no_mangle] +pub unsafe extern "C" fn nak_icon_data_free(icon: NakIconData) { + if !icon.data.is_null() && icon.len > 0 { + let _ = unsafe { Vec::from_raw_parts(icon.data, icon.len, icon.len) }; + } +} + // ============================================================================ // General: String free // ============================================================================ -- cgit v1.3.1