/src/kio-extras/thumbnail/exeutils.cpp
Line | Count | Source |
1 | | /* |
2 | | exeutils.cpp - Extract Microsoft Window icons from Microsoft Windows executables |
3 | | |
4 | | SPDX-FileCopyrightText: 2023 John Chadwick <john@jchw.io> |
5 | | |
6 | | SPDX-License-Identifier: LGPL-2.0-or-later OR BSD-2-Clause |
7 | | */ |
8 | | |
9 | | #include "exeutils.h" |
10 | | |
11 | | #include <QDataStream> |
12 | | #include <QIODevice> |
13 | | #include <QMap> |
14 | | #include <QVector> |
15 | | |
16 | | #include <optional> |
17 | | |
18 | | namespace |
19 | | { |
20 | | |
21 | | // Executable file (.exe) |
22 | | struct DosHeader { |
23 | | char signature[2]; |
24 | | quint32 newHeaderOffset; |
25 | | }; |
26 | | |
27 | | QDataStream &operator>>(QDataStream &s, DosHeader &v) |
28 | 3.69k | { |
29 | 3.69k | s.readRawData(v.signature, sizeof(v.signature)); |
30 | 3.69k | s.device()->skip(58); |
31 | 3.69k | s >> v.newHeaderOffset; |
32 | 3.69k | return s; |
33 | 3.69k | } |
34 | | |
35 | | // Resource Directory |
36 | | enum class ResourceType : quint32 { |
37 | | Icon = 3, |
38 | | GroupIcon = 14, |
39 | | }; |
40 | | |
41 | | struct RtGroupIconDirectory { |
42 | | quint16 reserved; |
43 | | quint16 type; |
44 | | quint16 count; |
45 | | }; |
46 | | |
47 | | struct RtGroupIconDirectoryEntry { |
48 | | quint8 width; |
49 | | quint8 height; |
50 | | quint8 colorCount; |
51 | | quint8 reserved; |
52 | | quint16 numPlanes; |
53 | | quint16 bpp; |
54 | | quint32 size; |
55 | | quint16 resourceId; |
56 | | }; |
57 | | |
58 | | QDataStream &operator>>(QDataStream &s, RtGroupIconDirectory &v) |
59 | 1.64k | { |
60 | 1.64k | s >> v.reserved >> v.type >> v.count; |
61 | 1.64k | return s; |
62 | 1.64k | } |
63 | | |
64 | | QDataStream &operator>>(QDataStream &s, RtGroupIconDirectoryEntry &v) |
65 | 3.51M | { |
66 | 3.51M | s >> v.width >> v.height >> v.colorCount >> v.reserved >> v.numPlanes >> v.bpp >> v.size >> v.resourceId; |
67 | 3.51M | return s; |
68 | 3.51M | } |
69 | | |
70 | | // Icon file (.ico) |
71 | | struct IconDir { |
72 | | quint16 reserved; |
73 | | quint16 type; |
74 | | quint16 count; |
75 | | }; |
76 | | |
77 | | constexpr int IconDirSize = 6; |
78 | | |
79 | | struct IconDirEntry { |
80 | | quint8 width; |
81 | | quint8 height; |
82 | | quint8 colorCount; |
83 | | quint8 reserved; |
84 | | quint16 numPlanes; |
85 | | quint16 bpp; |
86 | | quint32 size; |
87 | | quint32 imageOffset; |
88 | | |
89 | | IconDirEntry(const RtGroupIconDirectoryEntry &entry, quint32 dataOffset) |
90 | 3.51M | { |
91 | 3.51M | width = entry.width; |
92 | 3.51M | height = entry.height; |
93 | 3.51M | colorCount = entry.colorCount; |
94 | 3.51M | reserved = entry.reserved; |
95 | 3.51M | numPlanes = entry.numPlanes; |
96 | 3.51M | bpp = entry.bpp; |
97 | 3.51M | size = entry.size; |
98 | 3.51M | imageOffset = dataOffset; |
99 | 3.51M | } |
100 | | }; |
101 | | |
102 | | constexpr int IconDirEntrySize = 16; |
103 | | |
104 | | QDataStream &operator<<(QDataStream &s, const IconDir &v) |
105 | 1.64k | { |
106 | 1.64k | s << v.reserved << v.type << v.count; |
107 | 1.64k | return s; |
108 | 1.64k | } |
109 | | |
110 | | QDataStream &operator<<(QDataStream &s, const IconDirEntry &v) |
111 | 3.51M | { |
112 | 3.51M | s << v.width << v.height << v.colorCount << v.reserved << v.numPlanes << v.bpp << v.size << v.imageOffset; |
113 | 3.51M | return s; |
114 | 3.51M | } |
115 | | |
116 | | // Win16 New Executable |
117 | | |
118 | | struct NeFileHeader { |
119 | | quint16 offsetOfResourceTable; |
120 | | quint16 numberOfResourceSegments; |
121 | | }; |
122 | | |
123 | | struct NeResource { |
124 | | quint16 dataOffsetShifted; |
125 | | quint16 dataLength; |
126 | | quint16 flags; |
127 | | quint16 resourceId; |
128 | | quint16 resource[2]; |
129 | | }; |
130 | | |
131 | | struct NeResourceTable { |
132 | | struct Type { |
133 | | quint16 typeId; |
134 | | quint16 numResources; |
135 | | quint16 resource[2]; |
136 | | QVector<NeResource> resources; |
137 | | }; |
138 | | |
139 | | quint16 alignmentShiftCount; |
140 | | QMap<ResourceType, Type> types; |
141 | | }; |
142 | | |
143 | | QDataStream &operator>>(QDataStream &s, NeFileHeader &v) |
144 | 1.65k | { |
145 | 1.65k | s.device()->skip(34); |
146 | 1.65k | s >> v.offsetOfResourceTable; |
147 | 1.65k | s.device()->skip(14); |
148 | 1.65k | s >> v.numberOfResourceSegments; |
149 | 1.65k | return s; |
150 | 1.65k | } |
151 | | |
152 | | QDataStream &operator>>(QDataStream &s, NeResource &v) |
153 | 11.3M | { |
154 | 11.3M | s >> v.dataOffsetShifted >> v.dataLength >> v.flags >> v.resourceId >> v.resource[0] >> v.resource[1]; |
155 | 11.3M | v.resourceId ^= 0x8000; |
156 | 11.3M | return s; |
157 | 11.3M | } |
158 | | |
159 | | QDataStream &operator>>(QDataStream &s, NeResourceTable::Type &v) |
160 | 10.5k | { |
161 | 10.5k | s >> v.typeId; |
162 | 10.5k | if (v.typeId == 0) { |
163 | 1.65k | return s; |
164 | 1.65k | } |
165 | 8.92k | s >> v.numResources >> v.resource[0] >> v.resource[1]; |
166 | 11.3M | for (int i = 0; i < v.numResources; i++) { |
167 | 11.3M | NeResource resource; |
168 | 11.3M | s >> resource; |
169 | 11.3M | v.resources.append(resource); |
170 | 11.3M | } |
171 | 8.92k | return s; |
172 | 10.5k | } |
173 | | |
174 | | QDataStream &operator>>(QDataStream &s, NeResourceTable &v) |
175 | 1.65k | { |
176 | 1.65k | s >> v.alignmentShiftCount; |
177 | 10.5k | while (1) { |
178 | 10.5k | NeResourceTable::Type type; |
179 | 10.5k | s >> type; |
180 | 10.5k | if (!type.typeId) { |
181 | 1.65k | break; |
182 | 1.65k | } |
183 | 8.92k | v.types[ResourceType(type.typeId ^ 0x8000)] = type; |
184 | 8.92k | } |
185 | 1.65k | return s; |
186 | 1.65k | } |
187 | | |
188 | | bool readNewExecutablePrimaryIcon(QDataStream &ds, const DosHeader &dosHeader, QIODevice *outputDevice) |
189 | 3.09k | { |
190 | 3.09k | NeFileHeader fileHeader; |
191 | 3.09k | NeResourceTable resources; |
192 | 3.09k | QDataStream out{outputDevice}; |
193 | | |
194 | 3.09k | out.setByteOrder(QDataStream::LittleEndian); |
195 | | |
196 | 3.09k | if (!ds.device()->seek(dosHeader.newHeaderOffset)) { |
197 | 0 | return false; |
198 | 0 | } |
199 | | |
200 | 3.09k | char signature[2]; |
201 | 3.09k | ds.readRawData(signature, sizeof(signature)); |
202 | | |
203 | 3.09k | if (signature[0] != 'N' || signature[1] != 'E') { |
204 | 1.43k | return false; |
205 | 1.43k | } |
206 | | |
207 | 1.65k | ds >> fileHeader; |
208 | 1.65k | if (!ds.device()->seek(dosHeader.newHeaderOffset + fileHeader.offsetOfResourceTable)) { |
209 | 0 | return false; |
210 | 0 | } |
211 | | |
212 | 1.65k | ds >> resources; |
213 | | |
214 | 1.65k | if (!resources.types.contains(ResourceType::GroupIcon) || !resources.types.contains(ResourceType::Icon)) { |
215 | 242 | return false; |
216 | 242 | } |
217 | | |
218 | 1.41k | auto iconResources = resources.types[ResourceType::Icon].resources; |
219 | | |
220 | 1.41k | auto iconGroupResources = resources.types[ResourceType::GroupIcon]; |
221 | 1.41k | if (iconGroupResources.resources.empty()) { |
222 | 10 | return false; |
223 | 10 | } |
224 | | |
225 | 1.40k | auto iconGroupResource = iconGroupResources.resources.first(); |
226 | 1.40k | if (!ds.device()->seek(iconGroupResource.dataOffsetShifted << resources.alignmentShiftCount)) { |
227 | 11 | return false; |
228 | 11 | } |
229 | | |
230 | 1.39k | RtGroupIconDirectory iconGroup; |
231 | 1.39k | ds >> iconGroup; |
232 | | |
233 | 1.39k | IconDir icoHeader; |
234 | 1.39k | icoHeader.reserved = 0; |
235 | 1.39k | icoHeader.type = 1; // Always 1 for ico files. |
236 | 1.39k | icoHeader.count = iconGroup.count; |
237 | 1.39k | out << icoHeader; |
238 | | |
239 | 1.39k | quint32 dataOffset = IconDirSize + IconDirEntrySize * iconGroup.count; |
240 | 1.39k | QVector<QPair<qint64, quint32>> resourceOffsetSizePairs; |
241 | | |
242 | 3.43M | for (int i = 0; i < iconGroup.count; i++) { |
243 | 3.43M | RtGroupIconDirectoryEntry entry; |
244 | 3.43M | ds >> entry; |
245 | | |
246 | 3.45M | auto it = std::find_if(iconResources.begin(), iconResources.end(), [&entry](const NeResource &res) { |
247 | 3.45M | return res.resourceId == entry.resourceId; |
248 | 3.45M | }); |
249 | 3.43M | if (it == iconResources.end()) { |
250 | 178 | return false; |
251 | 178 | } |
252 | | |
253 | 3.43M | IconDirEntry icoEntry{entry, dataOffset}; |
254 | 3.43M | out << icoEntry; |
255 | | |
256 | 3.43M | NeResource iconRes = *it; |
257 | 3.43M | resourceOffsetSizePairs.append({iconRes.dataOffsetShifted << resources.alignmentShiftCount, entry.size}); |
258 | 3.43M | dataOffset += entry.size; |
259 | 3.43M | } |
260 | | |
261 | 3.42M | for (auto offsetSizePair : resourceOffsetSizePairs) { |
262 | 3.42M | if (!ds.device()->seek(offsetSizePair.first)) { |
263 | 0 | return false; |
264 | 0 | } |
265 | 3.42M | outputDevice->write(ds.device()->read(offsetSizePair.second)); |
266 | 3.42M | } |
267 | | |
268 | 1.21k | return true; |
269 | 1.21k | } |
270 | | |
271 | | // Win32 Portable Executable |
272 | | |
273 | | constexpr quint16 PeOptionalHeaderMagicPe32 = 0x010b; |
274 | | constexpr quint16 PeOptionalHeaderMagicPe32Plus = 0x020b; |
275 | | constexpr quint32 PeSubdirBitMask = 0x80000000; |
276 | | |
277 | | constexpr int PeSignatureSize = 4; |
278 | | constexpr int PeFileHeaderSize = 20; |
279 | | constexpr int PeOffsetToDataDirectoryPe32 = 120; |
280 | | constexpr int PeOffsetToDataDirectoryPe32Plus = 136; |
281 | | constexpr int PeDataDirectorySize = 8; |
282 | | |
283 | | enum class PeDataDirectoryIndex { |
284 | | Resource = 2, |
285 | | }; |
286 | | |
287 | | struct PeFileHeader { |
288 | | quint16 machine; |
289 | | quint16 numSections; |
290 | | quint32 timestamp; |
291 | | quint32 offsetToSymbolTable; |
292 | | quint32 numberOfSymbols; |
293 | | quint16 sizeOfOptionalHeader; |
294 | | quint16 fileCharacteristics; |
295 | | }; |
296 | | |
297 | | struct PeDataDirectory { |
298 | | quint32 virtualAddress, size; |
299 | | }; |
300 | | |
301 | | struct PeSection { |
302 | | char name[8]; |
303 | | quint32 virtualSize, virtualAddress; |
304 | | quint32 sizeOfRawData, pointerToRawData; |
305 | | quint32 pointerToRelocs, pointerToLineNums; |
306 | | quint16 numRelocs, numLineNums; |
307 | | quint32 characteristics; |
308 | | }; |
309 | | |
310 | | struct PeResourceDirectoryTable { |
311 | | quint32 characteristics; |
312 | | quint32 timestamp; |
313 | | quint16 majorVersion, minorVersion; |
314 | | quint16 numNameEntries, numIDEntries; |
315 | | }; |
316 | | |
317 | | struct PeResourceDirectoryEntry { |
318 | | quint32 resourceId, offset; |
319 | | }; |
320 | | |
321 | | struct PeResourceDataEntry { |
322 | | quint32 dataAddress; |
323 | | quint32 size; |
324 | | quint32 codepage; |
325 | | quint32 reserved; |
326 | | }; |
327 | | |
328 | | QDataStream &operator>>(QDataStream &s, PeFileHeader &v) |
329 | 1.28k | { |
330 | 1.28k | s >> v.machine >> v.numSections >> v.timestamp >> v.offsetToSymbolTable >> v.numberOfSymbols >> v.sizeOfOptionalHeader >> v.fileCharacteristics; |
331 | 1.28k | return s; |
332 | 1.28k | } |
333 | | |
334 | | QDataStream &operator>>(QDataStream &s, PeDataDirectory &v) |
335 | 1.23k | { |
336 | 1.23k | s >> v.virtualAddress >> v.size; |
337 | 1.23k | return s; |
338 | 1.23k | } |
339 | | |
340 | | QDataStream &operator>>(QDataStream &s, PeSection &v) |
341 | 16.7M | { |
342 | 16.7M | s.readRawData(v.name, sizeof(v.name)); |
343 | 16.7M | s >> v.virtualSize >> v.virtualAddress >> v.sizeOfRawData >> v.pointerToRawData >> v.pointerToRelocs >> v.pointerToLineNums >> v.numRelocs >> v.numLineNums |
344 | 16.7M | >> v.characteristics; |
345 | 16.7M | return s; |
346 | 16.7M | } |
347 | | |
348 | | QDataStream &operator>>(QDataStream &s, PeResourceDirectoryTable &v) |
349 | 59.2k | { |
350 | 59.2k | s >> v.characteristics >> v.timestamp >> v.majorVersion >> v.minorVersion >> v.numNameEntries >> v.numIDEntries; |
351 | 59.2k | return s; |
352 | 59.2k | } |
353 | | |
354 | | QDataStream &operator>>(QDataStream &s, PeResourceDirectoryEntry &v) |
355 | 31.6M | { |
356 | 31.6M | s >> v.resourceId >> v.offset; |
357 | 31.6M | return s; |
358 | 31.6M | } |
359 | | |
360 | | QDataStream &operator>>(QDataStream &s, PeResourceDataEntry &v) |
361 | 12.9M | { |
362 | 12.9M | s >> v.dataAddress >> v.size >> v.codepage >> v.reserved; |
363 | 12.9M | return s; |
364 | 12.9M | } |
365 | | |
366 | | qint64 addressToOffset(const QVector<PeSection> §ions, quint32 rva) |
367 | 84.3k | { |
368 | 11.3M | for (int i = 0; i < sections.size(); i++) { |
369 | 11.3M | auto sectionBegin = sections[i].virtualAddress; |
370 | 11.3M | auto effectiveSize = sections[i].sizeOfRawData; |
371 | 11.3M | if (sections[i].virtualSize) { |
372 | 93.5k | effectiveSize = std::min(effectiveSize, sections[i].virtualSize); |
373 | 93.5k | } |
374 | 11.3M | auto sectionEnd = sections[i].virtualAddress + effectiveSize; |
375 | 11.3M | if (rva >= sectionBegin && rva < sectionEnd) { |
376 | 22.3k | return rva - sectionBegin + sections[i].pointerToRawData; |
377 | 22.3k | } |
378 | 11.3M | } |
379 | 62.0k | return -1; |
380 | 84.3k | } |
381 | | |
382 | | QVector<PeResourceDirectoryEntry> readResourceDataDirectoryEntry(QDataStream &ds) |
383 | 59.2k | { |
384 | 59.2k | PeResourceDirectoryTable table; |
385 | 59.2k | ds >> table; |
386 | 59.2k | QVector<PeResourceDirectoryEntry> entries; |
387 | 31.7M | for (int i = 0; i < table.numNameEntries + table.numIDEntries; i++) { |
388 | 31.6M | PeResourceDirectoryEntry entry; |
389 | 31.6M | ds >> entry; |
390 | 31.6M | entries.append(entry); |
391 | 31.6M | } |
392 | 59.2k | return entries; |
393 | 59.2k | } |
394 | | |
395 | | bool readPortableExecutablePrimaryIcon(QDataStream &ds, const DosHeader &dosHeader, QIODevice *outputDevice) |
396 | 3.22k | { |
397 | 3.22k | PeFileHeader fileHeader; |
398 | 3.22k | bool isPe32Plus; |
399 | 3.22k | QMap<quint32, PeResourceDataEntry> iconResources; |
400 | 3.22k | std::optional<PeResourceDataEntry> primaryIconGroupResource; |
401 | 3.22k | QVector<PeSection> sections; |
402 | 3.22k | QDataStream out{outputDevice}; |
403 | | |
404 | 3.22k | out.setByteOrder(QDataStream::LittleEndian); |
405 | | |
406 | | // Seek to + verify PE header. We're at the file header after this. |
407 | 3.22k | if (!ds.device()->seek(dosHeader.newHeaderOffset)) { |
408 | 0 | return false; |
409 | 0 | } |
410 | | |
411 | 3.22k | char signature[4]; |
412 | 3.22k | if (ds.readRawData(signature, sizeof(signature)) == -1) { |
413 | 0 | return false; |
414 | 0 | } |
415 | | |
416 | 3.22k | if (signature[0] != 'P' || signature[1] != 'E' || signature[2] != 0 || signature[3] != 0) { |
417 | 1.93k | return false; |
418 | 1.93k | } |
419 | | |
420 | 1.28k | ds >> fileHeader; |
421 | | |
422 | | // Read optional header magic to determine if this is PE32 or PE32+. |
423 | 1.28k | quint16 optMagic; |
424 | 1.28k | ds >> optMagic; |
425 | | |
426 | 1.28k | switch (optMagic) { |
427 | 477 | case PeOptionalHeaderMagicPe32: |
428 | 477 | isPe32Plus = false; |
429 | 477 | break; |
430 | | |
431 | 756 | case PeOptionalHeaderMagicPe32Plus: |
432 | 756 | isPe32Plus = true; |
433 | 756 | break; |
434 | | |
435 | 52 | default: |
436 | 52 | return false; |
437 | 1.28k | } |
438 | | |
439 | | // Read section table now, so we can interpret RVAs. |
440 | 1.23k | quint64 sectionTableOffset = dosHeader.newHeaderOffset; |
441 | 1.23k | sectionTableOffset += PeSignatureSize + PeFileHeaderSize; |
442 | 1.23k | sectionTableOffset += fileHeader.sizeOfOptionalHeader; |
443 | 1.23k | if (!ds.device()->seek(sectionTableOffset)) { |
444 | 0 | return false; |
445 | 0 | } |
446 | | |
447 | 16.7M | for (int i = 0; i < fileHeader.numSections; i++) { |
448 | 16.7M | PeSection section; |
449 | 16.7M | ds >> section; |
450 | 16.7M | sections.append(section); |
451 | 16.7M | } |
452 | | |
453 | | // Find resource directory. |
454 | 1.23k | qint64 dataDirOffset = dosHeader.newHeaderOffset; |
455 | 1.23k | if (isPe32Plus) { |
456 | 756 | dataDirOffset += PeOffsetToDataDirectoryPe32Plus; |
457 | 756 | } else { |
458 | 477 | dataDirOffset += PeOffsetToDataDirectoryPe32; |
459 | 477 | } |
460 | 1.23k | dataDirOffset += qint64(PeDataDirectoryIndex::Resource) * PeDataDirectorySize; |
461 | 1.23k | if (!ds.device()->seek(dataDirOffset)) { |
462 | 0 | return false; |
463 | 0 | } |
464 | 1.23k | PeDataDirectory resourceDirectory; |
465 | 1.23k | ds >> resourceDirectory; |
466 | | |
467 | | // Read resource tree. |
468 | 1.23k | auto resourceOffset = addressToOffset(sections, resourceDirectory.virtualAddress); |
469 | 1.23k | if (resourceOffset < 0) { |
470 | 347 | return false; |
471 | 347 | } |
472 | | |
473 | 886 | if (!ds.device()->seek(resourceOffset)) { |
474 | 0 | return false; |
475 | 0 | } |
476 | | |
477 | 886 | const auto level1 = readResourceDataDirectoryEntry(ds); |
478 | | |
479 | 8.83M | for (auto entry1 : level1) { |
480 | 8.83M | if ((entry1.offset & PeSubdirBitMask) == 0) |
481 | 8.82M | continue; |
482 | 10.0k | if (!ds.device()->seek(resourceOffset + (entry1.offset & ~PeSubdirBitMask))) { |
483 | 0 | return false; |
484 | 0 | } |
485 | | |
486 | 10.0k | const auto level2 = readResourceDataDirectoryEntry(ds); |
487 | | |
488 | 9.71M | for (auto entry2 : level2) { |
489 | 9.71M | if ((entry2.offset & PeSubdirBitMask) == 0) |
490 | 9.66M | continue; |
491 | 48.3k | if (!ds.device()->seek(resourceOffset + (entry2.offset & ~PeSubdirBitMask))) { |
492 | 0 | return false; |
493 | 0 | } |
494 | | |
495 | | // Read subdirectory. |
496 | 48.3k | const auto level3 = readResourceDataDirectoryEntry(ds); |
497 | | |
498 | 13.0M | for (auto entry3 : level3) { |
499 | 13.0M | if ((entry3.offset & PeSubdirBitMask) == PeSubdirBitMask) |
500 | 163k | continue; |
501 | 12.9M | if (!ds.device()->seek(resourceOffset + (entry3.offset & ~PeSubdirBitMask))) { |
502 | 0 | return false; |
503 | 0 | } |
504 | | |
505 | | // Read data. |
506 | 12.9M | PeResourceDataEntry dataEntry; |
507 | 12.9M | ds >> dataEntry; |
508 | | |
509 | 12.9M | switch (ResourceType(entry1.resourceId)) { |
510 | 5.22M | case ResourceType::Icon: |
511 | 5.22M | iconResources[entry2.resourceId] = dataEntry; |
512 | 5.22M | break; |
513 | | |
514 | 1.82M | case ResourceType::GroupIcon: |
515 | 1.82M | if (!primaryIconGroupResource.has_value()) { |
516 | 339 | primaryIconGroupResource = dataEntry; |
517 | 339 | } |
518 | 12.9M | } |
519 | 12.9M | } |
520 | 48.3k | } |
521 | 10.0k | } |
522 | | |
523 | 886 | if (!primaryIconGroupResource.has_value()) { |
524 | 547 | return false; |
525 | 547 | } |
526 | | |
527 | 339 | if (!ds.device()->seek(addressToOffset(sections, primaryIconGroupResource->dataAddress))) { |
528 | 85 | return false; |
529 | 85 | } |
530 | | |
531 | 254 | RtGroupIconDirectory primaryIconGroup; |
532 | 254 | ds >> primaryIconGroup; |
533 | | |
534 | 254 | IconDir icoFileHeader; |
535 | 254 | icoFileHeader.reserved = 0; |
536 | 254 | icoFileHeader.type = 1; // Always 1 for ico files. |
537 | 254 | icoFileHeader.count = primaryIconGroup.count; |
538 | 254 | out << icoFileHeader; |
539 | | |
540 | 254 | quint32 dataOffset = IconDirSize + IconDirEntrySize * primaryIconGroup.count; |
541 | 254 | QVector<QPair<qint64, quint32>> resourceOffsetSizePairs; |
542 | | |
543 | 83.0k | for (int i = 0; i < primaryIconGroup.count; i++) { |
544 | 82.8k | RtGroupIconDirectoryEntry entry; |
545 | 82.8k | ds >> entry; |
546 | | |
547 | 82.8k | IconDirEntry icoFileEntry{entry, dataOffset}; |
548 | 82.8k | out << icoFileEntry; |
549 | | |
550 | 82.8k | if (auto it = iconResources.find(entry.resourceId); it != iconResources.end()) { |
551 | 82.7k | PeResourceDataEntry iconResource = *it; |
552 | 82.7k | resourceOffsetSizePairs.append({addressToOffset(sections, iconResource.dataAddress), iconResource.size}); |
553 | 82.7k | dataOffset += iconResource.size; |
554 | 82.7k | } else { |
555 | 105 | return false; |
556 | 105 | } |
557 | 82.8k | } |
558 | | |
559 | 21.1k | for (auto offsetSizePair : resourceOffsetSizePairs) { |
560 | 21.1k | if (!ds.device()->seek(offsetSizePair.first)) { |
561 | 18 | return false; |
562 | 18 | } |
563 | 21.0k | outputDevice->write(ds.device()->read(offsetSizePair.second)); |
564 | 21.0k | } |
565 | | |
566 | 131 | return true; |
567 | 149 | } |
568 | | |
569 | | } |
570 | | |
571 | | bool ExeUtils::loadIcoDataFromExe(QIODevice *inputDevice, QIODevice *outputDevice) |
572 | 3.69k | { |
573 | 3.69k | QDataStream ds{inputDevice}; |
574 | 3.69k | ds.setByteOrder(QDataStream::LittleEndian); |
575 | | |
576 | | // Read DOS header. |
577 | 3.69k | DosHeader dosHeader; |
578 | 3.69k | ds >> dosHeader; |
579 | | |
580 | | // Verify the MZ header. |
581 | 3.69k | if (dosHeader.signature[0] != 'M' || dosHeader.signature[1] != 'Z') { |
582 | 473 | return false; |
583 | 473 | } |
584 | | |
585 | 3.22k | if (readPortableExecutablePrimaryIcon(ds, dosHeader, outputDevice)) { |
586 | 131 | return true; |
587 | 131 | } |
588 | | |
589 | 3.09k | if (readNewExecutablePrimaryIcon(ds, dosHeader, outputDevice)) { |
590 | 1.21k | return true; |
591 | 1.21k | } |
592 | | |
593 | 1.87k | return false; |
594 | 3.09k | } |