/* ico2png.c - Create icon and cursor files from PNG images
 *
 * minimalistic version extracted from package 'icoutil'
 * 'icoutil' is copyrighted as follows :
 *
 * Copyright (C) 1998-2005 Oskar Liljeblad
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif

#include <stdint.h>		/* POSIX/Gnulib */
#include <stdio.h>		/* C89 */
#include <stdbool.h>		/* POSIX/Gnulib */
#include <stdlib.h>		/* C89 */
#include <stdarg.h>		/* C89 */
#include <stddef.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <errno.h>
#include <getopt.h>

#include <png.h>

#include <X11/Intrinsic.h>
#include <X11/xpm.h>

#include "xpaint.h"
#include "image.h"
#include "libpnmrw.h"

extern int file_numpages;
extern int file_isSpecialImage;
static int silent;

extern Image * readMagic(char *file);
extern Image * ReadPNG(char *file);
extern int WritePNGn(char *file, Image *image);
extern FILE * openTempFile(char **np);
extern void removeTempFile(void);
extern void AlphaWarning(char *format, int mode);
extern void * xmalloc(size_t n);

/* Version number of program/package */
#define PROGRAM "ico2png"

# ifndef __attribute__
#  if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
#   define __attribute__(x)
#  endif
# endif
#define PACKED __attribute__ ((packed))

struct MessageHeader {
	struct MessageHeader *old;
	char *message;
};

#define memclear(m, s)		memset((m), 0, (s))

#define xrealloc realloc
#define xcalloc(s) calloc(1, ((s)))
#define xstrdup strdup

typedef void (*IteratorFunc)(void *);

typedef struct _IteratorClass IteratorClass;
typedef struct _Iterator Iterator;

struct _IteratorClass {
	bool (*has_next)(Iterator *it);
	void *(*next)(Iterator *it);
	void (*remove)(Iterator *it);
	void (*free)(Iterator *it);
	void (*restart)(Iterator *it);
	void *(*previous)(Iterator *it);
	void (*add)(Iterator *it, void *value);
};

struct _Iterator {
	IteratorClass *class;
};

static bool
iterator_has_next(Iterator *it)
{
	return it->class->has_next(it);
}

static void *
iterator_next(Iterator *it)
{
	return it->class->next(it);
}

static void
iterator_free(Iterator *it)
{
	if (it->class->free == NULL)
		free(it);
	else 
		it->class->free(it);
}

static void
v_warn(const char *msg, va_list ap)
{
	fprintf(stderr, "%s: ", PROGRAM);
	if (msg != NULL)
		vfprintf(stderr, msg, ap);
	fprintf(stderr, "\n");
}

static void
warn(const char *msg, ...)
{
	va_list ap;
	
        if (silent) return;
	va_start(ap, msg);
	v_warn(msg, ap);
	va_end(ap);
}

static void
v_warn_errno(const char *msg, va_list ap)
{
	fprintf(stderr, "%s: ", PROGRAM);
	if (msg != NULL) {
		vfprintf(stderr, msg, ap);
		fprintf(stderr, ": ");
	}
	fprintf(stderr, "%s\n", strerror(errno));
}

static void
warn_errno(const char *msg, ...)
{
	va_list ap;
	
	va_start(ap, msg);
	v_warn_errno(msg, ap);
	va_end(ap);
}

typedef struct _Entry Entry;

struct _Entry {
	void *key;
	void *value;
	Entry *next;
};

typedef uint32_t (*HashFunc)(const void *);
typedef int32_t (*CompareFunc)(const void *, const void *);

typedef struct _HMap HMap;

struct _HMap {
	Entry **buckets;
	uint32_t buckets_length;
	uint32_t threshold;
	float load_factor;
	uint32_t size;
	
	HashFunc hash_key;
	CompareFunc compare_keys;
};

static uint32_t
string_hash(const char *key)
{
	uint32_t hash = 0;

	for (; *key != '\0'; key++)
		hash = (hash << 5) - hash + *key;

	return hash;
}

static HMap *
hmap_new_specific(uint32_t initial_capacity, float load_factor)
{
	HMap *map = xmalloc(sizeof(HMap));

	map->buckets = xmalloc(initial_capacity * sizeof(Entry *));
	memclear(map->buckets, initial_capacity * sizeof(Entry *));

	map->buckets_length = initial_capacity;
	map->threshold = (uint32_t) (initial_capacity * load_factor);
	map->load_factor = load_factor;
	map->size = 0;

	map->hash_key = (HashFunc) string_hash;
	map->compare_keys = (CompareFunc) strcmp;

	return map;
}

#define DEFAULT_CAPACITY 16
#define DEFAULT_LOAD_FACTOR           0.75F

