aboutsummaryrefslogtreecommitdiff
path: root/libs/uibase/src/utility.cpp
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-03-12 13:48:25 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-03-12 13:48:25 -0500
commit84a43a2e6afbdfab92c610d47aa75e3876ffacd6 (patch)
treeea624ec287181905441fbf1247c645976bdb8fc9 /libs/uibase/src/utility.cpp
parent3ba283d9350783c038cc2844450f4938ad125f08 (diff)
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 <noreply@anthropic.com>
Diffstat (limited to 'libs/uibase/src/utility.cpp')
-rw-r--r--libs/uibase/src/utility.cpp98
1 files changed, 30 insertions, 68 deletions
diff --git a/libs/uibase/src/utility.cpp b/libs/uibase/src/utility.cpp
index 33f227d..e6811af 100644
--- a/libs/uibase/src/utility.cpp
+++ b/libs/uibase/src/utility.cpp
@@ -21,6 +21,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include <uibase/utility.h>
#include <uibase/log.h>
#include <uibase/report.h>
+#ifndef _WIN32
+#include <nak_ffi.h>
+#endif
#include <QApplication>
#include <QBuffer>
#include <QCollator>
@@ -943,80 +946,39 @@ QIcon iconForExecutable(const QString& filePath)
cache.insert(cacheKey, icon);
return icon;
#else
- // Try to extract icon resources from PE executables via icoutils/wrestool.
- // If this fails, return a generic executable icon.
+ // Extract icon from PE executable via NaK (pelite-based, no external tools).
QIcon icon;
- const QString wrestool = QStandardPaths::findExecutable("wrestool");
- if (!wrestool.isEmpty()) {
- const QString cacheRoot =
- QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
- "/exe_icons";
- QDir().mkpath(cacheRoot);
-
- const QString outDirPath = cacheRoot + "/" + QString::number(qHash(cacheKey), 16);
- QDir outDir(outDirPath);
- if (!outDir.exists()) {
- QDir().mkpath(outDirPath);
- }
-
- auto findBestIcon = [&](const QString& dirPath) -> QIcon {
- QString bestFile;
- int bestArea = -1;
-
- QDirIterator it(dirPath,
- QStringList() << "*.png"
- << "*.ico"
- << "*.bmp",
- QDir::Files, QDirIterator::Subdirectories);
- while (it.hasNext()) {
- const QString path = it.next();
- QImageReader reader(path);
- const QSize size = reader.size();
- const int area = size.width() * size.height();
- if (area > bestArea) {
- bestArea = area;
- bestFile = path;
- } else if (bestArea < 0) {
- // Unknown image size (e.g. multi-size ico); keep first viable file.
- bestFile = path;
- }
- }
-
- if (!bestFile.isEmpty()) {
- QIcon candidate(bestFile);
- if (!candidate.pixmap(24, 24).isNull() || !candidate.pixmap(32, 32).isNull()) {
- return candidate;
- }
- }
-
- return QIcon();
- };
-
- // Reuse previous extraction if present.
- icon = findBestIcon(outDirPath);
-
- if (icon.isNull()) {
- const QList<QStringList> tries = {
- {"-x", "--type=group_icon", "-o", outDirPath, fi.absoluteFilePath()},
- {"-x", "--type=14", "-o", outDirPath, fi.absoluteFilePath()},
- {"-x", "-t14", "-o", outDirPath, fi.absoluteFilePath()},
- };
+ {
+ const QByteArray pathUtf8 = fi.absoluteFilePath().toUtf8();
+ NakIconData icoData = nak_extract_exe_icon(pathUtf8.constData());
+ if (icoData.data && icoData.len > 0) {
+ QByteArray ba(reinterpret_cast<const char*>(icoData.data),
+ static_cast<qsizetype>(icoData.len));
+ QBuffer buf(&ba);
+ buf.open(QIODevice::ReadOnly);
+ QImageReader reader(&buf, "ico");
- for (const auto& args : tries) {
- QProcess p;
- p.start(wrestool, args);
- if (!p.waitForFinished(4000)) {
- p.kill();
- continue;
- }
- if (p.exitStatus() == QProcess::NormalExit && p.exitCode() == 0) {
- icon = findBestIcon(outDirPath);
- if (!icon.isNull()) {
- break;
+ // ICO files contain multiple sizes/depths — read all and add to QIcon
+ // so Qt picks the best one for each requested size.
+ if (reader.canRead()) {
+ const int imageCount = reader.imageCount();
+ if (imageCount > 1) {
+ for (int i = 0; i < imageCount; ++i) {
+ reader.jumpToImage(i);
+ QImage img = reader.read();
+ if (!img.isNull()) {
+ icon.addPixmap(QPixmap::fromImage(img));
+ }
+ }
+ } else {
+ QImage img = reader.read();
+ if (!img.isNull()) {
+ icon = QIcon(QPixmap::fromImage(img));
}
}
}
+ nak_icon_data_free(icoData);
}
}