BsIconUtility.cpp 11 KB

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