/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 | 4.09k | { |
29 | 4.09k | s.readRawData(v.signature, sizeof(v.signature)); |
30 | 4.09k | s.device()->skip(58); |
31 | 4.09k | s >> v.newHeaderOffset; |
32 | 4.09k | return s; |
33 | 4.09k | } |
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.91k | { |
60 | 1.91k | s >> v.reserved >> v.type >> v.count; |
61 | 1.91k | return s; |
62 | 1.91k | } |
63 | | |
64 | | QDataStream &operator>>(QDataStream &s, RtGroupIconDirectoryEntry &v) |
65 | 2.78M | { |
66 | 2.78M | s >> v.width >> v.height >> v.colorCount >> v.reserved >> v.numPlanes >> v.bpp >> v.size >> v.resourceId; |
67 | 2.78M | return s; |
68 | 2.78M | } |
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 | 2.78M | { |
91 | 2.78M | width = entry.width; |
92 | 2.78M | height = entry.height; |
93 | 2.78M | colorCount = entry.colorCount; |
94 | 2.78M | reserved = entry.reserved; |
95 | 2.78M | numPlanes = entry.numPlanes; |
96 | 2.78M | bpp = entry.bpp; |
97 | 2.78M | size = entry.size; |
98 | 2.78M | imageOffset = dataOffset; |
99 | 2.78M | } |
100 | | }; |
101 | | |
102 | | constexpr int IconDirEntrySize = 16; |
103 | | |
104 | | QDataStream &operator<<(QDataStream &s, const IconDir &v) |
105 | 1.91k | { |
106 | 1.91k | s << v.reserved << v.type << v.count; |
107 | 1.91k | return s; |
108 | 1.91k | } |
109 | | |
110 | | QDataStream &operator<<(QDataStream &s, const IconDirEntry &v) |
111 | 2.78M | { |
112 | 2.78M | s << v.width << v.height << v.colorCount << v.reserved << v.numPlanes << v.bpp << v.size << v.imageOffset; |
113 | 2.78M | return s; |
114 | 2.78M | } |
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.91k | { |
145 | 1.91k | s.device()->skip(34); |
146 | 1.91k | s >> v.offsetOfResourceTable; |
147 | 1.91k | s.device()->skip(14); |
148 | 1.91k | s >> v.numberOfResourceSegments; |
149 | 1.91k | return s; |
150 | 1.91k | } |
151 | | |
152 | | QDataStream &operator>>(QDataStream &s, NeResource &v) |
153 | 14.2M | { |
154 | 14.2M | s >> v.dataOffsetShifted >> v.dataLength >> v.flags >> v.resourceId >> v.resource[0] >> v.resource[1]; |
155 | 14.2M | v.resourceId ^= 0x8000; |
156 | 14.2M | return s; |
157 | 14.2M | } |
158 | | |
159 | | QDataStream &operator>>(QDataStream &s, NeResourceTable::Type &v) |
160 | 10.6k | { |
161 | 10.6k | s >> v.typeId; |
162 | 10.6k | if (v.typeId == 0) { |
163 | 1.91k | return s; |
164 | 1.91k | } |
165 | 8.77k | s >> v.numResources >> v.resource[0] >> v.resource[1]; |
166 | 14.2M | for (int i = 0; i < v.numResources; i++) { |
167 | 14.2M | NeResource resource; |
168 | 14.2M | s >> resource; |
169 | 14.2M | v.resources.append(resource); |
170 | 14.2M | } |
171 | 8.77k | return s; |
172 | 10.6k | } |
173 | | |
174 | | QDataStream &operator>>(QDataStream &s, NeResourceTable &v) |
175 | 1.91k | { |
176 | 1.91k | s >> v.alignmentShiftCount; |
177 | 10.6k | while (1) { |
178 | 10.6k | NeResourceTable::Type type; |
179 | 10.6k | s >> type; |
180 | 10.6k | if (!type.typeId) { |
181 | 1.91k | break; |
182 | 1.91k | } |
183 | 8.77k | v.types[ResourceType(type.typeId ^ 0x8000)] = type; |
184 | 8.77k | } |
185 | 1.91k | return s; |
186 | 1.91k | } |
187 | | |
188 | | bool readNewExecutablePrimaryIcon(QDataStream &ds, const DosHeader &dosHeader, QIODevice *outputDevice) |
189 | 3.33k | { |
190 | 3.33k | NeFileHeader fileHeader; |
191 | 3.33k | NeResourceTable resources; |
192 | 3.33k | QDataStream out{outputDevice}; |
193 | | |
194 | 3.33k | out.setByteOrder(QDataStream::LittleEndian); |
195 | | |
196 | 3.33k | if (!ds.device()->seek(dosHeader.newHeaderOffset)) { |
197 | 0 | return false; |
198 | 0 | } |
199 | | |
200 | 3.33k | char signature[2]; |
201 | 3.33k | ds.readRawData(signature, sizeof(signature)); |
202 | | |
203 | 3.33k | if (signature[0] != 'N' || signature[1] != 'E') { |
204 | 1.41k | return false; |
205 | 1.41k | } |
206 | | |
207 | 1.91k | ds >> fileHeader; |
208 | 1.91k | if (!ds.device()->seek(dosHeader.newHeaderOffset + fileHeader.offsetOfResourceTable)) { |
209 | 0 | return false; |
210 | 0 | } |
211 | | |
212 | 1.91k | ds >> resources; |
213 | | |
214 | 1.91k | if (!resources.types.contains(ResourceType::GroupIcon) || !resources.types.contains(ResourceType::Icon)) { |
215 | 372 | return false; |
216 | 372 | } |
217 | | |
218 | 1.54k | auto iconResources = resources.types[ResourceType::Icon].resources; |
219 | | |
220 | 1.54k | auto iconGroupResources = resources.types[ResourceType::GroupIcon]; |
221 | 1.54k | if (iconGroupResources.resources.empty()) { |
222 | 10 | return false; |
223 | 10 | } |
224 | | |
225 | 1.53k | auto iconGroupResource = iconGroupResources.resources.first(); |
226 | 1.53k | if (!ds.device()->seek(iconGroupResource.dataOffsetShifted << resources.alignmentShiftCount)) { |
227 | 11 | return false; |
228 | 11 | } |
229 | | |
230 | 1.52k | RtGroupIconDirectory iconGroup; |
231 | 1.52k | ds >> iconGroup; |
232 | | |
233 | 1.52k | IconDir icoHeader; |
234 | 1.52k | icoHeader.reserved = 0; |
235 | 1.52k | icoHeader.type = 1; // Always 1 for ico files. |
236 | 1.52k | icoHeader.count = iconGroup.count; |
237 | 1.52k | out << icoHeader; |
238 | | |
239 | 1.52k | quint32 dataOffset = IconDirSize + IconDirEntrySize * iconGroup.count; |
240 | 1.52k | QVector<QPair<qint64, quint32>> resourceOffsetSizePairs; |
241 | | |
242 | 832k | for (int i = 0; i < iconGroup.count; i++) { |
243 | 831k | RtGroupIconDirectoryEntry entry; |
244 | 831k | ds >> entry; |
245 | | |
246 | 855k | auto it = std::find_if(iconResources.begin(), iconResources.end(), [&entry](const NeResource &res) { |
247 | 855k | return res.resourceId == entry.resourceId; |
248 | 855k | }); |
249 | 831k | if (it == iconResources.end()) { |
250 | 142 | return false; |
251 | 142 | } |
252 | | |
253 | 830k | IconDirEntry icoEntry{entry, dataOffset}; |
254 | 830k | out << icoEntry; |
255 | | |
256 | 830k | NeResource iconRes = *it; |
257 | 830k | if (entry.size != iconRes.dataLength) { |
258 | 238 | return false; |
259 | 238 | } |
260 | 830k | resourceOffsetSizePairs.append({iconRes.dataOffsetShifted << resources.alignmentShiftCount, entry.size}); |
261 | 830k | dataOffset += entry.size; |
262 | 830k | } |
263 | | |
264 | 830k | for (auto offsetSizePair : resourceOffsetSizePairs) { |
265 | 830k | if (!ds.device()->seek(offsetSizePair.first)) { |
266 | 0 | return false; |
267 | 0 | } |
268 | 830k | outputDevice->write(ds.device()->read(offsetSizePair.second)); |
269 | 830k | } |
270 | | |
271 | 1.14k | return true; |
272 | 1.14k | } |
273 | | |
274 | | // Win32 Portable Executable |
275 | | |
276 | | constexpr quint16 PeOptionalHeaderMagicPe32 = 0x010b; |
277 | | constexpr quint16 PeOptionalHeaderMagicPe32Plus = 0x020b; |
278 | | constexpr quint32 PeSubdirBitMask = 0x80000000; |
279 | | |
280 | | constexpr int PeSignatureSize = 4; |
281 | | constexpr int PeFileHeaderSize = 20; |
282 | | constexpr int PeOffsetToDataDirectoryPe32 = 120; |
283 | | constexpr int PeOffsetToDataDirectoryPe32Plus = 136; |
284 | | constexpr int PeDataDirectorySize = 8; |
285 | | |
286 | | enum class PeDataDirectoryIndex { |
287 | | Resource = 2, |
288 | | }; |
289 | | |
290 | | struct PeFileHeader { |
291 | | quint16 machine; |
292 | | quint16 numSections; |
293 | | quint32 timestamp; |
294 | | quint32 offsetToSymbolTable; |
295 | | quint32 numberOfSymbols; |
296 | | quint16 sizeOfOptionalHeader; |
297 | | quint16 fileCharacteristics; |
298 | | }; |
299 | | |
300 | | struct PeDataDirectory { |
301 | | quint32 virtualAddress, size; |
302 | | }; |
303 | | |
304 | | struct PeSection { |
305 | | char name[8]; |
306 | | quint32 virtualSize, virtualAddress; |
307 | | quint32 sizeOfRawData, pointerToRawData; |
308 | | quint32 pointerToRelocs, pointerToLineNums; |
309 | | quint16 numRelocs, numLineNums; |
310 | | quint32 characteristics; |
311 | | }; |
312 | | |
313 | | struct PeResourceDirectoryTable { |
314 | | quint32 characteristics; |
315 | | quint32 timestamp; |
316 | | quint16 majorVersion, minorVersion; |
317 | | quint16 numNameEntries, numIDEntries; |
318 | | }; |
319 | | |
320 | | struct PeResourceDirectoryEntry { |
321 | | quint32 resourceId, offset; |
322 | | }; |
323 | | |
324 | | struct PeResourceDataEntry { |
325 | | quint32 dataAddress; |
326 | | quint32 size; |
327 | | quint32 codepage; |
328 | | quint32 reserved; |
329 | | }; |
330 | | |
331 | | QDataStream &operator>>(QDataStream &s, PeFileHeader &v) |
332 | 1.42k | { |
333 | 1.42k | s >> v.machine >> v.numSections >> v.timestamp >> v.offsetToSymbolTable >> v.numberOfSymbols >> v.sizeOfOptionalHeader >> v.fileCharacteristics; |
334 | 1.42k | return s; |
335 | 1.42k | } |
336 | | |
337 | | QDataStream &operator>>(QDataStream &s, PeDataDirectory &v) |
338 | 1.35k | { |
339 | 1.35k | s >> v.virtualAddress >> v.size; |
340 | 1.35k | return s; |
341 | 1.35k | } |
342 | | |
343 | | QDataStream &operator>>(QDataStream &s, PeSection &v) |
344 | 17.3M | { |
345 | 17.3M | s.readRawData(v.name, sizeof(v.name)); |
346 | 17.3M | s >> v.virtualSize >> v.virtualAddress >> v.sizeOfRawData >> v.pointerToRawData >> v.pointerToRelocs >> v.pointerToLineNums >> v.numRelocs >> v.numLineNums |
347 | 17.3M | >> v.characteristics; |
348 | 17.3M | return s; |
349 | 17.3M | } |
350 | | |
351 | | QDataStream &operator>>(QDataStream &s, PeResourceDirectoryTable &v) |
352 | 56.0k | { |
353 | 56.0k | s >> v.characteristics >> v.timestamp >> v.majorVersion >> v.minorVersion >> v.numNameEntries >> v.numIDEntries; |
354 | 56.0k | return s; |
355 | 56.0k | } |
356 | | |
357 | | QDataStream &operator>>(QDataStream &s, PeResourceDirectoryEntry &v) |
358 | 43.6M | { |
359 | 43.6M | s >> v.resourceId >> v.offset; |
360 | 43.6M | return s; |
361 | 43.6M | } |
362 | | |
363 | | QDataStream &operator>>(QDataStream &s, PeResourceDataEntry &v) |
364 | 20.0M | { |
365 | 20.0M | s >> v.dataAddress >> v.size >> v.codepage >> v.reserved; |
366 | 20.0M | return s; |
367 | 20.0M | } |
368 | | |
369 | | qint64 addressToOffset(const QVector<PeSection> §ions, quint32 rva) |
370 | 1.95M | { |
371 | 14.1M | for (int i = 0; i < sections.size(); i++) { |
372 | 14.0M | auto sectionBegin = sections[i].virtualAddress; |
373 | 14.0M | auto effectiveSize = sections[i].sizeOfRawData; |
374 | 14.0M | if (sections[i].virtualSize) { |
375 | 1.95M | effectiveSize = std::min(effectiveSize, sections[i].virtualSize); |
376 | 1.95M | } |
377 | 14.0M | auto sectionEnd = sections[i].virtualAddress + effectiveSize; |
378 | 14.0M | if (rva >= sectionBegin && rva < sectionEnd) { |
379 | 1.86M | return rva - sectionBegin + sections[i].pointerToRawData; |
380 | 1.86M | } |
381 | 14.0M | } |
382 | 96.7k | return -1; |
383 | 1.95M | } |
384 | | |
385 | | QVector<PeResourceDirectoryEntry> readResourceDataDirectoryEntry(QDataStream &ds) |
386 | 56.0k | { |
387 | 56.0k | PeResourceDirectoryTable table; |
388 | 56.0k | ds >> table; |
389 | 56.0k | QVector<PeResourceDirectoryEntry> entries; |
390 | 43.6M | for (int i = 0; i < table.numNameEntries + table.numIDEntries; i++) { |
391 | 43.6M | PeResourceDirectoryEntry entry; |
392 | 43.6M | ds >> entry; |
393 | 43.6M | entries.append(entry); |
394 | 43.6M | } |
395 | 56.0k | return entries; |
396 | 56.0k | } |
397 | | |
398 | | bool readPortableExecutablePrimaryIcon(QDataStream &ds, const DosHeader &dosHeader, QIODevice *outputDevice) |
399 | 3.60k | { |
400 | 3.60k | PeFileHeader fileHeader; |
401 | 3.60k | bool isPe32Plus; |
402 | 3.60k | QMap<quint32, PeResourceDataEntry> iconResources; |
403 | 3.60k | std::optional<PeResourceDataEntry> primaryIconGroupResource; |
404 | 3.60k | QVector<PeSection> sections; |
405 | 3.60k | QDataStream out{outputDevice}; |
406 | | |
407 | 3.60k | out.setByteOrder(QDataStream::LittleEndian); |
408 | | |
409 | | // Seek to + verify PE header. We're at the file header after this. |
410 | 3.60k | if (!ds.device()->seek(dosHeader.newHeaderOffset)) { |
411 | 0 | return false; |
412 | 0 | } |
413 | | |
414 | 3.60k | char signature[4]; |
415 | 3.60k | if (ds.readRawData(signature, sizeof(signature)) == -1) { |
416 | 0 | return false; |
417 | 0 | } |
418 | | |
419 | 3.60k | if (signature[0] != 'P' || signature[1] != 'E' || signature[2] != 0 || signature[3] != 0) { |
420 | 2.17k | return false; |
421 | 2.17k | } |
422 | | |
423 | 1.42k | ds >> fileHeader; |
424 | | |
425 | | // Read optional header magic to determine if this is PE32 or PE32+. |
426 | 1.42k | quint16 optMagic; |
427 | 1.42k | ds >> optMagic; |
428 | | |
429 | 1.42k | switch (optMagic) { |
430 | 464 | case PeOptionalHeaderMagicPe32: |
431 | 464 | isPe32Plus = false; |
432 | 464 | break; |
433 | | |
434 | 888 | case PeOptionalHeaderMagicPe32Plus: |
435 | 888 | isPe32Plus = true; |
436 | 888 | break; |
437 | | |
438 | 72 | default: |
439 | 72 | return false; |
440 | 1.42k | } |
441 | | |
442 | | // Read section table now, so we can interpret RVAs. |
443 | 1.35k | quint64 sectionTableOffset = dosHeader.newHeaderOffset; |
444 | 1.35k | sectionTableOffset += PeSignatureSize + PeFileHeaderSize; |
445 | 1.35k | sectionTableOffset += fileHeader.sizeOfOptionalHeader; |
446 | 1.35k | if (!ds.device()->seek(sectionTableOffset)) { |
447 | 0 | return false; |
448 | 0 | } |
449 | | |
450 | 17.3M | for (int i = 0; i < fileHeader.numSections; i++) { |
451 | 17.3M | PeSection section; |
452 | 17.3M | ds >> section; |
453 | 17.3M | sections.append(section); |
454 | 17.3M | } |
455 | | |
456 | | // Find resource directory. |
457 | 1.35k | qint64 dataDirOffset = dosHeader.newHeaderOffset; |
458 | 1.35k | if (isPe32Plus) { |
459 | 888 | dataDirOffset += PeOffsetToDataDirectoryPe32Plus; |
460 | 888 | } else { |
461 | 464 | dataDirOffset += PeOffsetToDataDirectoryPe32; |
462 | 464 | } |
463 | 1.35k | dataDirOffset += qint64(PeDataDirectoryIndex::Resource) * PeDataDirectorySize; |
464 | 1.35k | if (!ds.device()->seek(dataDirOffset)) { |
465 | 0 | return false; |
466 | 0 | } |
467 | 1.35k | PeDataDirectory resourceDirectory; |
468 | 1.35k | ds >> resourceDirectory; |
469 | | |
470 | | // Read resource tree. |
471 | 1.35k | auto resourceOffset = addressToOffset(sections, resourceDirectory.virtualAddress); |
472 | 1.35k | if (resourceOffset < 0) { |
473 | 351 | return false; |
474 | 351 | } |
475 | | |
476 | 1.00k | if (!ds.device()->seek(resourceOffset)) { |
477 | 0 | return false; |
478 | 0 | } |
479 | | |
480 | 1.00k | const auto level1 = readResourceDataDirectoryEntry(ds); |
481 | | |
482 | 9.88M | for (auto entry1 : level1) { |
483 | 9.88M | if ((entry1.offset & PeSubdirBitMask) == 0) |
484 | 9.87M | continue; |
485 | 11.3k | if (!ds.device()->seek(resourceOffset + (entry1.offset & ~PeSubdirBitMask))) { |
486 | 0 | return false; |
487 | 0 | } |
488 | | |
489 | 11.3k | const auto level2 = readResourceDataDirectoryEntry(ds); |
490 | | |
491 | 13.4M | for (auto entry2 : level2) { |
492 | 13.4M | if ((entry2.offset & PeSubdirBitMask) == 0) |
493 | 13.3M | continue; |
494 | 43.7k | if (!ds.device()->seek(resourceOffset + (entry2.offset & ~PeSubdirBitMask))) { |
495 | 0 | return false; |
496 | 0 | } |
497 | | |
498 | | // Read subdirectory. |
499 | 43.7k | const auto level3 = readResourceDataDirectoryEntry(ds); |
500 | | |
501 | 20.3M | for (auto entry3 : level3) { |
502 | 20.3M | if ((entry3.offset & PeSubdirBitMask) == PeSubdirBitMask) |
503 | 218k | continue; |
504 | 20.0M | if (!ds.device()->seek(resourceOffset + (entry3.offset & ~PeSubdirBitMask))) { |
505 | 0 | return false; |
506 | 0 | } |
507 | | |
508 | | // Read data. |
509 | 20.0M | PeResourceDataEntry dataEntry; |
510 | 20.0M | ds >> dataEntry; |
511 | | |
512 | 20.0M | switch (ResourceType(entry1.resourceId)) { |
513 | 5.60M | case ResourceType::Icon: |
514 | 5.60M | iconResources[entry2.resourceId] = dataEntry; |
515 | 5.60M | break; |
516 | | |
517 | 4.61M | case ResourceType::GroupIcon: |
518 | 4.61M | if (!primaryIconGroupResource.has_value()) { |
519 | 514 | primaryIconGroupResource = dataEntry; |
520 | 514 | } |
521 | 20.0M | } |
522 | 20.0M | } |
523 | 43.7k | } |
524 | 11.3k | } |
525 | | |
526 | 1.00k | if (!primaryIconGroupResource.has_value()) { |
527 | 487 | return false; |
528 | 487 | } |
529 | | |
530 | 514 | if (!ds.device()->seek(addressToOffset(sections, primaryIconGroupResource->dataAddress))) { |
531 | 121 | return false; |
532 | 121 | } |
533 | | |
534 | 393 | RtGroupIconDirectory primaryIconGroup; |
535 | 393 | ds >> primaryIconGroup; |
536 | | |
537 | 393 | IconDir icoFileHeader; |
538 | 393 | icoFileHeader.reserved = 0; |
539 | 393 | icoFileHeader.type = 1; // Always 1 for ico files. |
540 | 393 | icoFileHeader.count = primaryIconGroup.count; |
541 | 393 | out << icoFileHeader; |
542 | | |
543 | 393 | quint32 dataOffset = IconDirSize + IconDirEntrySize * primaryIconGroup.count; |
544 | 393 | QVector<QPair<qint64, quint32>> resourceOffsetSizePairs; |
545 | | |
546 | 1.95M | for (int i = 0; i < primaryIconGroup.count; i++) { |
547 | 1.95M | RtGroupIconDirectoryEntry entry; |
548 | 1.95M | ds >> entry; |
549 | | |
550 | 1.95M | IconDirEntry icoFileEntry{entry, dataOffset}; |
551 | 1.95M | out << icoFileEntry; |
552 | | |
553 | 1.95M | if (auto it = iconResources.find(entry.resourceId); it != iconResources.end()) { |
554 | 1.95M | PeResourceDataEntry iconResource = *it; |
555 | 1.95M | resourceOffsetSizePairs.append({addressToOffset(sections, iconResource.dataAddress), iconResource.size}); |
556 | 1.95M | dataOffset += iconResource.size; |
557 | 1.95M | } else { |
558 | 106 | return false; |
559 | 106 | } |
560 | 1.95M | } |
561 | | |
562 | 1.85M | for (auto offsetSizePair : resourceOffsetSizePairs) { |
563 | 1.85M | if (!ds.device()->seek(offsetSizePair.first)) { |
564 | 20 | return false; |
565 | 20 | } |
566 | 1.85M | outputDevice->write(ds.device()->read(offsetSizePair.second)); |
567 | 1.85M | } |
568 | | |
569 | 267 | return true; |
570 | 287 | } |
571 | | |
572 | | } |
573 | | |
574 | | bool ExeUtils::loadIcoDataFromExe(QIODevice *inputDevice, QIODevice *outputDevice) |
575 | 4.09k | { |
576 | 4.09k | QDataStream ds{inputDevice}; |
577 | 4.09k | ds.setByteOrder(QDataStream::LittleEndian); |
578 | | |
579 | | // Read DOS header. |
580 | 4.09k | DosHeader dosHeader; |
581 | 4.09k | ds >> dosHeader; |
582 | | |
583 | | // Verify the MZ header. |
584 | 4.09k | if (dosHeader.signature[0] != 'M' || dosHeader.signature[1] != 'Z') { |
585 | 499 | return false; |
586 | 499 | } |
587 | | |
588 | 3.60k | if (readPortableExecutablePrimaryIcon(ds, dosHeader, outputDevice)) { |
589 | 267 | return true; |
590 | 267 | } |
591 | | |
592 | 3.33k | if (readNewExecutablePrimaryIcon(ds, dosHeader, outputDevice)) { |
593 | 1.14k | return true; |
594 | 1.14k | } |
595 | | |
596 | 2.19k | return false; |
597 | 3.33k | } |