1 /* ico2png.c - Create icon and cursor files from PNG images
2 *
3 * minimalistic version extracted from package 'icoutil'
4 * 'icoutil' is copyrighted as follows :
5 *
6 * Copyright (C) 1998-2005 Oskar Liljeblad
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 */
22
23 #ifndef _GNU_SOURCE
24 # define _GNU_SOURCE 1
25 #endif
26
27 #include <stdint.h> /* POSIX/Gnulib */
28 #include <stdio.h> /* C89 */
29 #include <stdbool.h> /* POSIX/Gnulib */
30 #include <stdlib.h> /* C89 */
31 #include <stdarg.h> /* C89 */
32 #include <stddef.h>
33 #include <dirent.h>
34 #include <ctype.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #ifdef HAVE_ERROR_H
38 #include <error.h>
39 #endif
40 #include <errno.h>
41 #include <getopt.h>
42
43 #include <png.h>
44
45 #include <X11/Intrinsic.h>
46 #include <X11/xpm.h>
47
48 #include "xpaint.h"
49 #include "image.h"
50 #include "libpnmrw.h"
51
52 extern int file_numpages;
53 extern int file_isSpecialImage;
54 static int silent;
55
56 extern Image * readMagic(char *file);
57 extern Image * ReadPNG(char *file);
58 extern int WritePNGn(char *file, Image *image);
59 extern FILE * openTempFile(char **np);
60 extern void removeTempFile(void);
61 extern void AlphaWarning(char *format, int mode);
62 extern void * xmalloc(size_t n);
63
64 /* Version number of program/package */
65 #define PROGRAM "ico2png"
66
67 # ifndef __attribute__
68 # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
69 # define __attribute__(x)
70 # endif
71 # endif
72 #define PACKED __attribute__ ((packed))
73
74 struct MessageHeader {
75 struct MessageHeader *old;
76 char *message;
77 };
78
79 #define memclear(m, s) memset((m), 0, (s))
80
81 #define xrealloc realloc
82 #define xcalloc(s) calloc(1, ((s)))
83 #define xstrdup strdup
84
85 typedef void (*IteratorFunc)(void *);
86
87 typedef struct _IteratorClass IteratorClass;
88 typedef struct _Iterator Iterator;
89
90 struct _IteratorClass {
91 bool (*has_next)(Iterator *it);
92 void *(*next)(Iterator *it);
93 void (*remove)(Iterator *it);
94 void (*free)(Iterator *it);
95 void (*restart)(Iterator *it);
96 void *(*previous)(Iterator *it);
97 void (*add)(Iterator *it, void *value);
98 };
99
100 struct _Iterator {
101 IteratorClass *class;
102 };
103
104 static bool
iterator_has_next(Iterator * it)105 iterator_has_next(Iterator *it)
106 {
107 return it->class->has_next(it);
108 }
109
110 static void *
iterator_next(Iterator * it)111 iterator_next(Iterator *it)
112 {
113 return it->class->next(it);
114 }
115
116 static void
iterator_free(Iterator * it)117 iterator_free(Iterator *it)
118 {
119 if (it->class->free == NULL)
120 free(it);
121 else
122 it->class->free(it);
123 }
124
125 static void
v_warn(const char * msg,va_list ap)126 v_warn(const char *msg, va_list ap)
127 {
128 fprintf(stderr, "%s: ", PROGRAM);
129 if (msg != NULL)
130 vfprintf(stderr, msg, ap);
131 fprintf(stderr, "\n");
132 }
133
134 static void
warn(const char * msg,...)135 warn(const char *msg, ...)
136 {
137 va_list ap;
138
139 if (silent) return;
140 va_start(ap, msg);
141 v_warn(msg, ap);
142 va_end(ap);
143 }
144
145 static void
v_warn_errno(const char * msg,va_list ap)146 v_warn_errno(const char *msg, va_list ap)
147 {
148 fprintf(stderr, "%s: ", PROGRAM);
149 if (msg != NULL) {
150 vfprintf(stderr, msg, ap);
151 fprintf(stderr, ": ");
152 }
153 fprintf(stderr, "%s\n", strerror(errno));
154 }
155
156 static void
warn_errno(const char * msg,...)157 warn_errno(const char *msg, ...)
158 {
159 va_list ap;
160
161 va_start(ap, msg);
162 v_warn_errno(msg, ap);
163 va_end(ap);
164 }
165
166 typedef struct _Entry Entry;
167
168 struct _Entry {
169 void *key;
170 void *value;
171 Entry *next;
172 };
173
174 typedef uint32_t (*HashFunc)(const void *);
175 typedef int32_t (*CompareFunc)(const void *, const void *);
176
177 typedef struct _HMap HMap;
178
179 struct _HMap {
180 Entry **buckets;
181 uint32_t buckets_length;
182 uint32_t threshold;
183 float load_factor;
184 uint32_t size;
185
186 HashFunc hash_key;
187 CompareFunc compare_keys;
188 };
189
190 static uint32_t
string_hash(const char * key)191 string_hash(const char *key)
192 {
193 uint32_t hash = 0;
194
195 for (; *key != '\0'; key++)
196 hash = (hash << 5) - hash + *key;
197
198 return hash;
199 }
200
201 static HMap *
hmap_new_specific(uint32_t initial_capacity,float load_factor)202 hmap_new_specific(uint32_t initial_capacity, float load_factor)
203 {
204 HMap *map = xmalloc(sizeof(HMap));
205
206 map->buckets = xmalloc(initial_capacity * sizeof(Entry *));
207 memclear(map->buckets, initial_capacity * sizeof(Entry *));
208
209 map->buckets_length = initial_capacity;
210 map->threshold = (uint32_t) (initial_capacity * load_factor);
211 map->load_factor = load_factor;
212 map->size = 0;
213
214 map->hash_key = (HashFunc) string_hash;
215 map->compare_keys = (CompareFunc) strcmp;
216
217 return map;
218 }
219
220 #define DEFAULT_CAPACITY 16
221 #define DEFAULT_LOAD_FACTOR 0.75F
222
223 static HMap *
hmap_new(void)224 hmap_new(void)
225 {
226 return hmap_new_specific(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
227 }
228
229 static void
hmap_set_hash_function(HMap * map,HashFunc hash_key)230 hmap_set_hash_function(HMap *map, HashFunc hash_key)
231 {
232 map->hash_key = hash_key;
233 }
234
235 static void
hmap_set_compare_function(HMap * map,CompareFunc compare_keys)236 hmap_set_compare_function(HMap *map, CompareFunc compare_keys)
237 {
238 map->compare_keys = compare_keys;
239 }
240
241 static void
hmap_iterate_values(HMap * map,IteratorFunc iterator_func)242 hmap_iterate_values(HMap *map, IteratorFunc iterator_func)
243 {
244 uint32_t i;
245
246 for (i = 0; i < map->buckets_length; i++) {
247 Entry *entry;
248 for (entry = map->buckets[i]; entry != NULL; entry = entry->next)
249 iterator_func(entry->value);
250 }
251 }
252
253 static void
hmap_clear(HMap * map)254 hmap_clear(HMap *map)
255 {
256 uint32_t i;
257
258 for (i = 0; i < map->buckets_length; i++) {
259 Entry *entry = map->buckets[i];
260 while (entry != NULL) {
261 Entry *next = entry->next;
262 free(entry);
263 entry = next;
264 }
265 map->buckets[i] = NULL;
266 }
267
268 map->size = 0;
269 }
270
271 static void
hmap_free(HMap * map)272 hmap_free(HMap *map)
273 {
274 hmap_clear(map);
275 free(map->buckets);
276 free(map);
277 }
278
279 static inline uint32_t
hmap_hash(HMap * map,const void * key)280 hmap_hash(HMap *map, const void *key)
281 {
282 // printf("hash %s into -> %d pos %d (%d)\n", key, map->hash_key(key), map->hash_key(key) % map->buckets_length, map->buckets_length);
283 return (key == NULL ? 0 : (map->hash_key(key) % map->buckets_length));
284 }
285
286 static bool
hmap_contains_key(HMap * map,const void * key)287 hmap_contains_key(HMap *map, const void *key)
288 {
289 if (key == NULL) {
290 Entry *entry = map->buckets[0];
291 for (; entry != NULL; entry = entry->next) {
292 if (entry->key == NULL)
293 return true;
294 }
295 } else {
296 Entry *entry = map->buckets[hmap_hash(map, key)];
297 for (; entry != NULL; entry = entry->next) {
298 // printf("compare %s vs %s -> %d\n",key,entry->key,map->compare_keys(key, entry->key));
299 if (map->compare_keys(key, entry->key) == 0)
300 return true;
301 }
302 }
303
304 return false;
305 }
306
307 static inline void
hmap_rehash(HMap * map)308 hmap_rehash(HMap *map)
309 {
310 Entry **old_buckets = map->buckets;
311 uint32_t old_capacity = map->buckets_length;
312 uint32_t i;
313
314 map->buckets_length = (map->buckets_length * 2) + 1;
315 map->threshold = (uint32_t) (map->buckets_length * map->load_factor);
316 map->buckets = xmalloc(map->buckets_length * sizeof(Entry *));
317 memclear(map->buckets, map->buckets_length * sizeof(Entry *));
318
319 for (i = 0; i < old_capacity; i++) {
320 Entry *entry = old_buckets[i];
321 while (entry != NULL) {
322 uint32_t index = hmap_hash(map, entry->key);
323 Entry *dest = map->buckets[index];
324 Entry *next;
325
326 if (dest != NULL) {
327 while (dest->next != NULL)
328 dest = dest->next;
329 dest->next = entry;
330 } else {
331 map->buckets[index] = entry;
332 }
333
334 next = entry->next;
335 entry->next = NULL;
336 entry = next;
337 }
338 }
339
340 free(old_buckets);
341 }
342
343 static uint32_t
hmap_size(HMap * map)344 hmap_size(HMap *map)
345 {
346 return map->size;
347 }
348
349 static void *
hmap_get(HMap * map,const void * key)350 hmap_get(HMap *map, const void *key)
351 {
352 if (key == NULL) {
353 Entry *entry = map->buckets[0];
354 for (; entry != NULL; entry = entry->next) {
355 if (entry->key == NULL)
356 return entry->value;
357 }
358 } else {
359 Entry *entry = map->buckets[hmap_hash(map, key)];
360 for (; entry != NULL; entry = entry->next) {
361 if (map->compare_keys(key, entry->key) == 0)
362 return entry->value;
363 }
364 }
365
366 return NULL;
367 }
368
369 static void *
hmap_put(HMap * map,void * key,void * value)370 hmap_put(HMap *map, void *key, void *value)
371 {
372 Entry *entry;
373 uint32_t index;
374
375 if (key == NULL) {
376 for (entry = map->buckets[0]; entry != NULL; entry = entry->next) {
377 if (entry->key == NULL) {
378 void *old_value = entry->value;
379 entry->value = value;
380 return old_value;
381 }
382 }
383 index = 0;
384 } else {
385 index = hmap_hash(map, key);
386 for (entry = map->buckets[index]; entry != NULL; entry = entry->next) {
387 if (map->compare_keys(key, entry->key) == 0) {
388 void *old_value = entry->value;
389 entry->value = value;
390 return old_value;
391 }
392 }
393 }
394
395 map->size++;
396 if (map->size > map->threshold) {
397 hmap_rehash(map);
398 index = hmap_hash(map, key);
399 }
400
401 entry = xmalloc(sizeof(Entry));
402 entry->key = key;
403 entry->value = value;
404 entry->next = map->buckets[index];
405 map->buckets[index] = entry;
406
407 return NULL;
408 }
409
410 static void *
hmap_remove(HMap * map,const void * key)411 hmap_remove(HMap *map, const void *key)
412 {
413 Entry *entry;
414 Entry *last = NULL;
415
416 if (key == NULL) {
417 for (entry = map->buckets[0]; entry != NULL; entry = entry->next) {
418 if (entry->key == NULL) {
419 void *value = entry->value;
420 if (last == NULL)
421 map->buckets[0] = entry->next;
422 else
423 last->next = entry->next;
424 map->size--;
425 free(entry);
426 return value;
427 }
428 last = entry;
429 }
430 } else {
431 uint32_t index = hmap_hash(map, key);
432 for (entry = map->buckets[index]; entry != NULL; entry = entry->next) {
433 if (map->compare_keys(key, entry->key) == 0) {
434 void *value = entry->value;
435 if (last == NULL)
436 map->buckets[index] = entry->next;
437 else
438 last->next = entry->next;
439 map->size--;
440 free(entry);
441 return value;
442 }
443 last = entry;
444 }
445 }
446
447 return NULL;
448 }
449
450 typedef struct _HMapIterator HMapIterator;
451
452 struct _HMapIterator {
453 Iterator iterator;
454 HMap *map;
455 uint32_t index;
456 Entry *entry;
457 Entry *previous_entry;
458 };
459
460 static bool hmap_iterator_has_next(Iterator *it);
461 static void *hmap_iterator_next(Iterator *it);
462 static void hmap_iterator_remove(Iterator *it);
463 static void hmap_iterator_restart(Iterator *iterator);
464
465 static IteratorClass hmap_iterator_class = {
466 hmap_iterator_has_next,
467 hmap_iterator_next,
468 hmap_iterator_remove,
469 NULL,
470 hmap_iterator_restart,
471 NULL,
472 NULL,
473 };
474
475 static bool
hmap_iterator_has_next(Iterator * it)476 hmap_iterator_has_next(Iterator *it)
477 {
478 return ((HMapIterator *) it)->entry != NULL;
479 }
480
481 static void *
hmap_iterator_next(Iterator * iterator)482 hmap_iterator_next(Iterator *iterator)
483 {
484 HMapIterator *it = (HMapIterator *) iterator;
485 HMap *map = it->map;
486 void *data;
487
488 if (it->entry == NULL)
489 return NULL;
490
491 data = it->entry->value;
492 it->previous_entry = it->entry;
493
494 it->entry = it->entry->next;
495 if (it->entry == NULL) {
496 uint32_t i = it->index + 1;
497 for (; i < map->buckets_length && map->buckets[i] == NULL; i++);
498 it->index = i;
499 it->entry = (i < map->buckets_length ? map->buckets[i] : NULL);
500 }
501
502 return data;
503 }
504
505 static void
hmap_iterator_remove(Iterator * iterator)506 hmap_iterator_remove(Iterator *iterator)
507 {
508 HMapIterator *it = (HMapIterator *) iterator;
509 if (it->previous_entry != NULL) {
510 hmap_remove(it->map, it->previous_entry->key);
511 it->previous_entry = NULL;
512 }
513 }
514
515 static void
hmap_iterator_restart(Iterator * iterator)516 hmap_iterator_restart(Iterator *iterator)
517 {
518 HMapIterator *it = (HMapIterator *) iterator;
519 uint32_t i;
520
521 for (i = 0; i < it->map->buckets_length && it->map->buckets[i] == NULL; i++);
522 it->index = i;
523 it->entry = (i < it->map->buckets_length ? it->map->buckets[i] : NULL);
524 it->previous_entry = NULL;
525 }
526
527 Iterator *
hmap_value_iterator(HMap * map)528 hmap_value_iterator(HMap *map)
529 {
530 HMapIterator *it = xmalloc(sizeof(HMapIterator));
531 it->iterator.class = &hmap_iterator_class;
532 it->map = map;
533 hmap_iterator_restart(&it->iterator);
534 return &it->iterator;
535 }
536
537 /* Main Windows BMP data structures */
538
539 typedef struct {
540 uint8_t width;
541 uint8_t height;
542 uint8_t color_count;
543 uint8_t reserved;
544 } Win32IconResDir;
545
546 typedef struct {
547 uint16_t width;
548 uint16_t height;
549 } Win32CursorDir;
550
551 typedef struct {
552 union {
553 Win32IconResDir icon;
554 Win32CursorDir cursor;
555 } res_info;
556 uint16_t plane_count;
557 uint16_t bpp;
558 uint32_t bytes_in_res;
559 uint16_t res_id;
560 } Win32CursorIconDirEntry;
561
562 typedef struct {
563 uint16_t reserved;
564 uint16_t type;
565 uint16_t count;
566 Win32CursorIconDirEntry entries[0] PACKED;
567 } Win32CursorIconDir;
568
569 typedef struct {
570 uint8_t width;
571 uint8_t height;
572 uint8_t color_count;
573 uint8_t reserved;
574 uint16_t hotspot_x; /* sometimes planes... */
575 uint16_t hotspot_y; /* sometimes bpp... */
576 uint32_t dib_size;
577 uint32_t dib_offset;
578 } Win32CursorIconFileDirEntry;
579
580 typedef struct {
581 uint16_t reserved;
582 uint16_t type;
583 uint16_t count;
584 Win32CursorIconFileDirEntry entries[0] PACKED;
585 } Win32CursorIconFileDir;
586
587 typedef struct {
588 uint32_t size;
589 int32_t width;
590 int32_t height;
591 uint16_t planes;
592 uint16_t bpp;
593 uint32_t compression;
594 uint32_t size_image;
595 int32_t x_res;
596 int32_t y_res;
597 uint32_t clr_used;
598 uint32_t clr_important;
599 } Win32BitmapInfoHeader;
600
601 typedef struct {
602 uint8_t blue;
603 uint8_t green;
604 uint8_t red;
605 uint8_t reserved;
606 } Win32RGBQuad;
607
608 #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
609 #define IMAGE_SIZEOF_SHORT_NAME 8
610
611 #define IMAGE_RESOURCE_NAME_IS_STRING 0x80000000
612 #define IMAGE_RESOURCE_DATA_IS_DIRECTORY 0x80000000
613
614 #define PE_HEADER(module) \
615 ((Win32ImageNTHeaders*)((uint8_t *)(module) + \
616 (((DOSImageHeader*)(module))->lfanew)))
617
618 #define PE_SECTIONS(module) \
619 ((Win32ImageSectionHeader *)((uint8_t *) &PE_HEADER(module)->optional_header + \
620 PE_HEADER(module)->file_header.size_of_optional_header))
621
622 #define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */
623 #define IMAGE_OS2_SIGNATURE 0x454E /* NE */
624 #define IMAGE_OS2_SIGNATURE_LE 0x454C /* LE */
625 #define IMAGE_OS2_SIGNATURE_LX 0x584C /* LX */
626 #define IMAGE_VXD_SIGNATURE 0x454C /* LE */
627 #define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */
628
629 #define IMAGE_SCN_CNT_CODE 0x00000020
630 #define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
631 #define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080
632
633 #define IMAGE_DIRECTORY_ENTRY_EXPORT 0
634 #define IMAGE_DIRECTORY_ENTRY_IMPORT 1
635 #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
636 #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
637 #define IMAGE_DIRECTORY_ENTRY_SECURITY 4
638 #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
639 #define IMAGE_DIRECTORY_ENTRY_DEBUG 6
640 #define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7
641 #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* (MIPS GP) */
642 #define IMAGE_DIRECTORY_ENTRY_TLS 9
643 #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
644 #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11
645 #define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */
646 #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13
647 #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14
648
649 #define RT_CURSOR 1
650 #define RT_BITMAP 2
651 #define RT_ICON 3
652 #define RT_MENU 4
653 #define RT_DIALOG 5
654 #define RT_STRING 6
655 #define RT_FONTDIR 7
656 #define RT_FONT 8
657 #define RT_ACCELERATOR 9
658 #define RT_RCDATA 10
659 #define RT_MESSAGELIST 11
660 #define RT_GROUP_CURSOR 12
661 #define RT_GROUP_ICON 14
662
663 typedef struct {
664 union {
665 struct {
666 #if BITFIELDS_BIGENDIAN
667 unsigned name_is_string:1;
668 unsigned name_offset:31;
669 #else
670 unsigned name_offset:31;
671 unsigned name_is_string:1;
672 #endif
673 } s1;
674 uint32_t name;
675 struct {
676 #if WORDS_BIGENDIAN
677 uint16_t __pad;
678 uint16_t id;
679 #else
680 uint16_t id;
681 uint16_t __pad;
682 #endif
683 } s2;
684 } u1;
685 union {
686 uint32_t offset_to_data;
687 struct {
688 #if BITFIELDS_BIGENDIAN
689 unsigned data_is_directory:1;
690 unsigned offset_to_directory:31;
691 #else
692 unsigned offset_to_directory:31;
693 unsigned data_is_directory:1;
694 #endif
695 } s;
696 } u2;
697 } Win32ImageResourceDirectoryEntry;
698
699 typedef struct {
700 uint16_t type_id;
701 uint16_t count;
702 uint32_t reserved;
703 } Win16NETypeInfo;
704
705 typedef struct {
706 uint16_t offset;
707 uint16_t length;
708 uint16_t flags;
709 uint16_t id;
710 uint16_t handle;
711 uint16_t usage;
712 } Win16NENameInfo;
713
714 typedef struct {
715 uint16_t magic;
716 uint8_t ver;
717 uint8_t rev;
718 uint16_t enttab;
719 uint16_t cbenttab;
720 int32_t crc;
721 uint16_t flags;
722 uint16_t autodata;
723 uint16_t heap;
724 uint16_t stack;
725 uint32_t csip;
726 uint32_t sssp;
727 uint16_t cseg;
728 uint16_t cmod;
729 uint16_t cbnrestab;
730 uint16_t segtab;
731 uint16_t rsrctab;
732 uint16_t restab;
733 uint16_t modtab;
734 uint16_t imptab;
735 uint32_t nrestab;
736 uint16_t cmovent;
737 uint16_t align;
738 uint16_t cres;
739 uint8_t exetyp;
740 uint8_t flagsothers;
741 uint16_t fastload_offset;
742 uint16_t fastload_length;
743 uint16_t swaparea;
744 uint16_t expver;
745 } OS2ImageHeader;
746
747 typedef struct {
748 uint16_t magic;
749 uint16_t cblp;
750 uint16_t cp;
751 uint16_t crlc;
752 uint16_t cparhdr;
753 uint16_t minalloc;
754 uint16_t maxalloc;
755 uint16_t ss;
756 uint16_t sp;
757 uint16_t csum;
758 uint16_t ip;
759 uint16_t cs;
760 uint16_t lfarlc;
761 uint16_t ovno;
762 uint16_t res[4];
763 uint16_t oemid;
764 uint16_t oeminfo;
765 uint16_t res2[10];
766 uint32_t lfanew;
767 } DOSImageHeader;
768
769 typedef struct {
770 uint16_t machine;
771 uint16_t number_of_sections;
772 uint32_t time_date_stamp;
773 uint32_t pointer_to_symbol_table;
774 uint32_t number_of_symbols;
775 uint16_t size_of_optional_header;
776 uint16_t characteristics;
777 } Win32ImageFileHeader;
778
779 typedef struct {
780 uint32_t virtual_address;
781 uint32_t size;
782 } Win32ImageDataDirectory;
783
784 typedef struct {
785 uint16_t magic;
786 uint8_t major_linker_version;
787 uint8_t minor_linker_version;
788 uint32_t size_of_code;
789 uint32_t size_of_initialized_data;
790 uint32_t size_of_uninitialized_data;
791 uint32_t address_of_entry_point;
792 uint32_t base_of_code;
793 uint32_t base_of_data;
794 uint32_t image_base;
795 uint32_t section_alignment;
796 uint32_t file_alignment;
797 uint16_t major_operating_system_version;
798 uint16_t minor_operating_system_version;
799 uint16_t major_image_version;
800 uint16_t minor_image_version;
801 uint16_t major_subsystem_version;
802 uint16_t minor_subsystem_version;
803 uint32_t win32_version_value;
804 uint32_t size_of_image;
805 uint32_t size_of_headers;
806 uint32_t checksum;
807 uint16_t subsystem;
808 uint16_t dll_characteristics;
809 uint32_t size_of_stack_reserve;
810 uint32_t size_of_stack_commit;
811 uint32_t size_of_heap_reserve;
812 uint32_t size_of_heap_commit;
813 uint32_t loader_flags;
814 uint32_t number_of_rva_and_sizes;
815 Win32ImageDataDirectory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
816 } Win32ImageOptionalHeader;
817
818 typedef struct {
819 uint32_t signature;
820 Win32ImageFileHeader file_header;
821 Win32ImageOptionalHeader optional_header;
822 } Win32ImageNTHeaders;
823
824 typedef struct {
825 uint8_t name[IMAGE_SIZEOF_SHORT_NAME];
826 union {
827 uint32_t physical_address;
828 uint32_t virtual_size;
829 } misc;
830 uint32_t virtual_address;
831 uint32_t size_of_raw_data;
832 uint32_t pointer_to_raw_data;
833 uint32_t pointer_to_relocations;
834 uint32_t pointer_to_linenumbers;
835 uint16_t number_of_relocations;
836 uint16_t number_of_linenumbers;
837 uint32_t characteristics;
838 } Win32ImageSectionHeader;
839
840 typedef struct {
841 uint32_t offset_to_data;
842 uint32_t size;
843 uint32_t code_page;
844 uint32_t resource_handle;
845 } Win32ImageResourceDataEntry;
846
847 typedef struct {
848 uint32_t characteristics;
849 uint32_t time_date_stamp;
850 uint16_t major_version;
851 uint16_t minor_version;
852 uint16_t number_of_named_entries;
853 uint16_t number_of_id_entries;
854 } Win32ImageResourceDirectory;
855
856 #if WORDS_BIGENDIAN
857
858 static void fix_win32_cursor_icon_file_dir_endian(Win32CursorIconFileDir *obj);
859 static void fix_win32_bitmap_info_header_endian(Win32BitmapInfoHeader *obj);
860 static void fix_win32_cursor_icon_file_dir_entry_endian(Win32CursorIconFileDirEntry *obj);
861 static void fix_win32_image_data_directory(Win32ImageDataDirectory *obj);
862 static void fix_os2_image_header_endian(OS2ImageHeader *obj);
863 static void fix_win32_image_section_header(Win32ImageSectionHeader *obj);
864 static void fix_win32_image_header_endian(Win32ImageNTHeaders *obj);
865
866 #else
867
868 #define fix_win32_bitmap_info_header_endian(x)
869 #define fix_win32_cursor_icon_file_dir_endian(x)
870 #define fix_win32_cursor_icon_file_dir_entry_endian(x)
871 #define fix_win32_image_data_directory(x)
872 #define fix_os2_image_header_endian(x)
873 #define fix_win32_image_section_header(x)
874 #define fix_win32_image_header_endian(x)
875
876 #endif /* WORDS_BIGENDIAN */
877
878 #define ROW_BYTES(bits) ((((bits) + 31) >> 5) << 2)
879
880 typedef struct _Palette {
881 HMap *map;
882 Iterator *it;
883 } Palette;
884
885 typedef struct {
886 uint8_t red;
887 uint8_t green;
888 uint8_t blue;
889 uint32_t index;
890 } PaletteColor;
891
892 static uint32_t
color_hash(PaletteColor * color)893 color_hash(PaletteColor *color)
894 {
895 return (color->red << 16) | (color->green << 8) | color->blue;
896 }
897
898 static int32_t
color_compare(PaletteColor * c1,PaletteColor * c2)899 color_compare(PaletteColor *c1, PaletteColor *c2)
900 {
901 if (c1->red != c2->red)
902 return c1->red - c2->red;
903 if (c1->green != c2->green)
904 return c1->green - c2->green;
905 if (c1->blue != c2->blue)
906 return c1->blue - c2->blue;
907 return 0;
908 }
909
910 static Palette *
palette_new(void)911 palette_new(void)
912 {
913 Palette *palette = xmalloc(sizeof(Palette));
914 palette->map = hmap_new();
915 palette->it = NULL;
916 hmap_set_hash_function(palette->map, (HashFunc) color_hash);
917 hmap_set_compare_function(palette->map, (CompareFunc) color_compare);
918 return palette;
919 }
920
921 static void
palette_free(Palette * palette)922 palette_free(Palette *palette)
923 {
924 if (palette->it != NULL)
925 iterator_free(palette->it);
926 hmap_iterate_values(palette->map, free);
927 hmap_free(palette->map);
928 free(palette);
929 }
930
931 static void
palette_add(Palette * palette,uint8_t r,uint8_t g,uint8_t b)932 palette_add(Palette *palette, uint8_t r, uint8_t g, uint8_t b)
933 {
934 PaletteColor color = { r, g, b, 0 };
935
936 if (!hmap_contains_key(palette->map, &color)) {
937 PaletteColor *new_color = xmalloc(sizeof(PaletteColor));
938 new_color->red = r;
939 new_color->green = g;
940 new_color->blue = b;
941 new_color->index = 0;
942 hmap_put(palette->map, new_color, new_color);
943 }
944 }
945
946 static bool
palette_next(Palette * palette,uint8_t * r,uint8_t * g,uint8_t * b)947 palette_next(Palette *palette, uint8_t *r, uint8_t *g, uint8_t *b)
948 {
949 if (palette->it == NULL)
950 palette->it = hmap_value_iterator(palette->map);
951 if (iterator_has_next(palette->it)) {
952 PaletteColor *color = iterator_next(palette->it);
953 *r = color->red;
954 *g = color->green;
955 *b = color->blue;
956 return true;
957 }
958 iterator_free(palette->it);
959 palette->it = NULL;
960 return false;
961 }
962
963 static void
palette_assign_indices(Palette * palette)964 palette_assign_indices(Palette *palette)
965 {
966 Iterator *it = hmap_value_iterator(palette->map);
967 uint32_t c;
968
969 for (c = 0; iterator_has_next(it); c++) {
970 PaletteColor *color = iterator_next(it);
971 color->index = c;
972 }
973 }
974
975 static uint32_t
palette_lookup(Palette * palette,uint8_t r,uint8_t g,uint8_t b)976 palette_lookup(Palette *palette, uint8_t r, uint8_t g, uint8_t b)
977 {
978 PaletteColor color = { r, g, b, 0 };
979 PaletteColor *real_color = hmap_get(palette->map, &color);
980 return (real_color != NULL ? real_color->index : -1);
981 }
982
983 static uint32_t
palette_count(Palette * palette)984 palette_count(Palette *palette)
985 {
986 return hmap_size(palette->map);
987 }
988
989 static void
simple_setvec(uint8_t * data,uint32_t ofs,uint8_t size,uint32_t value)990 simple_setvec(uint8_t *data, uint32_t ofs, uint8_t size, uint32_t value)
991 {
992 switch (size) {
993 case 1:
994 data[ofs/8] |= (value & 1) << (7 - ofs%8);
995 break;
996 case 2:
997 data[ofs/4] |= (value & 3) << ((3 - ofs%4) << 1);
998 break;
999 case 4:
1000 data[ofs/2] |= (value & 15) << ((1 - ofs%2) << 2);
1001 break;
1002 case 8:
1003 data[ofs] = value;
1004 break;
1005 case 16:
1006 data[2*ofs] = value;
1007 data[2*ofs+1] = (value >> 8);
1008 break;
1009 case 24:
1010 data[3*ofs] = value;
1011 data[3*ofs+1] = (value >> 8);
1012 data[3*ofs+2] = (value >> 16);
1013 break;
1014 case 32:
1015 data[4*ofs] = value;
1016 data[4*ofs+1] = (value >> 8);
1017 data[4*ofs+2] = (value >> 16);
1018 data[4*ofs+3] = (value >> 24);
1019 break;
1020 }
1021 }
1022
1023 static uint32_t
simple_vec(uint8_t * data,uint32_t ofs,uint8_t size)1024 simple_vec(uint8_t *data, uint32_t ofs, uint8_t size)
1025 {
1026 switch (size) {
1027 case 1:
1028 return (data[ofs/8] >> (7 - ofs%8)) & 1;
1029 case 2:
1030 return (data[ofs/4] >> ((3 - ofs%4) << 1)) & 3;
1031 case 4:
1032 return (data[ofs/2] >> ((1 - ofs%2) << 2)) & 15;
1033 case 8:
1034 return data[ofs];
1035 case 16:
1036 return data[2*ofs] | data[2*ofs+1] << 8;
1037 case 24:
1038 return data[3*ofs] | data[3*ofs+1] << 8 | data[3*ofs+2] << 16;
1039 case 32:
1040 return data[4*ofs] | data[4*ofs+1] << 8 | data[4*ofs+2] << 16 | data[4*ofs+3] << 24;
1041 }
1042
1043 return 0;
1044 }
1045
1046 static bool
xfread(void * ptr,size_t size,FILE * stream)1047 xfread(void *ptr, size_t size, FILE *stream)
1048 {
1049 if (fread(ptr, size, 1, stream) < 1) {
1050 if (ferror(stream))
1051 warn_errno("cannot read file");
1052 else
1053 warn("premature end");
1054 return false;
1055 }
1056 return true;
1057 }
1058
1059 typedef struct {
1060 uint32_t capacity;
1061 uint32_t count;
1062 char value[0];
1063 } StringBuffer;
1064
1065 static inline StringBuffer *
get_string_buffer(char ** buf)1066 get_string_buffer(char **buf)
1067 {
1068 return (StringBuffer *) (*buf - sizeof(StringBuffer));
1069 }
1070
1071 static inline char *
new_strbuf(uint32_t capacity,uint32_t count)1072 new_strbuf(uint32_t capacity, uint32_t count)
1073 {
1074 StringBuffer *strbuf;
1075 strbuf = xmalloc(sizeof(StringBuffer) + sizeof(char)*capacity);
1076 strbuf->capacity = capacity;
1077 strbuf->count = count;
1078 return strbuf->value;
1079 }
1080
1081 static inline void
ensure_capacity(char ** buf,StringBuffer ** strbuf,uint32_t min_capacity)1082 ensure_capacity(char **buf, StringBuffer **strbuf, uint32_t min_capacity)
1083 {
1084 if (min_capacity > (*strbuf)->capacity) {
1085 uint32_t max = (min_capacity > (*strbuf)->count ? (*strbuf)->count*2+2 : (*strbuf)->count);
1086 min_capacity = MAX(min_capacity, max);
1087
1088 *strbuf = xrealloc(*strbuf, sizeof(StringBuffer) + sizeof(char) * min_capacity);
1089 (*strbuf)->capacity = min_capacity;
1090 *buf = (*strbuf)->value;
1091 }
1092 }
1093
1094 /**
1095 * Read and discard some number of bytes from a stream.
1096 */
1097 static int
fskip(FILE * file,uint32_t bytes)1098 fskip(FILE *file, uint32_t bytes)
1099 {
1100 for (; bytes > 0; bytes--) {
1101 if (fgetc(file) == EOF)
1102 return -1;
1103 }
1104 return 0;
1105 }
1106
1107 /**
1108 * Write a number of bytes of the same value to a stream.
1109 */
1110 static int
fpad(FILE * file,char byte,uint32_t bytes)1111 fpad(FILE *file, char byte, uint32_t bytes)
1112 {
1113 for (; bytes > 0; bytes--) {
1114 if (fwrite(&byte, 1, 1, file) != 1)
1115 return -1;
1116 }
1117 return 0;
1118 }
1119
1120 static bool
create_icon(int filec,char ** filev,char * outname,bool icon_mode,int32_t hotspot_x,int32_t hotspot_y,int32_t alpha_threshold,int32_t bpp)1121 create_icon(int filec, char **filev, char *outname, bool icon_mode, int32_t hotspot_x, int32_t hotspot_y, int32_t alpha_threshold, int32_t bpp)
1122 {
1123 struct {
1124 FILE *in;
1125 png_structp png_ptr;
1126 png_infop info_ptr;
1127 uint32_t bpp;
1128 uint32_t palette_count;
1129 uint32_t image_size;
1130 uint32_t mask_size;
1131 uint32_t width;
1132 uint32_t height;
1133 uint8_t *image_data;
1134 uint8_t **row_datas;
1135 Palette *palette;
1136 } *img;
1137
1138 Win32CursorIconFileDir dir;
1139 FILE *out = NULL;
1140 uint32_t c, d, x;
1141 uint32_t dib_start;
1142 png_byte ct;
1143
1144 img = xcalloc(filec * sizeof(*img));
1145
1146 for (c = 0; c < filec; c++) {
1147 char header[8];
1148 uint32_t row_bytes;
1149 uint8_t transparency[256];
1150 uint16_t transparency_count;
1151 bool need_transparency;
1152
1153 img[c].in = fopen(filev[c], "r");
1154 if (img[c].in == NULL) {
1155 warn_errno("(#%d) cannot open file", c);
1156 goto cleanup;
1157 }
1158 if (!xfread(header, 8, img[c].in))
1159 goto cleanup;
1160 if (png_sig_cmp((png_bytep)header, 0, 8)) {
1161
1162 warn("(#%d) not a png file", c);
1163 goto cleanup;
1164 }
1165
1166 img[c].png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL /*user_error_fn, user_warning_fn*/);
1167 if (img[c].png_ptr == NULL) {
1168 warn("cannot initialize PNG library");
1169 goto cleanup;
1170 }
1171 img[c].info_ptr = png_create_info_struct(img[c].png_ptr);
1172 if (img[c].info_ptr == NULL) {
1173 warn("cannot create PNG info structure - out of memory");
1174 goto cleanup;
1175 }
1176
1177 png_init_io(img[c].png_ptr, img[c].in);
1178 png_set_sig_bytes(img[c].png_ptr, 8);
1179 png_set_strip_16(img[c].png_ptr);
1180 png_set_expand(img[c].png_ptr);
1181 png_set_gray_to_rgb(img[c].png_ptr);
1182 png_set_interlace_handling(img[c].png_ptr);
1183 png_set_filler(img[c].png_ptr, 0xFF, PNG_FILLER_AFTER);
1184 png_read_info(img[c].png_ptr, img[c].info_ptr);
1185 png_read_update_info(img[c].png_ptr, img[c].info_ptr);
1186
1187 img[c].width = png_get_image_width(img[c].png_ptr, img[c].info_ptr);
1188 /*if (img[c].width > 255) {
1189 warn("image too wide (max 255, was %d pixels)", img[c].width);
1190 goto cleanup;
1191 }*/
1192 img[c].height = png_get_image_height(img[c].png_ptr, img[c].info_ptr);
1193 /*if (img[c].height > 255) {
1194 warn("image too tall (max 255, was %d pixels)", img[c].height);
1195 goto cleanup;
1196 }*/
1197
1198 row_bytes = png_get_rowbytes(img[c].png_ptr, img[c].info_ptr);
1199 img[c].row_datas = xmalloc(img[c].height * sizeof(png_bytep *));
1200 img[c].row_datas[0] = xmalloc(img[c].height * row_bytes);
1201 for (d = 1; d < img[c].height; d++)
1202 img[c].row_datas[d] = img[c].row_datas[d-1] + row_bytes;
1203 png_read_rows(img[c].png_ptr, img[c].row_datas, NULL, img[c].height);
1204
1205 ct = png_get_color_type(img[c].png_ptr, img[c].info_ptr);
1206 img[c].palette = palette_new();
1207
1208
1209 /* Count number of necessary colors in palette and number of transparencies */
1210 memset(transparency, 0, 256);
1211 for (d = 0; d < img[c].height; d++) {
1212 png_bytep row = img[c].row_datas[d];
1213 for (x = 0; x < img[c].width; x++) {
1214 if (palette_count(img[c].palette) <= (1 << 8))
1215 palette_add(img[c].palette, row[4*x+0], row[4*x+1], row[4*x+2]);
1216 if (ct & PNG_COLOR_MASK_ALPHA)
1217 transparency[row[4*x+3]] = 1;
1218 }
1219 }
1220 transparency_count = 0;
1221 for (d = 0; d < 256; d++)
1222 transparency_count += transparency[d];
1223
1224 /* If there are more than two steps of transparency, or if the
1225 * two steps are NOT either entirely off (0) and entirely on (255),
1226 * then we will lose transparency information if bpp is not 32.
1227 */
1228 need_transparency =
1229 transparency_count > 2
1230 ||
1231 (transparency_count == 2 && (transparency[0] == 0 || transparency[255] == 0));
1232
1233 /* Can we keep all colors in a palette? */
1234 if (need_transparency) {
1235 if (bpp != -1) {
1236 if (bpp != 32)
1237 warn("decreasing bit depth will discard variable transparency", transparency_count);
1238 /* Why 24 and not bpp? Otherwise we might decrease below what's possible
1239 * due to number of colors in image. The real decrease happens below. */
1240 img[c].bpp = 24;
1241 } else {
1242 img[c].bpp = 32;
1243 }
1244 img[c].palette_count = 0;
1245 }
1246 else if (palette_count(img[c].palette) <= 256) {
1247 for (d = 1; palette_count(img[c].palette) > 1 << d; d <<= 1);
1248 if (d == 2) /* four colors (two bits) are not supported */
1249 d = 4;
1250 img[c].bpp = d;
1251 img[c].palette_count = 1 << d;
1252 }
1253 else {
1254 img[c].bpp = 24;
1255 img[c].palette_count = 0;
1256 }
1257
1258 /* Does the user want to change number of bits per pixel? */
1259 if (bpp != -1) {
1260 if (img[c].bpp == bpp) {
1261 /* No operation */
1262 } else if (img[c].bpp < bpp) {
1263 img[c].bpp = bpp;
1264 img[c].palette_count = (bpp > 16 ? 0 : 1 << bpp);
1265 } else {
1266 warn("cannot decrease bit depth from %d to %d, bit depth not changed", img[c].bpp, bpp);
1267 }
1268 }
1269
1270 img[c].image_size = img[c].height * ROW_BYTES(img[c].width * img[c].bpp);
1271 img[c].mask_size = img[c].height * ROW_BYTES(img[c].width);
1272
1273 }
1274
1275 out = fopen(outname, "w");
1276 if (out == NULL) {
1277 warn_errno("cannot create file");
1278 goto cleanup;
1279 }
1280
1281 dir.reserved = 0;
1282 dir.type = (icon_mode ? 1 : 2);
1283 dir.count = filec;
1284 fix_win32_cursor_icon_file_dir_endian(&dir);
1285 if (fwrite(&dir, sizeof(Win32CursorIconFileDir), 1, out) != 1) {
1286 warn_errno("cannot write to file");
1287 goto cleanup;
1288 }
1289
1290 dib_start = sizeof(Win32CursorIconFileDir) + filec * sizeof(Win32CursorIconFileDirEntry);
1291 for (c = 0; c < filec; c++) {
1292 Win32CursorIconFileDirEntry entry;
1293
1294 entry.width = MIN(255, img[c].width);
1295 entry.height = MIN(255, img[c].height);
1296 entry.reserved = 0;
1297 if (icon_mode) {
1298 entry.hotspot_x = 0; /* some mistake this for planes (XXX) */
1299 entry.hotspot_y = 0; /* some mistake this for bpp (XXX) */
1300 } else {
1301 entry.hotspot_x = hotspot_x; /* some mistake this for planes (XXX) */
1302 entry.hotspot_y = hotspot_y; /* some mistake this for bpp (XXX) */
1303 }
1304 entry.dib_offset = dib_start;
1305 entry.color_count = (img[c].bpp >= 8 ? 0 : 1 << img[c].bpp);
1306 entry.dib_size = img[c].palette_count * sizeof(Win32RGBQuad)
1307 + sizeof(Win32BitmapInfoHeader)
1308 + img[c].image_size
1309 + img[c].mask_size;
1310
1311 dib_start += entry.dib_size;
1312
1313 fix_win32_cursor_icon_file_dir_entry_endian(&entry);
1314 if (fwrite(&entry, sizeof(Win32CursorIconFileDirEntry), 1, out) != 1) {
1315 warn_errno("cannot write to file");
1316 goto cleanup;
1317 }
1318
1319 }
1320
1321 for (c = 0; c < filec; c++) {
1322 Win32BitmapInfoHeader bitmap;
1323
1324 bitmap.size = sizeof(Win32BitmapInfoHeader);
1325 bitmap.width = png_get_image_width(img[c].png_ptr, img[c].info_ptr);
1326 bitmap.height = png_get_image_height(img[c].png_ptr, img[c].info_ptr) * 2;
1327 bitmap.planes = 1; // appears to be 1 always (XXX)
1328 bitmap.bpp = img[c].bpp;
1329 bitmap.compression = 0;
1330 bitmap.x_res = 0; // should be 0 always
1331 bitmap.y_res = 0; // should be 0 always
1332 bitmap.clr_important = 0; // should be 0 always
1333 bitmap.clr_used = img[c].palette_count;
1334 bitmap.size_image = img[c].image_size; // appears to be ok here (may be image_size+mask_size or 0, XXX)
1335
1336 fix_win32_bitmap_info_header_endian(&bitmap);
1337 if (fwrite(&bitmap, sizeof(Win32BitmapInfoHeader), 1, out) != 1) {
1338 warn_errno("cannot write to file");
1339 goto cleanup;
1340 }
1341
1342 if (img[c].bpp <= 16) {
1343 Win32RGBQuad color;
1344
1345 palette_assign_indices(img[c].palette);
1346 color.reserved = 0;
1347 while (palette_next(img[c].palette, &color.red, &color.green, &color.blue))
1348 fwrite(&color, sizeof(Win32RGBQuad), 1, out);
1349
1350 /* Pad with empty colors. The reason we do this is because we
1351 * specify bitmap.clr_used as a base of 2. The latter is probably
1352 * not necessary according to the original specs, but many
1353 * programs that read icons assume it. Especially gdk-pixbuf.
1354 */
1355 memclear(&color, sizeof(Win32RGBQuad));
1356 for (d = palette_count(img[c].palette); d < 1 << img[c].bpp; d++)
1357 fwrite(&color, sizeof(Win32RGBQuad), 1, out);
1358 }
1359
1360 img[c].image_data = xcalloc(img[c].image_size);
1361
1362 for (d = 0; d < img[c].height; d++) {
1363 png_bytep row = img[c].row_datas[img[c].height - d - 1];
1364 if (img[c].bpp < 24) {
1365 uint32_t imod = d * (img[c].image_size/img[c].height) * 8 / img[c].bpp;
1366 for (x = 0; x < img[c].width; x++) {
1367 uint32_t color;
1368 color = palette_lookup(img[c].palette, row[4*x+0], row[4*x+1], row[4*x+2]);
1369 simple_setvec(img[c].image_data, x+imod, img[c].bpp, color);
1370 }
1371 } else if (img[c].bpp == 24) {
1372 uint32_t irow = d * (img[c].image_size/img[c].height);
1373 for (x = 0; x < img[c].width; x++) {
1374 img[c].image_data[3*x+0 + irow] = row[4*x+2];
1375 img[c].image_data[3*x+1 + irow] = row[4*x+1];
1376 img[c].image_data[3*x+2 + irow] = row[4*x+0];
1377 }
1378 } else if (img[c].bpp == 32) {
1379 uint32_t irow = d * (img[c].image_size/img[c].height);
1380 for (x = 0; x < img[c].width; x++) {
1381 img[c].image_data[4*x+0 + irow] = row[4*x+2];
1382 img[c].image_data[4*x+1 + irow] = row[4*x+1];
1383 img[c].image_data[4*x+2 + irow] = row[4*x+0];
1384 img[c].image_data[4*x+3 + irow] = row[4*x+3];
1385 }
1386 }
1387 }
1388
1389 if (fwrite(img[c].image_data, img[c].image_size, 1, out) != 1) {
1390 warn_errno("cannot write to file");
1391 goto cleanup;
1392 }
1393
1394 for (d = 0; d < img[c].height; d++) {
1395 png_bytep row = img[c].row_datas[img[c].height - d - 1];
1396
1397 for (x = 0; x < img[c].width; x += 8) {
1398 uint8_t mask = 0;
1399 mask |= (row[4*(x+0)+3] <= alpha_threshold ? 1 << 7 : 0);
1400 mask |= (row[4*(x+1)+3] <= alpha_threshold ? 1 << 6 : 0);
1401 mask |= (row[4*(x+2)+3] <= alpha_threshold ? 1 << 5 : 0);
1402 mask |= (row[4*(x+3)+3] <= alpha_threshold ? 1 << 4 : 0);
1403 mask |= (row[4*(x+4)+3] <= alpha_threshold ? 1 << 3 : 0);
1404 mask |= (row[4*(x+5)+3] <= alpha_threshold ? 1 << 2 : 0);
1405 mask |= (row[4*(x+6)+3] <= alpha_threshold ? 1 << 1 : 0);
1406 mask |= (row[4*(x+7)+3] <= alpha_threshold ? 1 << 0 : 0);
1407 fputc(mask, out);
1408 }
1409
1410 fpad(out, 0, img[c].mask_size/img[c].height - x/8);
1411 }
1412
1413 free(img[c].image_data);
1414 palette_free(img[c].palette);
1415 free(img[c].row_datas[0]);
1416 free(img[c].row_datas);
1417 png_read_end(img[c].png_ptr, img[c].info_ptr);
1418 png_destroy_read_struct(&img[c].png_ptr, &img[c].info_ptr, NULL);
1419 fclose(img[c].in);
1420 memclear(&img[c], sizeof(*img));
1421 }
1422
1423 free(img);
1424 if (out) fclose(out);
1425 return true;
1426
1427 cleanup:
1428
1429 for (c = 0; c < filec; c++) {
1430 if (img[c].image_data != NULL)
1431 free(img[c].image_data);
1432 if (img[c].palette != NULL)
1433 palette_free(img[c].palette);
1434 if (img[c].row_datas != NULL && img[c].row_datas[0] != NULL) {
1435 free(img[c].row_datas[0]);
1436 free(img[c].row_datas);
1437 }
1438 if (img[c].png_ptr != NULL)
1439 png_destroy_read_struct(&img[c].png_ptr, &img[c].info_ptr, NULL);
1440 if (img[c].in != NULL)
1441 fclose(img[c].in);
1442 }
1443 free(img);
1444 if (out) fclose(out);
1445 return false;
1446 }
1447
1448 static int32_t image_index;
1449 static int32_t width = -1;
1450 static int32_t height = -1;
1451 static int32_t bitdepth = -1;
1452 static int32_t palettesize = -1;
1453 static int32_t hotspot_x = 0;
1454 static int32_t hotspot_y = 0;
1455 static bool hotspot_x_set = false;
1456 static bool hotspot_y_set = false;
1457 static int32_t alpha_threshold = 127;
1458 static bool icon_only = false;
1459 static bool cursor_only = false;
1460
1461 static bool
filter(int i,int w,int h,int bd,int ps,bool icon,int hx,int hy)1462 filter(int i, int w, int h, int bd, int ps, bool icon, int hx, int hy)
1463 {
1464 if (image_index != -1 && i != image_index)
1465 return false;
1466 if (width != -1 && w != width)
1467 return false;
1468 if (height != -1 && h != height)
1469 return false;
1470 if (bitdepth != -1 && bd != bitdepth)
1471 return false;
1472 if (palettesize != -1 && ps != palettesize)
1473 return false;
1474 if ((icon_only && !icon) || (cursor_only && icon))
1475 return false;
1476 if (hotspot_x_set && hx != hotspot_x)
1477 return false;
1478 if (hotspot_y_set && hy != hotspot_y)
1479 return false;
1480 return true;
1481 }
1482
1483 static int
extract_icons(FILE * in,Image ** out_image,bool extractmode)1484 extract_icons(FILE *in, Image **out_image, bool extractmode)
1485 {
1486 Win32CursorIconFileDir dir;
1487 Win32CursorIconFileDirEntry *entries = NULL;
1488 uint32_t offset;
1489 uint32_t c, d;
1490 int completed;
1491 int matched = 0;
1492 Image *out = NULL;
1493
1494 if (!xfread(&dir, sizeof(Win32CursorIconFileDir), in))
1495 goto cleanup;
1496 fix_win32_cursor_icon_file_dir_endian(&dir);
1497
1498 if (dir.reserved != 0) {
1499 warn("not an icon or cursor file (reserved non-zero)");
1500 goto cleanup;
1501 }
1502 if (dir.type != 1 && dir.type != 2) {
1503 warn("not an icon or cursor file (wrong type)");
1504 goto cleanup;
1505 }
1506
1507 entries = xmalloc(dir.count * sizeof(Win32CursorIconFileDirEntry));
1508 for (c = 0; c < dir.count; c++) {
1509 if (!xfread(&entries[c], sizeof(Win32CursorIconFileDirEntry), in))
1510 goto cleanup;
1511 fix_win32_cursor_icon_file_dir_entry_endian(&entries[c]);
1512 if (entries[c].reserved != 0)
1513 warn("(#%d) reserved is not zero", c+1);
1514 }
1515 offset = sizeof(Win32CursorIconFileDir) + dir.count * sizeof(Win32CursorIconFileDirEntry);
1516
1517 warn("number of image entries :%d", dir.count);
1518
1519 for (completed = 0; completed < dir.count; ) {
1520 uint32_t min_offset = UINT32_MAX;
1521 int previous = completed;
1522
1523 for (c = 0; c < dir.count; c++) {
1524 int number = c;
1525 if (entries[c].dib_offset == offset) {
1526 Win32BitmapInfoHeader bitmap;
1527 Win32RGBQuad *palette = NULL;
1528 uint32_t palette_count = 0;
1529 uint32_t image_size, mask_size;
1530 uint32_t width = 0, height = 0;
1531 uint8_t *image_data = NULL, *mask_data = NULL;
1532 unsigned char *row;
1533 unsigned char *alpha;
1534
1535 if (!xfread(&bitmap, sizeof(Win32BitmapInfoHeader), in))
1536 goto local_cleanup;
1537
1538 fix_win32_bitmap_info_header_endian(&bitmap);
1539 if (bitmap.size < sizeof(Win32BitmapInfoHeader)) {
1540 warn("(#%d) bitmap header is too short", number);
1541 goto local_cleanup;
1542 }
1543 if (bitmap.compression != 0) {
1544 warn("(#%d) compressed image found", number);
1545 fseek(in, offset, SEEK_SET);
1546 mask_size = 0;
1547 image_size = bitmap.size_image;
1548 goto direct;
1549 }
1550 if (bitmap.x_res != 0)
1551 warn("(#%d) x_res field in bitmap should be zero", number);
1552 if (bitmap.y_res != 0)
1553 warn("(#%d) y_res field in bitmap should be zero", number);
1554 if (bitmap.clr_important != 0)
1555 warn("(#%d) clr_important field in bitmap should be zero", number);
1556 if (bitmap.planes != 1)
1557 warn("(#%d) planes field in bitmap should be one", number);
1558 if (bitmap.size != sizeof(Win32BitmapInfoHeader)) {
1559 uint32_t skip = bitmap.size - sizeof(Win32BitmapInfoHeader);
1560 warn("(#%d) skipping %d bytes of extended bitmap header", number, skip);
1561 fskip(in, skip);
1562 }
1563 offset += bitmap.size;
1564
1565 if (bitmap.clr_used != 0 || bitmap.bpp < 24) {
1566 palette_count = (bitmap.clr_used != 0 ? bitmap.clr_used : 1 << bitmap.bpp);
1567 palette = xmalloc(sizeof(Win32RGBQuad) * palette_count);
1568 if (!xfread(palette, sizeof(Win32RGBQuad) * palette_count, in))
1569 goto local_cleanup;
1570 offset += sizeof(Win32RGBQuad) * palette_count;
1571 }
1572
1573 width = bitmap.width;
1574 height = abs(bitmap.height)/2;
1575
1576 image_size = height * ROW_BYTES(width * bitmap.bpp);
1577 mask_size = height * ROW_BYTES(width);
1578
1579 if (entries[c].dib_size != bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad))
1580 warn("(#%d) incorrect total size of bitmap (%d specified; %d real)",
1581 c, entries[c].dib_size,
1582 bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad)
1583 );
1584
1585 direct:
1586 image_data = xmalloc(image_size);
1587 if (!xfread(image_data, image_size, in))
1588 goto local_cleanup;
1589
1590 if (mask_size) {
1591 mask_data = xmalloc(mask_size);
1592 if (!xfread(mask_data, mask_size, in))
1593 goto local_cleanup;
1594 }
1595
1596 offset += image_size;
1597 offset += mask_size;
1598 completed++;
1599
1600 if (!filter(completed, width, height, bitmap.bpp, palette_count, dir.type == 1,
1601 (dir.type == 1 ? 0 : entries[c].hotspot_x),
1602 (dir.type == 1 ? 0 : entries[c].hotspot_y))) {
1603 if (bitmap.compression != 0) goto local_cleanup;
1604 goto done;
1605 }
1606 matched++;
1607
1608 if (extractmode) {
1609 if (bitmap.compression) {
1610 /* Just hope it's an otherwise
1611 readable image ... */
1612 char *tmp;
1613 FILE * fp;
1614 int num = file_numpages;
1615 fp = openTempFile(&tmp);
1616 if (fp) {
1617 fwrite(image_data, 1, image_size, fp);
1618 fclose(fp);
1619 }
1620 if (fp) out = readMagic(tmp);
1621 file_numpages = num;
1622 if (num>1) file_isSpecialImage = 1;
1623 removeTempFile();
1624 goto local_cleanup;
1625 }
1626 out = ImageNew(width, height);
1627 if (out)
1628 out->alpha = (unsigned char *)
1629 xmalloc(width * height);
1630
1631 if (!out || !out->alpha) {
1632 warn_errno("(#%d) cannot create file", number);
1633 if (out) free(out->alpha);
1634 free(out);
1635 out = NULL;
1636 goto local_cleanup;
1637 }
1638 }
1639 if (bitmap.compression) goto local_cleanup;
1640
1641 if (out)
1642 for (d = 0; d < height; d++) {
1643 uint32_t x;
1644 uint32_t y = (bitmap.height < 0) ? d : height - d - 1;
1645 uint32_t imod = y * (image_size / height) * 8 / bitmap.bpp;
1646 uint32_t mmod = y * (mask_size / height) * 8;
1647 row = out->data + 3 * d * width;
1648 alpha = out->alpha + d * width;
1649
1650 for (x = 0; x < width; x++) {
1651 uint32_t color = simple_vec(image_data, x + imod, bitmap.bpp);
1652
1653 if (bitmap.bpp <= 16) {
1654 if (color >= palette_count) {
1655 warn("(#%d) color out of range in image data", number);
1656 goto local_cleanup;
1657 }
1658 row[3*x] = palette[color].red;
1659 row[3*x+1] = palette[color].green;
1660 row[3*x+2] = palette[color].blue;
1661 } else {
1662 row[3*x] = (color >> 16) & 0xFF;
1663 row[3*x+1] = (color >> 8) & 0xFF;
1664 row[3*x+2] = (color >> 0) & 0xFF;
1665 }
1666 if (bitmap.bpp == 32)
1667 alpha[x] = (color >> 24) & 0xFF;
1668 else
1669 alpha[x] = simple_vec(mask_data, x + mmod, 1) ? 0 : 0xFF;
1670 }
1671 }
1672
1673 done:
1674
1675 if (palette != NULL)
1676 free(palette);
1677 if (image_data != NULL) {
1678 free(image_data);
1679 free(mask_data);
1680 }
1681 continue;
1682
1683 local_cleanup:
1684
1685 if (palette != NULL)
1686 free(palette);
1687 if (image_data != NULL) {
1688 free(image_data);
1689 free(mask_data);
1690 }
1691 goto cleanup;
1692 } else {
1693 if (entries[c].dib_offset > offset)
1694 min_offset = MIN(min_offset, entries[c].dib_offset);
1695 }
1696 }
1697
1698 if (previous == completed) {
1699 if (min_offset < offset) {
1700 warn("offset of bitmap header incorrect (too low)");
1701 goto cleanup;
1702 }
1703 warn("skipping %d bytes of garbage at %d", min_offset-offset, offset);
1704 fskip(in, min_offset - offset);
1705 offset = min_offset;
1706 }
1707 }
1708
1709 cleanup:
1710 *out_image = out;
1711 free(entries);
1712 return matched;
1713 }
1714
1715 static char ico_magic_number[4] = {0, 0, 1, 0};
1716
1717 int
TestICO(char * file)1718 TestICO(char *file)
1719 {
1720 FILE *ico_stream;
1721 /* Values read from icon file */
1722 char iconheader[8];
1723
1724 if ((ico_stream = fopen (file, "r")) == NULL)
1725 return false;
1726
1727 if (fread (iconheader, 1, 6, ico_stream)<6 ||
1728 memcmp(iconheader, ico_magic_number, sizeof(ico_magic_number))) {
1729 fclose (ico_stream);
1730 return false;
1731 }
1732 fclose (ico_stream);
1733 return true;
1734 }
1735
1736 Image *
ReadICO(char * file)1737 ReadICO (char *file)
1738 {
1739 Image *image = NULL;
1740 FILE *fp;
1741 static char *prevfile = NULL;
1742
1743 fp = fopen(file, "r");
1744 if (!fp) return NULL;
1745
1746 if (prevfile && !strcmp(file, prevfile)) {
1747 silent = 1;
1748 } else {
1749 free(prevfile);
1750 silent = 0;
1751 prevfile = xstrdup(file);
1752 }
1753 prevfile = file;
1754
1755 image_index = -1;
1756 file_numpages = extract_icons(fp, &image, false);
1757 if (!silent)
1758 warn("number of images : %d\n", file_numpages);
1759 if (file_numpages == 0) goto failure;
1760 if (file_numpages > 1) file_isSpecialImage = 1;
1761
1762 fp = fopen(file, "r");
1763 if (!fp) return NULL;
1764 image_index = Global.numpage;
1765 silent = 1;
1766 extract_icons(fp, &image, true);
1767 failure:
1768 return image;
1769 }
1770
1771 int
WriteICO(char * file,Image * image)1772 WriteICO(char *file, Image * image)
1773 {
1774 int res;
1775 char *tmp;
1776 FILE *fp;
1777
1778 if (image->alpha) AlphaWarning("ICO", 1);
1779
1780 fp = openTempFile(&tmp);
1781 if (!fp) return 1;
1782 fclose(fp);
1783
1784 res = WritePNGn(tmp, image);
1785 if (res) return res;
1786 silent = 0;
1787 res = create_icon(1, &tmp, file, 1, hotspot_x, hotspot_y, alpha_threshold, bitdepth);
1788 removeTempFile();
1789
1790 return !res;
1791 }
1792