diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-03-12 13:48:25 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-03-12 13:48:25 -0500 |
| commit | 84a43a2e6afbdfab92c610d47aa75e3876ffacd6 (patch) | |
| tree | ea624ec287181905441fbf1247c645976bdb8fc9 /libs/uibase/src/utility.cpp | |
| parent | 3ba283d9350783c038cc2844450f4938ad125f08 (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.cpp | 98 |
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); } } |