static HMap *
hmap_new(void)
{
	return hmap_new_specific(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
}

static void
hmap_set_hash_function(HMap *map, HashFunc hash_key)
{
	map->hash_key = hash_key;
}

static void
hmap_set_compare_function(HMap *map, CompareFunc compare_keys)
{
	map->compare_keys = compare_keys;
}

static void
hmap_iterate_values(HMap *map, IteratorFunc iterator_func)
{
	uint32_t i;

    for (i = 0; i < map->buckets_length; i++) {
		Entry *entry;
		for (entry = map->buckets[i]; entry != NULL; entry = entry->next)
			iterator_func(entry->value);
	}
}

static void
hmap_clear(HMap *map)
{
	uint32_t i;

    for (i = 0; i < map->buckets_length; i++) {
		Entry *entry = map->buckets[i];
		while (entry != NULL) {
			Entry *next = entry->next;
			free(entry);
			entry = next;
		}
		map->buckets[i] = NULL;
	}

	map->size = 0;
}

static void
hmap_free(HMap *map)
{
	hmap_clear(map);
	free(map->buckets);
	free(map);
}

static inline uint32_t
hmap_hash(HMap *map, const void *key)
{
//	printf("hash %s into -> %d pos %d (%d)\n", key, map->hash_key(key), map->hash_key(key) % map->buckets_length, map->buckets_length);
	return (key == NULL ? 0 : (map->hash_key(key) % map->buckets_length));
}

static bool
hmap_contains_key(HMap *map, const void *key)
{
	if (key == NULL) {
		Entry *entry = map->buckets[0];
		for (; entry != NULL; entry = entry->next) {
			if (entry->key == NULL)
				return true;
		}
	} else {
		Entry *entry = map->buckets[hmap_hash(map, key)];
		for (; entry != NULL; entry = entry->next) {
//			printf("compare %s vs %s -> %d\n",key,entry->key,map->compare_keys(key, entry->key));
			if (map->compare_keys(key, entry->key) == 0)
				return true;
		}
	}

	return false;
}

static inline void
hmap_rehash(HMap *map)
{	
	Entry **old_buckets = map->buckets;
	uint32_t old_capacity = map->buckets_length;
	uint32_t i;

	map->buckets_length = (map->buckets_length * 2) + 1;
	map->threshold = (uint32_t) (map->buckets_length * map->load_factor);
	map->buckets = xmalloc(map->buckets_length * sizeof(Entry *));
	memclear(map->buckets, map->buckets_length * sizeof(Entry *));

	for (i = 0; i < old_capacity; i++) {
		Entry *entry = old_buckets[i];
		while (entry != NULL) {
			uint32_t index = hmap_hash(map, entry->key);
			Entry *dest = map->buckets[index];
			Entry *next;

			if (dest != NULL) {
				while (dest->next != NULL)
					dest = dest->next;
				dest->next = entry;
			} else {
				map->buckets[index] = entry;
			}

			next = entry->next;
			entry->next = NULL;
			entry = next;
		}
	}

	free(old_buckets);
}

static uint32_t
hmap_size(HMap *map)
{
	return map->size;
}

static void *
hmap_get(HMap *map, const void *key)
{
	if (key == NULL) {
		Entry *entry = map->buckets[0];
		for (; entry != NULL; entry = entry->next) {
			if (entry->key == NULL)
				return entry->value;
		}
	} else {
		Entry *entry = map->buckets[hmap_hash(map, key)];
		for (; entry != NULL; entry = entry->next) {
			if (map->compare_keys(key, entry->key) == 0)
				return entry->value;
		}
	}

	return NULL;
}

static void *
hmap_put(HMap *map, void *key, void *value)
{
	Entry *entry;
	uint32_t index;

	if (key == NULL) {
		for (entry = map->buckets[0]; entry != NULL; entry = entry->next) {
			if (entry->key == NULL) {
				void *old_value = entry->value;
				entry->value = value;
				return old_value;
			}
		}
		index = 0;
	} else {
		index = hmap_hash(map, key);
		for (entry = map->buckets[index]; entry != NULL; entry = entry->next) {
			if (map->compare_keys(key, entry->key) == 0) {
				void *old_value = entry->value;
				entry->value = value;
				return old_value;
			}
		}
	}

	map->size++;
	if (map->size > map->threshold) {
		hmap_rehash(map);
		index = hmap_hash(map, key);
	}

	entry = xmalloc(sizeof(Entry));
	entry->key = key;
	entry->value = value;
	entry->next = map->buckets[index];
	map->buckets[index] = entry;

	return NULL;
}

static void *
hmap_remove(HMap *map, const void *key)
{
	Entry *entry;
	Entry *last = NULL;

	if (key == NULL) {
		for (entry = map->buckets[0]; entry != NULL; entry = entry->next) {
			if (entry->key == NULL) {
				void *value = entry->value;
				if (last == NULL)
					map->buckets[0] = entry->next;
				else
					last->next = entry->next;
				map->size--;
				free(entry);
				return value;
			}
			last = entry;
		}
	} else {
		uint32_t index = hmap_hash(map, key);
		for (entry = map->buckets[index]; entry != NULL; entry = entry->next) {
			if (map->compare_keys(key, entry->key) == 0) {
				void *value = entry->value;
				if (last == NULL)
					map->buckets[index] = entry->next;
				else
					last->next = entry->next;
				map->size--;
				free(entry);
				return value;
			}
			last = entry;
		}
	}

	return NULL;
}

typedef struct _HMapIterator HMapIterator;

struct _HMapIterator {
	Iterator iterator;
	HMap *map;
	uint32_t index;
	Entry *entry;
	Entry *previous_entry;
};

static bool hmap_iterator_has_next(Iterator *it);
static void *hmap_iterator_next(Iterator *it);
static void hmap_iterator_remove(Iterator *it);
static void hmap_iterator_restart(Iterator *iterator);

static IteratorClass hmap_iterator_class = {
	hmap_iterator_has_next,
	hmap_iterator_next,
	hmap_iterator_remove,
	NULL,
	hmap_iterator_restart,
	NULL,
	NULL,
};

static bool
hmap_iterator_has_next(Iterator *it)
{
	return ((HMapIterator *) it)->entry != NULL;
}

static void *
hmap_iterator_next(Iterator *iterator)
{
	HMapIterator *it = (HMapIterator *) iterator;
	HMap *map = it->map;
	void *data;

	if (it->entry == NULL)
		return NULL;

	data = it->entry->value;
	it->previous_entry = it->entry;

	it->entry = it->entry->next;
	if (it->entry == NULL) {
		uint32_t i = it->index + 1;
		for (; i < map->buckets_length && map->buckets[i] == NULL; i++);
		it->index = i;
		it->entry = (i < map->buckets_length ? map->buckets[i] : NULL);
	}

	return data;
}

static void
hmap_iterator_remove(Iterator *iterator)
{
	HMapIterator *it = (HMapIterator *) iterator;
	if (it->previous_entry != NULL) {
		hmap_remove(it->map, it->previous_entry->key);
		it->previous_entry = NULL;
	}
}

static void
hmap_iterator_restart(Iterator *iterator)
{
	HMapIterator *it = (HMapIterator *) iterator;
	uint32_t i;

	for (i = 0; i < it->map->buckets_length && it->map->buckets[i] == NULL; i++);
	it->index = i;
	it->entry = (i < it->map->buckets_length ? it->map->buckets[i] : NULL);
	it->previous_entry = NULL;
}

Iterator *
hmap_value_iterator(HMap *map)
{
	HMapIterator *it = xmalloc(sizeof(HMapIterator));
	it->iterator.class = &hmap_iterator_class;
	it->map = map;
	hmap_iterator_restart(&it->iterator);
	return &it->iterator;
}

/* Main Windows BMP data structures */

typedef struct {
    uint8_t width;
    uint8_t height;
    uint8_t color_count;
    uint8_t reserved;
} Win32IconResDir;

typedef struct {
    uint16_t width;
    uint16_t height;
} Win32CursorDir;

typedef struct {
    union {
		Win32IconResDir icon;
		Win32CursorDir cursor;
    } res_info;
    uint16_t plane_count;
    uint16_t bpp;
    uint32_t bytes_in_res;
    uint16_t res_id;
} Win32CursorIconDirEntry;

typedef struct {
    uint16_t reserved;
    uint16_t type;
    uint16_t count;
    Win32CursorIconDirEntry entries[0] PACKED;
} Win32CursorIconDir;

typedef struct {
    uint8_t width;
    uint8_t height;
    uint8_t color_count;
    uint8_t reserved;
    uint16_t hotspot_x;		/* sometimes planes... */
    uint16_t hotspot_y;		/* sometimes bpp... */
    uint32_t dib_size;
    uint32_t dib_offset;
} Win32CursorIconFileDirEntry;

typedef struct {
    uint16_t reserved;
    uint16_t type;
    uint16_t count;
    Win32CursorIconFileDirEntry entries[0] PACKED;
} Win32CursorIconFileDir;

typedef struct {
    uint32_t size;
    int32_t width;
    int32_t height;
    uint16_t planes;
    uint16_t bpp;
    uint32_t compression;
    uint32_t size_image;
    int32_t x_res;
    int32_t y_res;
    uint32_t clr_used;
    uint32_t clr_important;
} Win32BitmapInfoHeader;

typedef struct {
    uint8_t blue;
    uint8_t green;
    uint8_t red;
    uint8_t reserved;
} Win32RGBQuad;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
#define IMAGE_SIZEOF_SHORT_NAME 8

#define	IMAGE_RESOURCE_NAME_IS_STRING		0x80000000
#define	IMAGE_RESOURCE_DATA_IS_DIRECTORY	0x80000000

#define PE_HEADER(module) \
    ((Win32ImageNTHeaders*)((uint8_t *)(module) + \
    	(((DOSImageHeader*)(module))->lfanew)))

#define PE_SECTIONS(module) \
    ((Win32ImageSectionHeader *)((uint8_t *) &PE_HEADER(module)->optional_header + \
                           PE_HEADER(module)->file_header.size_of_optional_header))

#define IMAGE_DOS_SIGNATURE    0x5A4D     /* MZ */
#define IMAGE_OS2_SIGNATURE    0x454E     /* NE */
#define IMAGE_OS2_SIGNATURE_LE 0x454C     /* LE */
#define IMAGE_OS2_SIGNATURE_LX 0x584C     /* LX */
#define IMAGE_VXD_SIGNATURE    0x454C     /* LE */
#define IMAGE_NT_SIGNATURE     0x00004550 /* PE00 */

#define IMAGE_SCN_CNT_CODE			0x00000020
#define IMAGE_SCN_CNT_INITIALIZED_DATA		0x00000040
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA	0x00000080

#define	IMAGE_DIRECTORY_ENTRY_EXPORT		0
#define	IMAGE_DIRECTORY_ENTRY_IMPORT		1
#define	IMAGE_DIRECTORY_ENTRY_RESOURCE		2
#define	IMAGE_DIRECTORY_ENTRY_EXCEPTION		3
#define	IMAGE_DIRECTORY_ENTRY_SECURITY		4
#define	IMAGE_DIRECTORY_ENTRY_BASERELOC		5
#define	IMAGE_DIRECTORY_ENTRY_DEBUG		6
#define	IMAGE_DIRECTORY_ENTRY_COPYRIGHT		7
#define	IMAGE_DIRECTORY_ENTRY_GLOBALPTR		8   /* (MIPS GP) */
#define	IMAGE_DIRECTORY_ENTRY_TLS		9
#define	IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG	10
#define	IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT	11
#define	IMAGE_DIRECTORY_ENTRY_IAT		12  /* Import Address Table */
#define	IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT	13
#define	IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR	14

#define RT_CURSOR        1
#define RT_BITMAP        2
#define RT_ICON          3
#define RT_MENU          4
#define RT_DIALOG        5
#define RT_STRING        6
#define RT_FONTDIR       7
#define RT_FONT          8
#define RT_ACCELERATOR   9
#define RT_RCDATA        10
#define RT_MESSAGELIST   11
#define RT_GROUP_CURSOR  12
#define RT_GROUP_ICON    14

typedef struct {
    union {
    	struct {
    	    #if BITFIELDS_BIGENDIAN
    	    unsigned name_is_string:1;
    	    unsigned name_offset:31;
    	    #else
    	    unsigned name_offset:31;
    	    unsigned name_is_string:1;
    	    #endif
    	} s1;
    	uint32_t name;
    	struct {
    	    #if WORDS_BIGENDIAN
    	    uint16_t __pad;
    	    uint16_t id;
    	    #else
    	    uint16_t id;
    	    uint16_t __pad;
    	    #endif
    	} s2;
    } u1;
    union {
    	uint32_t offset_to_data;
    	struct {
    	    #if BITFIELDS_BIGENDIAN
    	    unsigned data_is_directory:1;
    	    unsigned offset_to_directory:31;
    	    #else
    	    unsigned offset_to_directory:31;
    	    unsigned data_is_directory:1;
    	    #endif
    	} s;
    } u2;
} Win32ImageResourceDirectoryEntry;

typedef struct {
    uint16_t type_id;
    uint16_t count;
    uint32_t reserved;
} Win16NETypeInfo;

typedef struct {
    uint16_t offset;
    uint16_t length;
    uint16_t flags;
    uint16_t id;
    uint16_t handle;
    uint16_t usage;
} Win16NENameInfo;

typedef struct {
    uint16_t magic;
    uint8_t ver;
    uint8_t rev;
    uint16_t enttab;
    uint16_t cbenttab;
    int32_t crc;
    uint16_t flags;
    uint16_t autodata;
    uint16_t heap;
    uint16_t stack;
    uint32_t csip;
    uint32_t sssp;
    uint16_t cseg;
    uint16_t cmod;
    uint16_t cbnrestab;
    uint16_t segtab;
    uint16_t rsrctab;
    uint16_t restab;
    uint16_t modtab;
    uint16_t imptab;
    uint32_t nrestab;
    uint16_t cmovent;
    uint16_t align;
    uint16_t cres;
    uint8_t exetyp;
    uint8_t flagsothers;
    uint16_t fastload_offset;
    uint16_t fastload_length;
    uint16_t swaparea;
    uint16_t expver;
} OS2ImageHeader;

typedef struct {
    uint16_t magic;
    uint16_t cblp;
    uint16_t cp;
    uint16_t crlc;
    uint16_t cparhdr;
    uint16_t minalloc;
    uint16_t maxalloc;
    uint16_t ss;
    uint16_t sp;
    uint16_t csum;
    uint16_t ip;
    uint16_t cs;
    uint16_t lfarlc;
    uint16_t ovno;
    uint16_t res[4];
    uint16_t oemid;
    uint16_t oeminfo;
    uint16_t res2[10];
    uint32_t lfanew;
} DOSImageHeader;

typedef struct {
    uint16_t machine;
    uint16_t number_of_sections;
    uint32_t time_date_stamp;
    uint32_t pointer_to_symbol_table;
    uint32_t number_of_symbols;
    uint16_t size_of_optional_header;
    uint16_t characteristics;
} Win32ImageFileHeader;

typedef struct {
    uint32_t virtual_address;
    uint32_t size;
} Win32ImageDataDirectory;

typedef struct {
    uint16_t magic;
    uint8_t major_linker_version;
    uint8_t minor_linker_version;
    uint32_t size_of_code;
    uint32_t size_of_initialized_data;
    uint32_t size_of_uninitialized_data;
    uint32_t address_of_entry_point;
    uint32_t base_of_code;
    uint32_t base_of_data;
    uint32_t image_base;
    uint32_t section_alignment;
    uint32_t file_alignment;
    uint16_t  major_operating_system_version;
    uint16_t  minor_operating_system_version;
    uint16_t  major_image_version;
    uint16_t  minor_image_version;
    uint16_t  major_subsystem_version;
    uint16_t  minor_subsystem_version;
    uint32_t win32_version_value;
    uint32_t size_of_image;
    uint32_t size_of_headers;
    uint32_t checksum;
    uint16_t subsystem;
    uint16_t dll_characteristics;
    uint32_t size_of_stack_reserve;
    uint32_t size_of_stack_commit;
    uint32_t size_of_heap_reserve;
    uint32_t size_of_heap_commit;
    uint32_t loader_flags;
    uint32_t number_of_rva_and_sizes;
    Win32ImageDataDirectory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} Win32ImageOptionalHeader;

typedef struct {
    uint32_t signature;
    Win32ImageFileHeader file_header;
    Win32ImageOptionalHeader optional_header;
} Win32ImageNTHeaders;

typedef struct  {
    uint8_t name[IMAGE_SIZEOF_SHORT_NAME];
    union {
	uint32_t physical_address;
	uint32_t virtual_size;
    } misc;
    uint32_t virtual_address;
    uint32_t size_of_raw_data;
    uint32_t pointer_to_raw_data;
    uint32_t pointer_to_relocations;
    uint32_t pointer_to_linenumbers;
    uint16_t number_of_relocations;
    uint16_t number_of_linenumbers;
    uint32_t characteristics;
} Win32ImageSectionHeader;

typedef struct {
    uint32_t offset_to_data;
    uint32_t size;
    uint32_t code_page;
    uint32_t resource_handle;
} Win32ImageResourceDataEntry;

typedef struct {
    uint32_t characteristics;
    uint32_t time_date_stamp;
    uint16_t major_version;
    uint16_t minor_version;
    uint16_t number_of_named_entries;
    uint16_t number_of_id_entries;
} Win32ImageResourceDirectory;

#if WORDS_BIGENDIAN

static void fix_win32_cursor_icon_file_dir_endian(Win32CursorIconFileDir *obj);
static void fix_win32_bitmap_info_header_endian(Win32BitmapInfoHeader *obj);
static void fix_win32_cursor_icon_file_dir_entry_endian(Win32CursorIconFileDirEntry *obj);
static void fix_win32_image_data_directory(Win32ImageDataDirectory *obj);
static void fix_os2_image_header_endian(OS2ImageHeader *obj);
static void fix_win32_image_section_header(Win32ImageSectionHeader *obj);
static void fix_win32_image_header_endian(Win32ImageNTHeaders *obj);

#else

#define fix_win32_bitmap_info_header_endian(x)
#define fix_win32_cursor_icon_file_dir_endian(x)
#define fix_win32_cursor_icon_file_dir_entry_endian(x)
#define fix_win32_image_data_directory(x)
#define fix_os2_image_header_endian(x)
#define fix_win32_image_section_header(x)
#define fix_win32_image_header_endian(x)

#endif /* WORDS_BIGENDIAN */

#define ROW_BYTES(bits) ((((bits) + 31) >> 5) << 2)

typedef struct _Palette {
	HMap *map;
	Iterator *it;
} Palette;

typedef struct {
	uint8_t red;
	uint8_t green;
	uint8_t blue;
	uint32_t index;
} PaletteColor;

static uint32_t
color_hash(PaletteColor *color)
{
	return (color->red << 16) | (color->green << 8) | color->blue;
}

static int32_t
color_compare(PaletteColor *c1, PaletteColor *c2)
{
	if (c1->red != c2->red)
		return c1->red - c2->red;
	if (c1->green != c2->green)
		return c1->green - c2->green;
	if (c1->blue != c2->blue)
		return c1->blue - c2->blue;
	return 0;
}

static Palette *
palette_new(void)
{
	Palette *palette = xmalloc(sizeof(Palette));
	palette->map = hmap_new();
	palette->it = NULL;
	hmap_set_hash_function(palette->map, (HashFunc) color_hash);
	hmap_set_compare_function(palette->map, (CompareFunc) color_compare);
	return palette;
}

static void
palette_free(Palette *palette)
{
	if (palette->it != NULL)
		iterator_free(palette->it);
	hmap_iterate_values(palette->map, free);
	hmap_free(palette->map);
	free(palette);
}

static void
palette_add(Palette *palette, uint8_t r, uint8_t g, uint8_t b)
{
	PaletteColor color = { r, g, b, 0 };

	if (!hmap_contains_key(palette->map, &color)) {
		PaletteColor *new_color = xmalloc(sizeof(PaletteColor));
		new_color->red = r;
		new_color->green = g;
		new_color->blue = b;
		new_color->index = 0;
		hmap_put(palette->map, new_color, new_color);
	}
}

static bool
palette_next(Palette *palette, uint8_t *r, uint8_t *g, uint8_t *b)
{
	if (palette->it == NULL)
		palette->it = hmap_value_iterator(palette->map);
	if (iterator_has_next(palette->it)) {
		PaletteColor *color = iterator_next(palette->it);
		*r = color->red;
		*g = color->green;
		*b = color->blue;
		return true;
	}
	iterator_free(palette->it);
	palette->it = NULL;
	return false;
}

static void
palette_assign_indices(Palette *palette)
{
	Iterator *it = hmap_value_iterator(palette->map);
	uint32_t c;

	for (c = 0; iterator_has_next(it); c++) {
		PaletteColor *color = iterator_next(it);
		color->index = c;
	}
}

static uint32_t
palette_lookup(Palette *palette, uint8_t r, uint8_t g, uint8_t b)
{
	PaletteColor color = { r, g, b, 0 };
	PaletteColor *real_color = hmap_get(palette->map, &color);
	return (real_color != NULL ? real_color->index : -1);
}

static uint32_t
palette_count(Palette *palette)
{
	return hmap_size(palette->map);
}

static void
simple_setvec(uint8_t *data, uint32_t ofs, uint8_t size, uint32_t value)
{
	switch (size) {
	case 1:
		data[ofs/8] |= (value & 1) << (7 - ofs%8);
		break;
	case 2:
		data[ofs/4] |= (value & 3) << ((3 - ofs%4) << 1);
		break;
	case 4:
		data[ofs/2] |= (value & 15) << ((1 - ofs%2) << 2);
		break;
	case 8:
		data[ofs] = value;
		break;
	case 16:
		data[2*ofs] = value;
		data[2*ofs+1] = (value >> 8);
		break;
	case 24:
		data[3*ofs] = value;
		data[3*ofs+1] = (value >> 8);
		data[3*ofs+2] = (value >> 16);
		break;
	case 32:
		data[4*ofs] = value;
		data[4*ofs+1] = (value >> 8);
		data[4*ofs+2] = (value >> 16);
		data[4*ofs+3] = (value >> 24);
		break;
	}
}

static uint32_t
simple_vec(uint8_t *data, uint32_t ofs, uint8_t size)
{
	switch (size) {
	case 1:
		return (data[ofs/8] >> (7 - ofs%8)) & 1;
	case 2:
		return (data[ofs/4] >> ((3 - ofs%4) << 1)) & 3;
	case 4:
		return (data[ofs/2] >> ((1 - ofs%2) << 2)) & 15;
	case 8:
		return data[ofs];
	case 16:
		return data[2*ofs] | data[2*ofs+1] << 8;
	case 24:
		return data[3*ofs] | data[3*ofs+1] << 8 | data[3*ofs+2] << 16;
	case 32:
		return data[4*ofs] | data[4*ofs+1] << 8 | data[4*ofs+2] << 16 | data[4*ofs+3] << 24;
	}

	return 0;
}

static bool
xfread(void *ptr, size_t size, FILE *stream)
{
	if (fread(ptr, size, 1, stream) < 1) {
		if (ferror(stream))
			warn_errno("cannot read file");
		else
			warn("premature end");
		return false;
	}
	return true;
}

typedef struct {
	uint32_t capacity;
	uint32_t count;
	char value[0];
} StringBuffer;

static inline StringBuffer *
get_string_buffer(char **buf)
{
	return (StringBuffer *) (*buf - sizeof(StringBuffer));
}

static inline char *
new_strbuf(uint32_t capacity, uint32_t count)
{
	StringBuffer *strbuf;
	strbuf = xmalloc(sizeof(StringBuffer) + sizeof(char)*capacity);
	strbuf->capacity = capacity;
	strbuf->count = count;
	return strbuf->value;
}

static inline void
ensure_capacity(char **buf, StringBuffer **strbuf, uint32_t min_capacity)
{
	if (min_capacity > (*strbuf)->capacity) {
		uint32_t max = (min_capacity > (*strbuf)->count ? (*strbuf)->count*2+2 : (*strbuf)->count);
		min_capacity = MAX(min_capacity, max);

		*strbuf = xrealloc(*strbuf, sizeof(StringBuffer) + sizeof(char) * min_capacity);
		(*strbuf)->capacity = min_capacity;
		*buf = (*strbuf)->value;
	}
}

/**
 * Read and discard some number of bytes from a stream.
 */
static int
fskip(FILE *file, uint32_t bytes)
{
	for (; bytes > 0; bytes--) {
		if (fgetc(file) == EOF)
			return -1;
	}
	return 0;
}

/**
 * Write a number of bytes of the same value to a stream.
 */
static int
fpad(FILE *file, char byte, uint32_t bytes)
{
	for (; bytes > 0; bytes--) {
		if (fwrite(&byte, 1, 1, file) != 1)
			return -1;
	}
	return 0;
}

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)
{
	struct {
		FILE *in;
		png_structp png_ptr;
		png_infop info_ptr;
		uint32_t bpp;
		uint32_t palette_count;
		uint32_t image_size;
		uint32_t mask_size;
		uint32_t width;
		uint32_t height;
		uint8_t *image_data;
		uint8_t **row_datas;
		Palette *palette;
	} *img;

	Win32CursorIconFileDir dir;
	FILE *out = NULL;
	uint32_t c, d, x;
	uint32_t dib_start;
	png_byte ct;

	img = xcalloc(filec * sizeof(*img));

	for (c = 0; c < filec; c++) {
		char header[8];
		uint32_t row_bytes;
		uint8_t transparency[256];
		uint16_t transparency_count;
		bool need_transparency;

		img[c].in = fopen(filev[c], "r");
    	if (img[c].in == NULL) {
	    warn_errno("(#%d) cannot open file", c);
			goto cleanup;
		}
    	if (!xfread(header, 8, img[c].in))
			goto cleanup;
    	if (png_sig_cmp((png_bytep)header, 0, 8)) {
	    
	    warn("(#%d) not a png file", c);
			goto cleanup;
		}

		img[c].png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL /*user_error_fn, user_warning_fn*/);
		if (img[c].png_ptr == NULL) {
			warn("cannot initialize PNG library");
			goto cleanup;
		}
		img[c].info_ptr = png_create_info_struct(img[c].png_ptr);
		if (img[c].info_ptr == NULL) {
			warn("cannot create PNG info structure - out of memory");
			goto cleanup;
		}

		png_init_io(img[c].png_ptr, img[c].in);
		png_set_sig_bytes(img[c].png_ptr, 8);
		png_set_strip_16(img[c].png_ptr);
		png_set_expand(img[c].png_ptr);
		png_set_gray_to_rgb(img[c].png_ptr);
		png_set_interlace_handling(img[c].png_ptr);
		png_set_filler(img[c].png_ptr, 0xFF, PNG_FILLER_AFTER);
		png_read_info(img[c].png_ptr, img[c].info_ptr);
		png_read_update_info(img[c].png_ptr, img[c].info_ptr);

		img[c].width = png_get_image_width(img[c].png_ptr, img[c].info_ptr);
		/*if (img[c].width > 255) {
			warn("image too wide (max 255, was %d pixels)", img[c].width);
			goto cleanup;
		}*/
		img[c].height = png_get_image_height(img[c].png_ptr, img[c].info_ptr);
		/*if (img[c].height > 255) {
			warn("image too tall (max 255, was %d pixels)", img[c].height);
			goto cleanup;
		}*/

		row_bytes = png_get_rowbytes(img[c].png_ptr, img[c].info_ptr);
		img[c].row_datas = xmalloc(img[c].height * sizeof(png_bytep *));
		img[c].row_datas[0] = xmalloc(img[c].height * row_bytes);
		for (d = 1; d < img[c].height; d++)
			img[c].row_datas[d] = img[c].row_datas[d-1] + row_bytes;
		png_read_rows(img[c].png_ptr, img[c].row_datas, NULL, img[c].height);

		ct = png_get_color_type(img[c].png_ptr, img[c].info_ptr);
		img[c].palette = palette_new();


		/* Count number of necessary colors in palette and number of transparencies */
		memset(transparency, 0, 256);
		for (d = 0; d < img[c].height; d++) {
			png_bytep row = img[c].row_datas[d];
			for (x = 0; x < img[c].width; x++) {
				if (palette_count(img[c].palette) <= (1 << 8))
				    palette_add(img[c].palette, row[4*x+0], row[4*x+1], row[4*x+2]);
				if (ct & PNG_COLOR_MASK_ALPHA)
				    transparency[row[4*x+3]] = 1;
			}
		}
		transparency_count = 0;
		for (d = 0; d < 256; d++)
		    transparency_count += transparency[d];

		/* If there are more than two steps of transparency, or if the
		 * two steps are NOT either entirely off (0) and entirely on (255),
		 * then we will lose transparency information if bpp is not 32.
		 */
		need_transparency =
		    transparency_count > 2
		    ||
		    (transparency_count == 2 && (transparency[0] == 0 || transparency[255] == 0));

		/* Can we keep all colors in a palette? */
		if (need_transparency) {
			if (bpp != -1) {
			    if (bpp != 32)
				warn("decreasing bit depth will discard variable transparency", transparency_count);
			    /* Why 24 and not bpp? Otherwise we might decrease below what's possible
			           * due to number of colors in image. The real decrease happens below. */
			    img[c].bpp = 24;
			} else {
			    img[c].bpp = 32;
			}
			img[c].palette_count = 0;
		}
		else if (palette_count(img[c].palette) <= 256) {
			for (d = 1; palette_count(img[c].palette) > 1 << d; d <<= 1);
			if (d == 2)	/* four colors (two bits) are not supported */
				d = 4;
			img[c].bpp = d;
			img[c].palette_count = 1 << d;
		}
		else {
			img[c].bpp = 24;
			img[c].palette_count = 0;
		}

		/* Does the user want to change number of bits per pixel? */
		if (bpp != -1) {
			if (img[c].bpp == bpp) {
				/* No operation */
			} else if (img[c].bpp < bpp) {
				img[c].bpp = bpp;
				img[c].palette_count = (bpp > 16 ? 0 : 1 << bpp);
			} else {
				warn("cannot decrease bit depth from %d to %d, bit depth not changed", img[c].bpp, bpp);
			}
		}
		
		img[c].image_size = img[c].height * ROW_BYTES(img[c].width * img[c].bpp);
		img[c].mask_size = img[c].height * ROW_BYTES(img[c].width);

	}

        out = fopen(outname, "w");
	if (out == NULL) {
		warn_errno("cannot create file");
		goto cleanup;
	}

	dir.reserved = 0;
	dir.type = (icon_mode ? 1 : 2);
	dir.count = filec;
	fix_win32_cursor_icon_file_dir_endian(&dir);
	if (fwrite(&dir, sizeof(Win32CursorIconFileDir), 1, out) != 1) {
		warn_errno("cannot write to file");
		goto cleanup;
	}

	dib_start = sizeof(Win32CursorIconFileDir) + filec * sizeof(Win32CursorIconFileDirEntry);
	for (c = 0; c < filec; c++) {
		Win32CursorIconFileDirEntry entry;

		entry.width = MIN(255, img[c].width);
		entry.height = MIN(255, img[c].height);
		entry.reserved = 0;
		if (icon_mode) {
			entry.hotspot_x = 0;	 /* some mistake this for planes (XXX) */
			entry.hotspot_y = 0;	 /* some mistake this for bpp (XXX) */
		} else {
			entry.hotspot_x = hotspot_x;	 /* some mistake this for planes (XXX) */
			entry.hotspot_y = hotspot_y;	 /* some mistake this for bpp (XXX) */
		}
		entry.dib_offset = dib_start;
		entry.color_count = (img[c].bpp >= 8 ? 0 : 1 << img[c].bpp);
		entry.dib_size = img[c].palette_count * sizeof(Win32RGBQuad)
				+ sizeof(Win32BitmapInfoHeader)
				+ img[c].image_size
				+ img[c].mask_size;

		dib_start += entry.dib_size;

		fix_win32_cursor_icon_file_dir_entry_endian(&entry);
		if (fwrite(&entry, sizeof(Win32CursorIconFileDirEntry), 1, out) != 1) {
			warn_errno("cannot write to file");
			goto cleanup;
		}

	}

	for (c = 0; c < filec; c++) {
		Win32BitmapInfoHeader bitmap;

		bitmap.size = sizeof(Win32BitmapInfoHeader);
		bitmap.width = png_get_image_width(img[c].png_ptr, img[c].info_ptr);
		bitmap.height = png_get_image_height(img[c].png_ptr, img[c].info_ptr) * 2;
		bitmap.planes = 1;							// appears to be 1 always (XXX)
		bitmap.bpp = img[c].bpp;
		bitmap.compression = 0;
		bitmap.x_res = 0;				// should be 0 always
		bitmap.y_res = 0;				// should be 0 always
		bitmap.clr_important = 0;					// should be 0 always
		bitmap.clr_used = img[c].palette_count;
		bitmap.size_image = img[c].image_size;		// appears to be ok here (may be image_size+mask_size or 0, XXX)

		fix_win32_bitmap_info_header_endian(&bitmap);
		if (fwrite(&bitmap, sizeof(Win32BitmapInfoHeader), 1, out) != 1) {
			warn_errno("cannot write to file");
			goto cleanup;
		}

		if (img[c].bpp <= 16) {
			Win32RGBQuad color;

			palette_assign_indices(img[c].palette);
			color.reserved = 0;
			while (palette_next(img[c].palette, &color.red, &color.green, &color.blue))
				fwrite(&color, sizeof(Win32RGBQuad), 1, out);

			/* Pad with empty colors. The reason we do this is because we
			 * specify bitmap.clr_used as a base of 2. The latter is probably
			 * not necessary according to the original specs, but many
			 * programs that read icons assume it. Especially gdk-pixbuf.
			 */
		    	memclear(&color, sizeof(Win32RGBQuad));
			for (d = palette_count(img[c].palette); d < 1 << img[c].bpp; d++)
				fwrite(&color, sizeof(Win32RGBQuad), 1, out);
		}

		img[c].image_data = xcalloc(img[c].image_size);

		for (d = 0; d < img[c].height; d++) {
			png_bytep row = img[c].row_datas[img[c].height - d - 1];
			if (img[c].bpp < 24) {
				uint32_t imod = d * (img[c].image_size/img[c].height) * 8 / img[c].bpp;
				for (x = 0; x < img[c].width; x++) {
					uint32_t color;
					color = palette_lookup(img[c].palette, row[4*x+0], row[4*x+1], row[4*x+2]);
					simple_setvec(img[c].image_data, x+imod, img[c].bpp, color);
				}
			} else if (img[c].bpp == 24) {
				uint32_t irow = d * (img[c].image_size/img[c].height);
				for (x = 0; x < img[c].width; x++) {
					img[c].image_data[3*x+0 + irow] = row[4*x+2];
					img[c].image_data[3*x+1 + irow] = row[4*x+1];
					img[c].image_data[3*x+2 + irow] = row[4*x+0];
				}
			} else if (img[c].bpp == 32) {
				uint32_t irow = d * (img[c].image_size/img[c].height);
				for (x = 0; x < img[c].width; x++) {
					img[c].image_data[4*x+0 + irow] = row[4*x+2];
					img[c].image_data[4*x+1 + irow] = row[4*x+1];
					img[c].image_data[4*x+2 + irow] = row[4*x+0];
					img[c].image_data[4*x+3 + irow] = row[4*x+3];
				}
			}
		}

		if (fwrite(img[c].image_data, img[c].image_size, 1, out) != 1) {
			warn_errno("cannot write to file");
			goto cleanup;
		}

		for (d = 0; d < img[c].height; d++) {
			png_bytep row = img[c].row_datas[img[c].height - d - 1];

			for (x = 0; x < img[c].width; x += 8) {
				uint8_t mask = 0;
				mask |= (row[4*(x+0)+3] <= alpha_threshold ? 1 << 7 : 0);
				mask |= (row[4*(x+1)+3] <= alpha_threshold ? 1 << 6 : 0);
				mask |= (row[4*(x+2)+3] <= alpha_threshold ? 1 << 5 : 0);
				mask |= (row[4*(x+3)+3] <= alpha_threshold ? 1 << 4 : 0);
				mask |= (row[4*(x+4)+3] <= alpha_threshold ? 1 << 3 : 0);
				mask |= (row[4*(x+5)+3] <= alpha_threshold ? 1 << 2 : 0);
				mask |= (row[4*(x+6)+3] <= alpha_threshold ? 1 << 1 : 0);
				mask |= (row[4*(x+7)+3] <= alpha_threshold ? 1 << 0 : 0);
				fputc(mask, out);
			}

			fpad(out, 0, img[c].mask_size/img[c].height - x/8);
		}

		free(img[c].image_data);
		palette_free(img[c].palette);
		free(img[c].row_datas[0]);
		free(img[c].row_datas);
		png_read_end(img[c].png_ptr, img[c].info_ptr);
		png_destroy_read_struct(&img[c].png_ptr, &img[c].info_ptr, NULL);
		fclose(img[c].in);
		memclear(&img[c], sizeof(*img));
	}

	free(img);
        if (out) fclose(out);
	return true;

