Skip to content

Commit f9171e2

Browse files
committed
[libromdata,librptexture] EXE_icons, ICO: Check bColorCount if size and bitcount are identical.
Notepad from NT 3.51 (and possibly older NT versions) has two 32x32 4bpp icons: - The first one, with bColorCount == 8, has a greenish color. - The second one, with bColorCount == 16, has a cyan color. The cyan color is the "standard" Notepad icon, so we should check bColorCount in this case and select the one with the higher count. TODO: Consolidate the icon selection code in EXE_icons and ICO?
1 parent 87d48d6 commit f9171e2

File tree

2 files changed

+51
-14
lines changed

2 files changed

+51
-14
lines changed

src/libromdata/Other/EXE_icons.cpp

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* ROM Properties Page shell extension. (libromdata) *
33
* EXE.cpp: DOS/Windows executable reader. (Icon handling) *
44
* *
5-
* Copyright (c) 2016-2025 by David Korth. *
5+
* Copyright (c) 2016-2026 by David Korth. *
66
* SPDX-License-Identifier: GPL-2.0-or-later *
77
***************************************************************************/
88

@@ -211,6 +211,7 @@ rp::uvector<uint8_t> EXEPrivate::loadIconResourceData(int iconindex, int width,
211211
// Current icon
212212
uint16_t cur_id = 0;
213213
uint16_t cur_bitCount = 0;
214+
uint8_t cur_bColorCount = 0;
214215
int cur_w = 0, cur_h = 0;
215216
for (unsigned int i = 0; i < iconCount; i++) {
216217
GRPICONDIRENTRY iconDirEntry;
@@ -225,26 +226,32 @@ rp::uvector<uint8_t> EXEPrivate::loadIconResourceData(int iconindex, int width,
225226
const int new_w = (iconDirEntry.bWidth != 0) ? iconDirEntry.bWidth : 256;
226227
const int new_h = (iconDirEntry.bHeight != 0) ? iconDirEntry.bHeight : 256;
227228
const uint16_t new_bitCount = le16_to_cpu(iconDirEntry.wBitCount);
229+
const uint8_t new_bColorCount = iconDirEntry.bColorCount;
228230

229231
// Going by size first. Higher bitcount is only a differentiation for identical sizes.
232+
// TODO: Non-square icon handling.
233+
bool icon_is_better = false;
230234
if (new_w > cur_w || new_h > cur_h) {
231235
// New icon is bigger.
236+
// FIXME: What's the second size check for?
232237
if ((cur_w == 0 && cur_h == 0) || (new_w < width && new_h < height)) {
233238
// New icon is smaller than the previous icon.
234-
cur_id = le16_to_cpu(iconDirEntry.nID);
235-
cur_bitCount = new_bitCount;
236-
cur_w = new_w;
237-
cur_h = new_h;
239+
icon_is_better = true;
238240
}
239241
} else if (new_w == cur_w && new_h == cur_h) {
240242
// Size is identical.
241243
// If the bit count is higher, use this icon.
242244
if (new_bitCount > cur_bitCount) {
243245
// New icon has a higher color depth than the previous icon.
244-
cur_id = le16_to_cpu(iconDirEntry.nID);
245-
cur_bitCount = new_bitCount;
246-
cur_w = new_w;
247-
cur_h = new_h;
246+
icon_is_better = true;
247+
} else if (new_bitCount == cur_bitCount) {
248+
// NOTE: Notepad from NT 3.51 has *two* 32x32 4bpp icons.
249+
// The first one (greenish color) has bColorCount == 8.
250+
// The second one (cyan color) has bColorCount == 16.
251+
if (new_bColorCount > cur_bColorCount) {
252+
// Color count is higher.
253+
icon_is_better = true;
254+
}
248255
}
249256
} else if (new_w < cur_w || new_h < cur_h) {
250257
// New icon is smaller.
@@ -253,12 +260,17 @@ rp::uvector<uint8_t> EXEPrivate::loadIconResourceData(int iconindex, int width,
253260
(abs(new_h - height) < abs(cur_h - height)))
254261
{
255262
// New icon is closer to the requested icon size.
256-
cur_id = le16_to_cpu(iconDirEntry.nID);
257-
cur_bitCount = new_bitCount;
258-
cur_w = new_w;
259-
cur_h = new_h;
263+
icon_is_better = true;
260264
}
261265
}
266+
267+
if (icon_is_better) {
268+
cur_id = le16_to_cpu(iconDirEntry.nID);
269+
cur_bitCount = new_bitCount;
270+
cur_bColorCount = new_bColorCount;
271+
cur_w = new_w;
272+
cur_h = new_h;
273+
}
262274
}
263275

264276
if (cur_id == 0) {

src/librptexture/fileformat/ICO.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ using namespace LibRpFile;
2828
using std::array;
2929
using std::map;
3030
using std::string;
31+
using std::unique_ptr;
3132
using std::vector;
3233

3334
// Uninitialized vector class
@@ -434,6 +435,10 @@ int ICOPrivate::loadIconDirectory_Win3(void)
434435
return -ENOENT;
435436
}
436437

438+
// bColorCount values for disambiguation if multiple icons have the
439+
// same size and bitcount. (See Notepad from NT 3.51.)
440+
unique_ptr<uint8_t[]> bColorCounts(new uint8_t[count]);
441+
437442
if (dir.is_res) {
438443
// Icon/cursor resource from a Windows executable.
439444

@@ -475,6 +480,11 @@ int ICOPrivate::loadIconDirectory_Win3(void)
475480
return -EIO;
476481
}
477482
}
483+
484+
// Get the bColorCount values.
485+
for (size_t i = 0; i < count; i++) {
486+
bColorCounts[i] = iconDirectory[i].bColorCount;
487+
}
478488
} else {
479489
// Standalone .ico/.cur file.
480490
const size_t fullsize = count * sizeof(ICONDIRENTRY);
@@ -500,11 +510,17 @@ int ICOPrivate::loadIconDirectory_Win3(void)
500510
return -EIO;
501511
}
502512
}
513+
514+
// Get the bColorCount values.
515+
for (size_t i = 0; i < count; i++) {
516+
bColorCounts[i] = iconDirectory[i].bColorCount;
517+
}
503518
}
504519

505520
// Go through the icon bitmap headers and figure out the "best" one.
506521
int width_best = 0, height_best = 0;
507-
unsigned int bitcount_best = 0;
522+
uint16_t bitcount_best = 0;
523+
uint8_t bColorCount_best = 0;
508524
int bestIcon_idx = -1;
509525
for (unsigned int i = 0; i < count; i++) {
510526
// Get the width, height, and color depth from this bitmap header.
@@ -527,6 +543,14 @@ int ICOPrivate::loadIconDirectory_Win3(void)
527543
if (data.bitcount > bitcount_best) {
528544
// Color depth is higher.
529545
icon_is_better = true;
546+
} else if (data.bitcount == bitcount_best) {
547+
// NOTE: Notepad from NT 3.51 has *two* 32x32 4bpp icons.
548+
// The first one (greenish color) has bColorCount == 8.
549+
// The second one (cyan color) has bColorCount == 16.
550+
if (bColorCounts[i] > bColorCount_best) {
551+
// Color count is higher.
552+
icon_is_better = true;
553+
}
530554
}
531555
}
532556

@@ -536,6 +560,7 @@ int ICOPrivate::loadIconDirectory_Win3(void)
536560
width_best = data.width;
537561
height_best = data.height;
538562
bitcount_best = data.bitcount;
563+
bColorCount_best = bColorCounts[i];
539564
}
540565
}
541566

0 commit comments

Comments
 (0)