/*--------------------------------------------------------------------------*/
/*																			*/
/* Ico2bmp - Small utility program, which reads a icon file (.ico), extracts*/
/* the color part of the icon, and generates a bitmap file (.bmp).			*/
/*																			*/
/* Author: Niels Erik Holm, ICL DATA A/S, Copenhagen Denmark, neh@rci.dk	*/
/* No copyrights apply, no Warranty											*/
/*																			*/
/*--------------------------------------------------------------------------*/

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <process.h>
#include <dos.h>

char *	Version = "Ico2bmp v1.0 - Extracts color bitmap from icon file - Author: neh@rci.dk";

typedef	struct {
	WORD	icoReserved;		/* Reserved, must be set to 0	*/
	WORD	icoResourceType;	/* Resource type, 1 for icon resources */
	WORD	icoResourceCount;	/* Number of images in the file */
}	IconFileHeader;

typedef	struct {
	BYTE	Width;				/* Width in pixels */
	BYTE	Height;				/* Height in pixels */
	BYTE	ColorCount;			/* Number of colors */
	BYTE	Reserved1;
	WORD	Reserved2;
	WORD	Reserved3;
	DWORD	icoDIBSize;			/* Size in bytes of the pixel array */
	DWORD	icoDIBOffset;		/* Offset in bytes from beginning of file */
								/* to pixel array. */
}	IconRscDir;

typedef	struct {
	IconFileHeader	IHead;		/* Icon file header */
	IconRscDir		IDir;		/* Icon resource directory */
	BYTE			IBits[1000];
}	IconFile;

BITMAPFILEHEADER	bmFH;
BITMAPINFOHEADER	bmIH;
RGBQUAD				bmCol[16];

unsigned char		Idata[1000];
unsigned char		Odata[1000];

int		opterr = 1;
int		optind = 1;
int		optopt;
char *	optarg;

struct	find_t	fb;
int		MoreFiles;

static int getopt(int,char **,char *);
static int  ProcessFile(char *,char *);
static void ManipulateBits(BYTE *,BYTE *,int);
static void usage(void);

main(argc,argv)
int argc;
char *argv[];
{
	int		opt,attr;
	char *	Fptr;
	char	InFileSpec[64];
	char	FirstName[64];
	char	InName[64];
	char	OutName[64];

	InFileSpec[0] = 0;
	attr = 0;

	while ((opt = getopt(argc,argv,"hi:")) != -1)
		switch (opt) {
			case 'h':
				usage();
				exit(1);
				break;
			case 'i':
				strcpy(InFileSpec,optarg);
				break;
		}

	if (InFileSpec[0] == 0) {
		usage();
		exit(1);
	}

	if ((Fptr = strchr(InFileSpec,'.')) == NULL)
		strcat(InFileSpec,".ico");

	MoreFiles = (_dos_findfirst(InFileSpec,attr,&fb) == 0);
	if (!MoreFiles) {
		fprintf (stderr,"No files matching %s\n",InFileSpec);
		exit(1);
	}

	strcpy(FirstName,fb.name);

	while (MoreFiles) {

		strcpy(InName,fb.name);
		strcpy(OutName,InName);
		Fptr = strchr(OutName,'.');
		strcpy(Fptr,".bmp");

		(void) ProcessFile(InName,OutName);

		MoreFiles = ((_dos_findnext(&fb)==0) && (strcmp(FirstName,fb.name)!=0));
	}
}