cleanup:

	for (c = 0; c < filec; c++) {
		if (img[c].image_data != NULL)
			free(img[c].image_data);
		if (img[c].palette != NULL)
			palette_free(img[c].palette);
		if (img[c].row_datas != NULL && img[c].row_datas[0] != NULL) {
			free(img[c].row_datas[0]);
			free(img[c].row_datas);
		}
		if (img[c].png_ptr != NULL)
			png_destroy_read_struct(&img[c].png_ptr, &img[c].info_ptr, NULL);
		if (img[c].in != NULL)
			fclose(img[c].in);
	}
	free(img);
        if (out) fclose(out);
	return false;
}

static int32_t image_index;
static int32_t width = -1;
static int32_t height = -1;
static int32_t bitdepth = -1;
static int32_t palettesize = -1;
static int32_t hotspot_x = 0;
static int32_t hotspot_y = 0;
static bool hotspot_x_set = false;
static bool hotspot_y_set = false;
static int32_t alpha_threshold = 127;
static bool icon_only = false;	
static bool cursor_only = false;

static bool
filter(int i, int w, int h, int bd, int ps, bool icon, int hx, int hy)
{
    if (image_index != -1 && i != image_index)
	return false;
    if (width != -1 && w != width)
	return false;
    if (height != -1 && h != height)
	return false;
    if (bitdepth != -1 && bd != bitdepth)
	return false;
    if (palettesize != -1 && ps != palettesize)
	return false;
    if ((icon_only && !icon) || (cursor_only && icon))
	return false;
    if (hotspot_x_set && hx != hotspot_x)
	return false;
    if (hotspot_y_set && hy != hotspot_y)	
	return false;
    return true;
}

