rufus/src/icon.c

197 lines
6.2 KiB
C

/*
* Rufus: The Reliable USB Formatting Utility
* Extract icon from executable and set autorun.inf
* Copyright © 2012-2024 Pete Batard <pete@akeo.ie>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */
#ifdef _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "rufus.h"
#include "missing.h"
#include "resource.h"
#include "msapi_utf8.h"
#pragma pack(push)
#pragma pack(2)
typedef struct
{
BYTE bWidth; // Width, in pixels, of the image
BYTE bHeight; // Height, in pixels, of the image
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved ( must be 0)
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // How many bytes in this resource?
DWORD dwImageOffset; // Where in the file is this image?
} ICONDIRENTRY, *LPICONDIRENTRY;
typedef struct
{
WORD idReserved; // Reserved (must be 0)
WORD idType; // Resource Type (1 for icons)
WORD idCount; // How many images?
ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
} ICONDIR, *LPICONDIR;
typedef struct
{
BITMAPINFOHEADER icHeader; // DIB header
RGBQUAD icColors[1]; // Color table
BYTE icXOR[1]; // DIB bits for XOR mask
BYTE icAND[1]; // DIB bits for AND mask
} ICONIMAGE, *LPICONIMAGE;
typedef struct
{
BYTE bWidth; // Width, in pixels, of the image
BYTE bHeight; // Height, in pixels, of the image
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // how many bytes in this resource?
WORD nID; // the ID
} GRPICONDIRENTRY, *LPGRPICONDIRENTRY;
typedef struct
{
WORD idReserved; // Reserved (must be 0)
WORD idType; // Resource type (1 for icons)
WORD idCount; // How many images?
GRPICONDIRENTRY idEntries[1]; // The entries for each image
} GRPICONDIR, *LPGRPICONDIR;
#pragma pack(pop)
/*
* Extract the main icon set from the exe
*/
BOOL ExtractAppIcon(const char* path, BOOL bSilent)
{
HGLOBAL res_handle;
HRSRC res;
WORD i;
BYTE* res_data;
DWORD res_size, offset;
HANDLE hFile = INVALID_HANDLE_VALUE;
BOOL r = FALSE;
GRPICONDIR* icondir;
icondir = (GRPICONDIR*)GetResource(hMainInstance, MAKEINTRESOURCEA(IDI_ICON), _RT_GROUP_ICON, "icon", &res_size, FALSE);
assert(icondir != NULL && icondir->idCount <= 64);
if (icondir == NULL || icondir->idCount > 64)
goto out;
hFile = CreateFileU(path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
uprintf("Unable to create icon '%s': %s.", path, WindowsErrorString());
goto out;
}
// Write .ico header
if (!WriteFileWithRetry(hFile, icondir, 3 * sizeof(WORD), NULL, WRITE_RETRIES)) {
uprintf("Could not write icon header: %s.", WindowsErrorString());
goto out;
}
// Write icon data
offset = 3 * sizeof(WORD) + icondir->idCount * sizeof(ICONDIRENTRY);
for (i = 0; i < icondir->idCount; i++) {
// Write the common part of ICONDIRENTRY
if (!WriteFileWithRetry(hFile, &icondir->idEntries[i], sizeof(GRPICONDIRENTRY)-sizeof(WORD), NULL, WRITE_RETRIES)) {
uprintf("Could not write ICONDIRENTRY[%d]: %s.", i, WindowsErrorString());
goto out;
}
res = FindResourceA(hMainInstance, MAKEINTRESOURCEA(icondir->idEntries[i].nID), _RT_ICON);
// Write the DWORD offset
if (!WriteFileWithRetry(hFile, &offset, sizeof(offset), NULL, WRITE_RETRIES)) {
uprintf("Could not write ICONDIRENTRY[%d] offset: %s.", i, WindowsErrorString());
goto out;
}
offset += SizeofResource(NULL, res);
}
for (i = 0; i < icondir->idCount; i++) {
// Write icon data
res = FindResourceA(hMainInstance, MAKEINTRESOURCEA(icondir->idEntries[i].nID), _RT_ICON);
res_handle = LoadResource(NULL, res);
res_data = (BYTE*)LockResource(res_handle);
res_size = SizeofResource(NULL, res);
if (!WriteFileWithRetry(hFile, res_data, res_size, NULL, WRITE_RETRIES)) {
uprintf("Could not write icon data #%d: %s.", i, WindowsErrorString());
goto out;
}
}
suprintf("Created: %s", path);
r = TRUE;
out:
safe_closehandle(hFile);
return r;
}
/*
* Create an autorun.inf, if none exists
* We use this to set the icon as well as labels that are longer than 11/32 chars or,
* in the case of FAT, contain non-English characters
*/
BOOL SetAutorun(const char* path)
{
FILE* fd;
char filename[64];
wchar_t wlabel[128], wRufusVersion[32];
static_sprintf(filename, "%sautorun.inf", path);
fd = fopen(filename, "r"); // If there's an existing autorun, don't overwrite
if (fd != NULL) {
uprintf("%s already exists - keeping it", filename);
fclose(fd);
return FALSE;
}
// No "/autorun.inf" => create a new one in UTF-16 LE mode
fd = fopen(filename, "w, ccs=UTF-16LE");
if (fd == NULL) {
uprintf("Unable to create %s", filename);
uprintf("NOTE: This may be caused by a poorly designed security solution. See https://goo.gl/QTobxX.");
return FALSE;
}
GetWindowTextW(hLabel, wlabel, ARRAYSIZE(wlabel));
GetWindowTextW(hMainDialog, wRufusVersion, ARRAYSIZE(wRufusVersion));
// coverity[invalid_type]
fwprintf_s(fd, L"; Created by %s\n; " LTEXT(RUFUS_URL) L"\n", wRufusVersion);
// coverity[invalid_type]
fwprintf_s(fd, L"[autorun]\nicon = autorun.ico\nlabel = %s\n", wlabel);
fclose(fd);
uprintf("Created: %s", filename);
// .inf -> .ico
assert(strlen(filename) >= 2);
filename[strlen(filename) - 1] = 'o';
filename[strlen(filename) - 2] = 'c';
return ExtractAppIcon(filename, FALSE);
}