static int ProcessFile(char * InFileName,char * OutFileName)
{
	FILE *		Ifile;
	FILE *		Ofile;
	IconRscDir*	pIRD;
	BITMAPINFOHEADER * pBMIH;
	RGBQUAD *	pRGB;
	BYTE *		pColorData;
	BYTE *		pMonoData;
	int			i,j,Ino,Cno,DataNo,ColorTabSize,BitMapSize,Pixels,BitsPerPixel;
	char *		Iptr;
	unsigned char *		Bptr;


	if ((Ifile = fopen(InFileName,"rb")) == NULL) {
		fprintf (stderr,"Error opening input file %s\n",InFileName);
		return(0);
	}
	fread (Idata,1,sizeof(Idata),Ifile);
	fclose(Ifile);

	if ((Ofile = fopen(OutFileName,"wb")) == NULL) {
		fprintf (stderr,"Error opening output file %s\n",OutFileName);
		return(0);
	}

	fprintf (stdout,"Processing %s -> %s",InFileName,OutFileName);

	Iptr = Idata;

	if (((IconFileHeader *)Iptr)->icoReserved != 0) {
		fprintf (stderr,"\nSorry! Invalid icon file.\n");
		return(0);
	}

	Ino = ((IconFileHeader *)Iptr)->icoResourceCount;
	if (Ino != 1) {
		fprintf (stderr,"\nSorry! Ico2bmp can only handle icon file with one icons.\n");
		return(0);
	}

	Iptr += sizeof(IconFileHeader);
	pIRD = (IconRscDir *)Iptr;
	pBMIH= (BITMAPINFOHEADER *)(Idata + pIRD->icoDIBOffset);
	pRGB = (RGBQUAD *)((BYTE *)pBMIH+sizeof(BITMAPINFOHEADER));
	ColorTabSize = pIRD->ColorCount*sizeof(RGBQUAD);
	pColorData = (BYTE *)((BYTE *)pRGB+ColorTabSize);
	BitsPerPixel = pBMIH->biBitCount;
	Pixels = pIRD->Width*pIRD->Height*BitsPerPixel;
	BitMapSize = Pixels/8;
	pMonoData = (BYTE *)(pColorData + BitMapSize);

	Cno=pBMIH->biBitCount;
	if (Cno==1)
		Cno=2;
	else if (Cno==4)
		Cno=16;
	else if (Cno==8)
		Cno=256;
	else if (Cno==24)
		Cno=0;

	if (Cno != 16) {
		fprintf (stderr,"\nSorry! Ico2bmp can only handle 16 color icons\n");
		return(0);
	}

	bmFH.bfType = 'B' | 'M'<<8;
	bmFH.bfSize = sizeof(bmFH)+sizeof(bmIH)+ColorTabSize+BitMapSize;

	bmFH.bfOffBits = sizeof(bmFH)+sizeof(bmIH)+ColorTabSize;

	bmIH.biSize				= pBMIH->biSize;
	bmIH.biWidth			= pBMIH->biWidth;
	bmIH.biHeight			= pBMIH->biHeight/2;
	bmIH.biPlanes			= pBMIH->biPlanes;
	bmIH.biBitCount			= pBMIH->biBitCount;
	bmIH.biCompression		= pBMIH->biCompression;
	bmIH.biSizeImage		= BitMapSize;
	bmIH.biXPelsPerMeter	= pBMIH->biXPelsPerMeter;
	bmIH.biYPelsPerMeter	= pBMIH->biYPelsPerMeter;
	bmIH.biClrUsed			= pBMIH->biClrUsed;
	bmIH.biClrImportant		= Cno;

	for (i=0;i<Cno;i++)
		bmCol[i] = *((RGBQUAD*)((BYTE*)pRGB + i*sizeof(RGBQUAD)));

	ManipulateBits(pColorData,pMonoData,Pixels);

	memcpy(Odata,pColorData,BitMapSize);

	fwrite(&bmFH,sizeof(bmFH),1,Ofile);
	fwrite(&bmIH,sizeof(bmIH),1,Ofile);
	fwrite(&bmCol[0],ColorTabSize,1,Ofile);
	fwrite(&Odata[0],BitMapSize,1,Ofile);

	fclose(Ofile);

	fprintf (stdout,"\n");

	return(1);
}

static void ManipulateBits(BYTE * pColorData,BYTE * pMonoData,int pixels)
/* Works only on 16 color bitmaps */
{
	register int i,MonoBits,XorMask,j;
	register BYTE * pMD;

	pMD = pMonoData;
	MonoBits = *pMD;
	j=0;

	for (i=0;i<pixels/2;i++) {
		if (MonoBits & 0x80)
			*(pColorData+i) |= 0xF0;
		MonoBits <<= 1;
		j++;
		if (MonoBits & 0x80)
			*(pColorData+i) |= 0x0F;
		MonoBits <<= 1;
		j++;
		if (j==8) {
			++pMD;
			MonoBits = *pMD;
			j=0;
		}
	}
}

static void usage()
{
	fprintf (stdout,"%s\n",Version);
	fprintf (stdout,"Usage: ico2bmp -i <inputfile> [-h]\n");
	fprintf (stdout,"\n");
	fprintf (stdout," -h:  Display this help\n");
	fprintf (stdout," -i:  Name of input file. If no extension is specified .ico is used.\n");
	fprintf (stdout,"      Wildcards are allowed. Ex. -i *.ico\n");
}

#define	index	strchr

#define ERR(s, c)	if(opterr){\
	extern int write();\
	char errbuf[2];\
	errbuf[0] = c; errbuf[1] = '\n';\
	(void) fprintf(stderr, argv[0], (unsigned)strlen(argv[0]));\
	(void) fprintf(stderr, s, (unsigned)strlen(s));\
	(void) fprintf(stderr, errbuf, 2);}

static int getopt(int argc, char ** argv, char * opts)
{
	static int sp = 1;
	register int c;
	register char *cp;

	if(sp == 1)
		if(optind >= argc ||
		   argv[optind][0] != '-' || argv[optind][1] == '\0')
			return(-1);
		else if(strcmp(argv[optind], "--") == 0) {
			optind++;
			return(-1);
		}
	optopt = c = argv[optind][sp];
	if(c == ':' || (cp=index(opts, c)) == 0) {
		ERR(": illegal option -- ", c);
		if(argv[optind][++sp] == '\0') {
			optind++;
			sp = 1;
		}
		return('?');
	}
	if(*++cp == ':') {
		if(argv[optind][sp+1] != '\0')
			optarg = &argv[optind++][sp+1];
		else if(++optind >= argc) {
			ERR(": option requires an argument -- ", c);
			sp = 1;
			return('?');
		} else
			optarg = argv[optind++];
		sp = 1;
	} else {
		if(argv[optind][++sp] == '\0') {
			sp = 1;
			optind++;
		}
		optarg = 0;
	}
	return(c);
}
