@@ -299,6 +299,7 @@ struct Commands {
299299
300300 int processIcon (string filename) {
301301 // FIXME: error if target is not windows prolly here instead of in the compiler
302+ // https://devblogs.microsoft.com/oldnewthing/20120720-00/?p=7083
302303 // gotta create a Windows resource file. this means:
303304 /+
304305 1) if it is a png, convert to ico. ico can be used directly. other formats not supported by simplifiying choice
@@ -308,7 +309,135 @@ struct Commands {
308309 3) pass this file to the linker
309310 +/
310311 //
311- argsToKeep ~= filename ~ " .res" ;
312+
313+ import std.string ;
314+ auto idx = lastIndexOf(filename, " ." );
315+ auto ext = idx == - 1 ? null : filename[idx .. $].toLower;
316+
317+ static import std.file ;
318+ import arsd.png;
319+
320+ const (ubyte )[] icoBytes;
321+ ubyte imageWidth;
322+ ubyte imageHeight;
323+ switch (ext) {
324+ case " .png" :
325+ icoBytes = cast (ubyte []) std.file.read (filename);
326+ auto hdr = getHeader(readPng(icoBytes));
327+ if (hdr.width > 256 || hdr.height > 256 ) {
328+ import std.stdio ;
329+ stderr.writeln(" Error: only files 256x256 or smaller supported as exe icons" );
330+ return 1 ;
331+ }
332+ imageWidth = cast (ubyte ) hdr.width;
333+ imageHeight = cast (ubyte ) hdr.height;
334+ break ;
335+ default :
336+ import std.stdio ;
337+ stderr.writeln(" Error: only .png files supported for --icon" );
338+ return 1 ;
339+ }
340+
341+ ubyte [] resBytes;
342+
343+ static struct ResourceHeader {
344+ align (1 ):
345+ uint dataSize;
346+ uint headerSize;
347+ ushort typeFirst;
348+ ushort typeSecond;
349+ ushort nameFirst;
350+ ushort nameSecond;
351+ uint dataVersion;
352+ ushort memoryFlags;
353+ ushort languageId;
354+ uint Version;
355+ uint characteristics;
356+ }
357+ static assert (ResourceHeader.sizeof == 32 );
358+
359+ ResourceHeader rh;
360+ rh.dataSize = cast (int ) icoBytes.length;
361+ rh.headerSize = cast (int ) ResourceHeader.sizeof;
362+ rh.typeFirst = 0xffff ; // indicating typeSecond is an integer instead of this being a 16 bit string
363+ rh.typeSecond = 3 ; // RT_ICON
364+ rh.nameFirst = 0xffff ; // indicate integral
365+ rh.nameSecond = 1 ; // arbitrary ID number
366+ rh.dataVersion = 0 ;
367+ rh.memoryFlags = 0x1010 ; // MOVABLE (0x10) | DISCARDABLE (0x1000)
368+ rh.languageId = 0x0409 ; // English (United States)
369+ rh.Version = 0 ;
370+ rh.characteristics = 0 ;
371+
372+ // create the .res file header (id #1, type 0, etc)
373+ resBytes.length = 32 ;
374+ resBytes[0 .. 32 ] = 0 ;
375+ resBytes[4 ] = 0x20 ;
376+ resBytes[0x08 ] = 0xff ;
377+ resBytes[0x09 ] = 0xff ;
378+ resBytes[0x0c ] = 0xff ;
379+ resBytes[0x0d ] = 0xff ;
380+
381+ // add the rest of the data
382+ resBytes ~= cast (ubyte []) ((&rh)[0 .. 1 ]);
383+ resBytes ~= icoBytes;
384+ while (resBytes.length % 4 )
385+ resBytes ~= 0 ; // pad to DWORD boundary
386+
387+ // and now also need the group directory and entries
388+ static struct GRPICONDIR {
389+ align (1 ):
390+ ushort idReserved;
391+ ushort idType;
392+ ushort idCount;
393+ }
394+ static struct GRPICONDIRENTRY {
395+ align (1 ):
396+ ubyte bWidth;
397+ ubyte bHeight;
398+ ubyte bColorCount;
399+ ubyte bReserved;
400+ ushort wPlanes;
401+ ushort wBitCount;
402+ uint dwBytesInRes;
403+ ushort nId;
404+ }
405+ // should be 20 bytes for an icon dir with a single entry, do
406+ // a GRPICONDIR immediately followed by however many GRPICONDIRENTRYs for it
407+
408+ // here we just support one image
409+ GRPICONDIR gid;
410+ gid.idType = 1 ; // icon
411+ gid.idCount = 1 ;
412+
413+ ResourceHeader rhDir;
414+ rhDir.dataSize = cast (int ) GRPICONDIR .sizeof + cast (int ) GRPICONDIRENTRY .sizeof * gid.idCount;
415+ rhDir.headerSize = cast (int ) ResourceHeader.sizeof;
416+ rhDir.typeFirst = 0xffff ;
417+ rhDir.typeSecond = 0x0e ; // RT_GROUP_ICON
418+ rhDir.nameFirst = 0xffff ;
419+ rhDir.nameSecond = 1 ; // must be same ID number as the other header we put in
420+ rhDir.dataVersion = 0 ;
421+ rhDir.memoryFlags = 0x1010 ; // MOVABLE (0x10) | DISCARDABLE (0x1000)
422+ rhDir.languageId = 0x0409 ; // English (United States)
423+ rhDir.Version = 0 ;
424+ rhDir.characteristics = 0 ;
425+
426+ GRPICONDIRENTRY [1 ] gide;
427+ gide[0 ].bWidth = imageWidth;
428+ gide[0 ].bHeight = imageHeight;
429+ gide[0 ].wPlanes = 1 ;
430+ gide[0 ].dwBytesInRes = cast (int ) icoBytes.length;
431+ gide[0 ].nId = 1 ; // again, same ID number as the other header for the img data
432+
433+ resBytes ~= cast (ubyte []) ((&rhDir)[0 .. 1 ]);
434+ resBytes ~= cast (ubyte []) ((&gid)[0 .. 1 ]);
435+ resBytes ~= cast (ubyte []) gide[];
436+
437+
438+ auto resName = filename ~ " .res" ;
439+ std.file.write (resName, resBytes);
440+ argsToKeep ~= resName;
312441 return 0 ;
313442 }
314443
0 commit comments