2
0

BsIconUtility.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. #include "BsIconUtility.h"
  2. #include "BsPixelData.h"
  3. #include "BsColor.h"
  4. #define MSDOS_SIGNATURE 0x5A4D
  5. #define PE_SIGNATURE 0x00004550
  6. #define PE_32BIT_SIGNATURE 0x10B
  7. #define PE_64BIT_SIGNATURE 0x20B
  8. #define PE_NUM_DIRECTORY_ENTRIES 16
  9. #define PE_SECTION_UNINITIALIZED_DATA 0x00000080
  10. #define PE_IMAGE_DIRECTORY_ENTRY_RESOURCE 2
  11. #define PE_IMAGE_RT_ICON 3
  12. namespace BansheeEngine
  13. {
  14. /**
  15. * @brief MS-DOS header found at the beggining in a PE format file.
  16. */
  17. struct MSDOSHeader
  18. {
  19. UINT16 signature;
  20. UINT16 lastSize;
  21. UINT16 numBlocks;
  22. UINT16 numReloc;
  23. UINT16 hdrSize;
  24. UINT16 minAlloc;
  25. UINT16 maxAlloc;
  26. UINT16 ss;
  27. UINT16 sp;
  28. UINT16 checksum;
  29. UINT16 ip;
  30. UINT16 cs;
  31. UINT16 relocPos;
  32. UINT16 numOverlay;
  33. UINT16 reserved1[4];
  34. UINT16 oemId;
  35. UINT16 oemInfo;
  36. UINT16 reserved2[10];
  37. UINT32 lfanew;
  38. };
  39. /**
  40. * @brief COFF header found in a PE format file.
  41. */
  42. struct COFFHeader
  43. {
  44. UINT16 machine;
  45. UINT16 numSections;
  46. UINT32 timeDateStamp;
  47. UINT32 ptrSymbolTable;
  48. UINT32 numSymbols;
  49. UINT16 sizeOptHeader;
  50. UINT16 characteristics;
  51. };
  52. /**
  53. * @brief Contains address and size of data areas in a PE image.
  54. */
  55. struct PEDataDirectory
  56. {
  57. UINT32 virtualAddress;
  58. UINT32 size;
  59. };
  60. /**
  61. * @brief Optional header in a 32-bit PE format file.
  62. */
  63. struct PEOptionalHeader32
  64. {
  65. UINT16 signature;
  66. UINT8 majorLinkerVersion;
  67. UINT8 minorLinkerVersion;
  68. UINT32 sizeCode;
  69. UINT32 sizeInitializedData;
  70. UINT32 sizeUninitializedData;
  71. UINT32 addressEntryPoint;
  72. UINT32 baseCode;
  73. UINT32 baseData;
  74. UINT32 baseImage;
  75. UINT32 alignmentSection;
  76. UINT32 alignmentFile;
  77. UINT16 majorOSVersion;
  78. UINT16 minorOSVersion;
  79. UINT16 majorImageVersion;
  80. UINT16 minorImageVersion;
  81. UINT16 majorSubsystemVersion;
  82. UINT16 minorSubsystemVersion;
  83. UINT32 reserved;
  84. UINT32 sizeImage;
  85. UINT32 sizeHeaders;
  86. UINT32 checksum;
  87. UINT16 subsystem;
  88. UINT16 characteristics;
  89. UINT32 sizeStackReserve;
  90. UINT32 sizeStackCommit;
  91. UINT32 sizeHeapReserve;
  92. UINT32 sizeHeapCommit;
  93. UINT32 loaderFlags;
  94. UINT32 NumRvaAndSizes;
  95. PEDataDirectory dataDirectory[16];
  96. };
  97. /**
  98. * @brief Optional header in a 64-bit PE format file.
  99. */
  100. struct PEOptionalHeader64
  101. {
  102. UINT16 signature;
  103. UINT8 majorLinkerVersion;
  104. UINT8 minorLinkerVersion;
  105. UINT32 sizeCode;
  106. UINT32 sizeInitializedData;
  107. UINT32 sizeUninitializedData;
  108. UINT32 addressEntryPoint;
  109. UINT32 baseCode;
  110. UINT64 baseImage;
  111. UINT32 alignmentSection;
  112. UINT32 alignmentFile;
  113. UINT16 majorOSVersion;
  114. UINT16 minorOSVersion;
  115. UINT16 majorImageVersion;
  116. UINT16 minorImageVersion;
  117. UINT16 majorSubsystemVersion;
  118. UINT16 minorSubsystemVersion;
  119. UINT32 reserved;
  120. UINT32 sizeImage;
  121. UINT32 sizeHeaders;
  122. UINT32 checksum;
  123. UINT16 subsystem;
  124. UINT16 characteristics;
  125. UINT64 sizeStackReserve;
  126. UINT64 sizeStackCommit;
  127. UINT64 sizeHeapReserve;
  128. UINT64 sizeHeapCommit;
  129. UINT32 loaderFlags;
  130. UINT32 NumRvaAndSizes;
  131. PEDataDirectory dataDirectory[16];
  132. };
  133. /**
  134. * @brief A section header in a PE format file.
  135. */
  136. struct PESectionHeader
  137. {
  138. char name[8];
  139. UINT32 virtualSize;
  140. UINT32 relativeVirtualAddress;
  141. UINT32 physicalSize;
  142. UINT32 physicalAddress;
  143. UINT8 deprecated[12];
  144. UINT32 flags;
  145. };
  146. /**
  147. * @brief A resource table header within a .rsrc section in a PE format file.
  148. */
  149. struct PEImageResourceDirectory
  150. {
  151. UINT32 flags;
  152. UINT32 timeDateStamp;
  153. UINT16 majorVersion;
  154. UINT16 minorVersion;
  155. UINT16 numNamedEntries;
  156. UINT16 numIdEntries;
  157. };
  158. /**
  159. * @brief A single entry in a resource table within a .rsrc section in a PE format file.
  160. */
  161. struct PEImageResourceEntry
  162. {
  163. UINT32 type;
  164. UINT32 offsetDirectory : 31;
  165. UINT32 isDirectory : 1;
  166. };
  167. /**
  168. * @brief An entry in a resource table referencing resource data. Found within a
  169. * .rsrc section in a PE format file.
  170. */
  171. struct PEImageResourceEntryData
  172. {
  173. UINT32 offsetData;
  174. UINT32 size;
  175. UINT32 codePage;
  176. UINT32 resourceHandle;
  177. };
  178. /**
  179. * @brief Header used in icon file format.
  180. */
  181. struct IconHeader
  182. {
  183. UINT32 size;
  184. INT32 width;
  185. INT32 height;
  186. UINT16 planes;
  187. UINT16 bitCount;
  188. UINT32 compression;
  189. UINT32 sizeImage;
  190. INT32 xPelsPerMeter;
  191. INT32 yPelsPerMeter;
  192. UINT32 clrUsed;
  193. UINT32 clrImportant;
  194. };
  195. void IconUtility::updateIconExe(const Path& path, const Map<UINT32, PixelDataPtr>& pixelsPerSize)
  196. {
  197. // A PE file is structured as such:
  198. // - MSDOS Header
  199. // - PE Signature
  200. // - COFF Header
  201. // - PE Optional Header
  202. // - One or multiple sections
  203. // - .code
  204. // - .data
  205. // - ...
  206. // - .rsrc
  207. // - icon/cursor/etc data
  208. std::fstream stream;
  209. stream.open(path.toString().c_str(), std::ios::in | std::ios::out | std::ios::binary);
  210. // First check magic number to ensure file is even an executable
  211. UINT16 magicNum;
  212. stream.read((char*)&magicNum, sizeof(magicNum));
  213. if (magicNum != MSDOS_SIGNATURE)
  214. BS_EXCEPT(InvalidStateException, "Provided file is not a valid executable.");
  215. // Read the MSDOS header and skip over it
  216. stream.seekg(0);
  217. MSDOSHeader msdosHeader;
  218. stream.read((char*)&msdosHeader, sizeof(MSDOSHeader));
  219. // Read PE signature
  220. stream.seekg(msdosHeader.lfanew);
  221. UINT32 peSignature;
  222. stream.read((char*)&peSignature, sizeof(peSignature));
  223. if (peSignature != PE_SIGNATURE)
  224. BS_EXCEPT(InvalidStateException, "Provided file is not in PE format.");
  225. // Read COFF header
  226. COFFHeader coffHeader;
  227. stream.read((char*)&coffHeader, sizeof(COFFHeader));
  228. if (coffHeader.sizeOptHeader == 0) // .exe files always have an optional header
  229. BS_EXCEPT(InvalidStateException, "Provided file is not a valid executable.");
  230. UINT32 numSectionHeaders = coffHeader.numSections;
  231. // Read optional header
  232. auto optionalHeaderPos = stream.tellg();
  233. UINT16 optionalHeaderSignature;
  234. stream.read((char*)&optionalHeaderSignature, sizeof(optionalHeaderSignature));
  235. PEDataDirectory* dataDirectory = nullptr;
  236. stream.seekg(optionalHeaderPos);
  237. if (optionalHeaderSignature == PE_32BIT_SIGNATURE)
  238. {
  239. PEOptionalHeader32 optionalHeader;
  240. stream.read((char*)&optionalHeader, sizeof(optionalHeader));
  241. dataDirectory = optionalHeader.dataDirectory + PE_IMAGE_DIRECTORY_ENTRY_RESOURCE;
  242. }
  243. else if (optionalHeaderSignature == PE_64BIT_SIGNATURE)
  244. {
  245. PEOptionalHeader64 optionalHeader;
  246. stream.read((char*)&optionalHeader, sizeof(optionalHeader));
  247. dataDirectory = optionalHeader.dataDirectory + PE_IMAGE_DIRECTORY_ENTRY_RESOURCE;
  248. }
  249. else
  250. BS_EXCEPT(InvalidStateException, "Unrecognized PE format.");
  251. // Read section headers
  252. auto sectionHeaderPos = optionalHeaderPos + (std::ifstream::pos_type)coffHeader.sizeOptHeader;
  253. stream.seekg(sectionHeaderPos);
  254. PESectionHeader* sectionHeaders = bs_stack_alloc<PESectionHeader>(numSectionHeaders);
  255. stream.read((char*)sectionHeaders, sizeof(PESectionHeader) * numSectionHeaders);
  256. // Look for .rsrc section header
  257. std::function<void(PEImageResourceDirectory*, PEImageResourceDirectory*, UINT8*, UINT32)> setIconData =
  258. [&](PEImageResourceDirectory* base, PEImageResourceDirectory* current, UINT8* imageData, UINT32 sectionAddress)
  259. {
  260. UINT32 numEntries = current->numIdEntries; // Not supporting name entries
  261. PEImageResourceEntry* entries = (PEImageResourceEntry*)(current + 1);
  262. for (UINT32 i = 0; i < numEntries; i++)
  263. {
  264. // Only at root does the type identify resource type
  265. if (base == current && entries[i].type != PE_IMAGE_RT_ICON)
  266. continue;
  267. if (entries[i].isDirectory)
  268. {
  269. PEImageResourceDirectory* child = (PEImageResourceDirectory*)(((UINT8*)base) + entries[i].offsetDirectory);
  270. setIconData(base, child, imageData, sectionAddress);
  271. }
  272. else
  273. {
  274. PEImageResourceEntryData* data = (PEImageResourceEntryData*)(((UINT8*)base) + entries[i].offsetDirectory);
  275. UINT8* iconData = imageData + (data->offsetData - sectionAddress);
  276. updateIconData(iconData, pixelsPerSize);
  277. }
  278. }
  279. };
  280. for (UINT32 i = 0; i < numSectionHeaders; i++)
  281. {
  282. if (sectionHeaders[i].flags & PE_SECTION_UNINITIALIZED_DATA)
  283. continue;
  284. if (strcmp(sectionHeaders[i].name, ".rsrc") == 0)
  285. {
  286. UINT32 imageSize = sectionHeaders[i].physicalSize;
  287. UINT8* imageData = (UINT8*)bs_stack_alloc(imageSize);
  288. stream.seekg(sectionHeaders[i].physicalAddress);
  289. stream.read((char*)imageData, imageSize);
  290. UINT32 resourceDirOffset = dataDirectory->virtualAddress - sectionHeaders[i].relativeVirtualAddress;
  291. PEImageResourceDirectory* resourceDirectory = (PEImageResourceDirectory*)&imageData[resourceDirOffset];
  292. setIconData(resourceDirectory, resourceDirectory, imageData, sectionHeaders[i].relativeVirtualAddress);
  293. stream.seekp(sectionHeaders[i].physicalAddress);
  294. stream.write((char*)imageData, imageSize);
  295. bs_stack_free(imageData);
  296. }
  297. }
  298. bs_stack_free(sectionHeaders);
  299. stream.close();
  300. }
  301. void IconUtility::updateIconData(UINT8* iconData, const Map<UINT32, PixelDataPtr>& pixelsPerSize)
  302. {
  303. IconHeader* iconHeader = (IconHeader*)iconData;
  304. if (iconHeader->size != sizeof(IconHeader) || iconHeader->compression != 0
  305. || iconHeader->planes != 1 || iconHeader->bitCount != 32)
  306. {
  307. // Unsupported format
  308. return;
  309. }
  310. UINT8* iconPixels = iconData + sizeof(IconHeader);
  311. UINT32 width = iconHeader->width;
  312. UINT32 height = iconHeader->height / 2;
  313. auto iterFind = pixelsPerSize.find(width);
  314. if (iterFind == pixelsPerSize.end() || iterFind->second->getWidth() != width
  315. || iterFind->second->getHeight() != height)
  316. {
  317. // No icon of this size provided
  318. return;
  319. }
  320. // Write colors
  321. PixelDataPtr srcPixels = iterFind->second;
  322. UINT32* colorData = (UINT32*)iconPixels;
  323. UINT32 idx = 0;
  324. for (INT32 y = (INT32)height - 1; y >= 0; y--)
  325. {
  326. for (UINT32 x = 0; x < width; x++)
  327. colorData[idx++] = srcPixels->getColorAt(x, y).getAsBGRA();
  328. }
  329. // Write AND mask
  330. UINT32 colorDataSize = width * height * sizeof(UINT32);
  331. UINT8* maskData = iconPixels + colorDataSize;
  332. UINT32 numPackedPixels = width / 8; // One per bit in byte
  333. for (INT32 y = (INT32)height - 1; y >= 0; y--)
  334. {
  335. UINT8 mask = 0;
  336. for (UINT32 packedX = 0; packedX < numPackedPixels; packedX++)
  337. {
  338. for (UINT32 pixelIdx = 0; pixelIdx < 8; pixelIdx++)
  339. {
  340. UINT32 x = packedX * 8 + pixelIdx;
  341. Color color = srcPixels->getColorAt(x, y);
  342. if (color.a < 0.25f)
  343. mask |= 1 << (7 - pixelIdx);
  344. }
  345. *maskData = mask;
  346. maskData++;
  347. }
  348. }
  349. }
  350. }