File indexing completed on 2024-04-28 04:58:03

0001 /*
0002     icoutils_win.cpp - Extract Microsoft Window icons and images using icoutils package
0003 
0004     SPDX-FileCopyrightText: 2013 Andrius da Costa Ribas <andriusmao@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "icoutils.h"
0010 
0011 #include <windows.h>
0012 
0013 #include <QBuffer>
0014 #include <QDir>
0015 #include <QFile>
0016 #include <QList>
0017 
0018 extern "C" {
0019 // icon structs, as per https://msdn.microsoft.com/en-us/library/ms997538.aspx
0020 #pragma pack(push)
0021 #pragma pack(2)
0022 typedef struct {
0023     BYTE bWidth; // Width, in pixels, of the image
0024     BYTE bHeight; // Height, in pixels, of the image
0025     BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
0026     BYTE bReserved; // Reserved ( must be 0)
0027     WORD wPlanes; // Color Planes
0028     WORD wBitCount; // Bits per pixel
0029     DWORD dwBytesInRes; // How many bytes in this resource?
0030     DWORD dwImageOffset; // Where in the file is this image?
0031 } ICONDIRENTRY, *LPICONDIRENTRY;
0032 
0033 typedef struct {
0034     WORD idReserved; // Reserved (must be 0)
0035     WORD idType; // Resource Type (1 for icons)
0036     WORD idCount; // How many images?
0037     ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
0038 } ICONDIR, *LPICONDIR;
0039 
0040 typedef struct {
0041     BYTE bWidth; // Width, in pixels, of the image
0042     BYTE bHeight; // Height, in pixels, of the image
0043     BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
0044     BYTE bReserved; // Reserved
0045     WORD wPlanes; // Color Planes
0046     WORD wBitCount; // Bits per pixel
0047     DWORD dwBytesInRes; // how many bytes in this resource?
0048     WORD nID; // the ID
0049 } GRPICONDIRENTRY, *LPGRPICONDIRENTRY;
0050 
0051 typedef struct {
0052     WORD idReserved; // Reserved (must be 0)
0053     WORD idType; // Resource type (1 for icons)
0054     WORD idCount; // How many images?
0055     GRPICONDIRENTRY idEntries[1]; // The entries for each image
0056 } GRPICONDIR, *LPGRPICONDIR;
0057 #pragma pack(pop)
0058 }
0059 
0060 BOOL CALLBACK enumResNameCallback(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
0061 {
0062     QList<LPTSTR> *iconResources = (QList<LPTSTR> *)lParam;
0063     LPTSTR copyString;
0064     if (IS_INTRESOURCE(lpszName)) {
0065         copyString = lpszName;
0066     } else {
0067         copyString = new WCHAR[lstrlen(lpszName) + 1];
0068         lstrcpy(copyString, lpszName);
0069     }
0070 
0071     if (lpszType == RT_GROUP_ICON) {
0072         (*iconResources) << copyString;
0073     }
0074     return TRUE;
0075 }
0076 
0077 bool IcoUtils::loadIcoImageFromExe(const QString &inputFileName, QIODevice *outputDevice)
0078 {
0079     HMODULE hModule;
0080     LPCTSTR fileName;
0081     QList<LPTSTR> iconResources;
0082 
0083     fileName = (TCHAR *)QDir::toNativeSeparators(inputFileName).utf16();
0084 
0085     hModule = LoadLibraryEx(fileName, 0, LOAD_LIBRARY_AS_DATAFILE);
0086 
0087     if (!hModule) {
0088         return false;
0089     }
0090 
0091     EnumResourceNames(hModule, RT_GROUP_ICON, enumResNameCallback, (LONG_PTR)&iconResources);
0092 
0093     if (!iconResources.isEmpty()) {
0094         HRSRC resourceInfo = FindResourceW(hModule, (LPCTSTR)iconResources.at(0), RT_GROUP_ICON);
0095         if (resourceInfo == 0) {
0096             FreeLibrary(hModule);
0097             return false;
0098         }
0099 
0100         // we can get rid of the iconResources list now
0101         for (LPTSTR iconRes : qAsConst(iconResources)) {
0102             if (!IS_INTRESOURCE(iconRes)) {
0103                 delete[] iconRes;
0104             }
0105         }
0106 
0107         HGLOBAL resourceData = LoadResource(hModule, resourceInfo);
0108         LPVOID resourcePointer = LockResource(resourceData);
0109         int resourceSize = SizeofResource(hModule, resourceInfo);
0110         if (resourceSize == 0) {
0111             FreeLibrary(hModule);
0112             return false;
0113         }
0114 
0115         LPGRPICONDIR grpIconDir = (LPGRPICONDIR)resourcePointer;
0116 
0117         QBuffer outBuffer;
0118         outBuffer.open(QIODevice::ReadWrite);
0119 
0120         // helper
0121         const int iconDirHeaderSize = sizeof(grpIconDir->idReserved) + sizeof(grpIconDir->idType) + sizeof(grpIconDir->idCount);
0122 
0123         // copy the common part of GRPICONDIR and ICONDIR structures to the file
0124         outBuffer.write((char *)resourcePointer, iconDirHeaderSize);
0125 
0126         DWORD imageOffset = iconDirHeaderSize + (grpIconDir->idCount) * sizeof(ICONDIRENTRY);
0127 
0128         for (int i = 0; i < (grpIconDir->idCount); ++i) {
0129             // copy the common part of GRPICONDIRENTRY and ICONDIRENTRY structures to the respective ICONDIRENTRY's position
0130             LPGRPICONDIRENTRY grpIconDirEntry = &(grpIconDir->idEntries[i]);
0131             outBuffer.seek(iconDirHeaderSize + i * sizeof(ICONDIRENTRY));
0132             outBuffer.write((char *)grpIconDirEntry, sizeof(GRPICONDIRENTRY) - sizeof(grpIconDirEntry->nID));
0133 
0134             // now, instead of nID, write the image offset
0135             outBuffer.write((char *)&imageOffset, sizeof(DWORD));
0136 
0137             // find the icon resource
0138             resourceInfo = FindResourceW(hModule, MAKEINTRESOURCE(grpIconDirEntry->nID), RT_ICON);
0139             if (resourceInfo == 0) {
0140                 FreeLibrary(hModule);
0141                 return false;
0142             }
0143             resourceData = LoadResource(hModule, resourceInfo);
0144             resourcePointer = LockResource(resourceData);
0145             resourceSize = SizeofResource(hModule, resourceInfo);
0146             if (resourceSize == 0) {
0147                 FreeLibrary(hModule);
0148                 return false;
0149             }
0150 
0151             // seek to imageOffset
0152             outBuffer.seek(imageOffset);
0153             // write the icon data
0154             outBuffer.write((char *)resourcePointer, resourceSize);
0155             // increment imageOffset
0156             imageOffset += resourceSize;
0157         }
0158 
0159         const bool ok = (outputDevice->write(outBuffer.data()) == outBuffer.size());
0160 
0161         FreeLibrary(hModule);
0162         return ok;
0163     }
0164 
0165     FreeLibrary(hModule);
0166     return false;
0167 }