static int
extract_icons(FILE *in, Image **out_image, bool extractmode)
{
	Win32CursorIconFileDir dir;
	Win32CursorIconFileDirEntry *entries = NULL;
	uint32_t offset;
	uint32_t c, d;
	int completed;
	int matched = 0;
        Image *out = NULL;

	if (!xfread(&dir, sizeof(Win32CursorIconFileDir), in))
		goto cleanup;
	fix_win32_cursor_icon_file_dir_endian(&dir);

	if (dir.reserved != 0) {
		warn("not an icon or cursor file (reserved non-zero)");
		goto cleanup;
	}
	if (dir.type != 1 && dir.type != 2) {
		warn("not an icon or cursor file (wrong type)");
		goto cleanup;
	}

	entries = xmalloc(dir.count * sizeof(Win32CursorIconFileDirEntry));
	for (c = 0; c < dir.count; c++) {
		if (!xfread(&entries[c], sizeof(Win32CursorIconFileDirEntry), in))
			goto cleanup;
		fix_win32_cursor_icon_file_dir_entry_endian(&entries[c]);
		if (entries[c].reserved != 0)
		    warn("(#%d) reserved is not zero", c+1);
	}
	offset = sizeof(Win32CursorIconFileDir) + dir.count * sizeof(Win32CursorIconFileDirEntry);

        warn("number of image entries :%d", dir.count);

	for (completed = 0; completed < dir.count; ) {
		uint32_t min_offset = UINT32_MAX;
		int previous = completed;

		for (c = 0; c < dir.count; c++) {
                        int number = c;
			if (entries[c].dib_offset == offset) {
				Win32BitmapInfoHeader bitmap;
				Win32RGBQuad *palette = NULL;
				uint32_t palette_count = 0;
				uint32_t image_size, mask_size;
				uint32_t width = 0, height = 0;
				uint8_t *image_data = NULL, *mask_data = NULL;
				unsigned char *row;
				unsigned char *alpha;

				if (!xfread(&bitmap, sizeof(Win32BitmapInfoHeader), in))
					goto local_cleanup;

				fix_win32_bitmap_info_header_endian(&bitmap);
				if (bitmap.size < sizeof(Win32BitmapInfoHeader)) {
				        warn("(#%d) bitmap header is too short", number);
					goto local_cleanup;
				}
				if (bitmap.compression != 0) {
				        warn("(#%d) compressed image found", number);
                                        fseek(in, offset, SEEK_SET);
                                        mask_size = 0;
                                        image_size = bitmap.size_image;
                                        goto direct;
				}
				if (bitmap.x_res != 0)
					warn("(#%d) x_res field in bitmap should be zero", number);
				if (bitmap.y_res != 0)
					warn("(#%d) y_res field in bitmap should be zero", number);
				if (bitmap.clr_important != 0)
					warn("(#%d) clr_important field in bitmap should be zero", number);
				if (bitmap.planes != 1)
					warn("(#%d) planes field in bitmap should be one", number);
				if (bitmap.size != sizeof(Win32BitmapInfoHeader)) {
					uint32_t skip = bitmap.size - sizeof(Win32BitmapInfoHeader);
					warn("(#%d) skipping %d bytes of extended bitmap header", number, skip);
					fskip(in, skip);
				}
				offset += bitmap.size;

				if (bitmap.clr_used != 0 || bitmap.bpp < 24) {
					palette_count = (bitmap.clr_used != 0 ? bitmap.clr_used : 1 << bitmap.bpp);
					palette = xmalloc(sizeof(Win32RGBQuad) * palette_count);
					if (!xfread(palette, sizeof(Win32RGBQuad) * palette_count, in))
						goto local_cleanup;
					offset += sizeof(Win32RGBQuad) * palette_count;
				}

				width = bitmap.width;
				height = abs(bitmap.height)/2;
				
				image_size = height * ROW_BYTES(width * bitmap.bpp);
				mask_size = height * ROW_BYTES(width);

				if (entries[c].dib_size	!= bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad))
					warn("(#%d) incorrect total size of bitmap (%d specified; %d real)",
					    c, entries[c].dib_size,
					    bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad)
					);

			direct:
				image_data = xmalloc(image_size);
				if (!xfread(image_data, image_size, in))
					goto local_cleanup;

                                if (mask_size) {
				    mask_data = xmalloc(mask_size);
				    if (!xfread(mask_data, mask_size, in))
					goto local_cleanup;
				}

				offset += image_size;
				offset += mask_size;
				completed++;

				if (!filter(completed, width, height, bitmap.bpp, palette_count, dir.type == 1,
						(dir.type == 1 ? 0 : entries[c].hotspot_x),
					        (dir.type == 1 ? 0 : entries[c].hotspot_y))) {
                                        if (bitmap.compression != 0) goto local_cleanup;
					goto done;
				}
				matched++;

				if (extractmode) {
				        if (bitmap.compression) {
                                        /* Just hope it's an otherwise
                                           readable image ... */
					    char *tmp;
					    FILE * fp; 
                                            int num = file_numpages;
                                            fp = openTempFile(&tmp);
					    if (fp) {
                                                fwrite(image_data, 1, image_size, fp);
                                                fclose(fp);
					    }
                                            if (fp) out = readMagic(tmp);
                                            file_numpages = num;
                                            if (num>1)  file_isSpecialImage = 1;
                                            removeTempFile();
					    goto local_cleanup;
					}
					out = ImageNew(width, height);
                                        if (out)
                                            out->alpha = (unsigned char *)
					         xmalloc(width * height);
                                        
					if (!out || !out->alpha) {
						warn_errno("(#%d) cannot create file", number);
                                                if (out) free(out->alpha);
                                                free(out);
                                                out = NULL;
						goto local_cleanup;
					}
				}
				if (bitmap.compression) goto local_cleanup;

                                if (out)
				for (d = 0; d < height; d++) {
				        uint32_t x;
					uint32_t y = (bitmap.height < 0) ? d : height - d - 1;
					uint32_t imod = y * (image_size / height) * 8 / bitmap.bpp;
					uint32_t mmod = y * (mask_size / height) * 8;
				        row = out->data + 3 * d * width;
                                        alpha = out->alpha + d * width;

					for (x = 0; x < width; x++) {
						uint32_t color = simple_vec(image_data, x + imod, bitmap.bpp);

						if (bitmap.bpp <= 16) {
							if (color >= palette_count) {
								warn("(#%d) color out of range in image data", number);
								goto local_cleanup;
							}
							row[3*x]   = palette[color].red;
							row[3*x+1] = palette[color].green;
							row[3*x+2] = palette[color].blue;
						} else {
							row[3*x]  = (color >> 16) & 0xFF;
							row[3*x+1] = (color >>  8) & 0xFF;
							row[3*x+2] = (color >>  0) & 0xFF;
						}
						if (bitmap.bpp == 32)
						    alpha[x] = (color >> 24) & 0xFF;
						else
						    alpha[x] = simple_vec(mask_data, x + mmod, 1) ? 0 : 0xFF;
					}
				}

			done:

				if (palette != NULL)
					free(palette);
				if (image_data != NULL) {
					free(image_data);
					free(mask_data);
				}
				continue;

			local_cleanup:

				if (palette != NULL)
					free(palette);
				if (image_data != NULL) {
					free(image_data);
					free(mask_data);
				}
				goto cleanup;
			} else {
				if (entries[c].dib_offset > offset)
						min_offset = MIN(min_offset, entries[c].dib_offset);
			}
		}

		if (previous == completed) {
			if (min_offset < offset) {
				warn("offset of bitmap header incorrect (too low)");
				goto cleanup;
			}
			warn("skipping %d bytes of garbage at %d", min_offset-offset, offset);
			fskip(in, min_offset - offset);
			offset = min_offset;
		}
	}

