BsIconUtility.cpp 10 KB

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