cleanup:
        *out_image = out;
	free(entries);
	return matched;
}

static char ico_magic_number[4] = {0, 0, 1, 0};

int 
TestICO(char *file)
{
    FILE *ico_stream;
/* Values read from icon file */
    char iconheader[8];

    if ((ico_stream = fopen (file, "r")) == NULL)
        return false;

    if (fread (iconheader, 1, 6, ico_stream)<6 ||
        memcmp(iconheader, ico_magic_number, sizeof(ico_magic_number))) {
        fclose (ico_stream);
        return false;
    }
    fclose (ico_stream);
    return true;
}

Image *
ReadICO (char *file)
{
    Image *image = NULL;
    FILE *fp;
    static char *prevfile = NULL;

    fp = fopen(file, "r");
    if (!fp) return NULL;

    if (prevfile && !strcmp(file, prevfile)) {
        silent = 1;
    } else {
        free(prevfile);
        silent = 0;
        prevfile = xstrdup(file);
    }
    prevfile = file;

    image_index = -1;
    file_numpages = extract_icons(fp, &image, false);
    if (!silent)
        warn("number of images : %d\n", file_numpages);
    if (file_numpages == 0) goto failure;
    if (file_numpages > 1) file_isSpecialImage = 1;

    fp = fopen(file, "r");
    if (!fp) return NULL;
    image_index = Global.numpage;
    silent = 1;
    extract_icons(fp, &image, true);
 failure:
    return image;
}

int
WriteICO(char *file, Image * image)
{
    int res;
    char *tmp;
    FILE *fp;

    if (image->alpha) AlphaWarning("ICO", 1);

    fp = openTempFile(&tmp);
    if (!fp) return 1;
    fclose(fp);

    res = WritePNGn(tmp, image);
    if (res) return res;
    silent = 0;
    res = create_icon(1, &tmp, file, 1, hotspot_x, hotspot_y, alpha_threshold, bitdepth);
    removeTempFile();

    return !res;
} 
