#include <stdlib.h>
#include <stdio.h>

typedef unsigned char byte;
typedef unsigned char u8;
typedef signed char s8;
typedef signed short s16;
typedef unsigned short u16;
typedef signed long s32;
typedef unsigned long u32;

typedef FILE* FS_file_handle;

byte FS_fgetbyte(FS_file_handle file)
{
	return((byte)fgetc(file));
}

u16 FS_fget16(FS_file_handle file)
{
	u16 c;
	byte l=FS_fgetbyte(file);
	byte h=FS_fgetbyte(file);
	c=l|(h<<8);
	return(c);
}

u32 FS_fget32(FS_file_handle file)
{
	u32 c;
	u16 l=FS_fget16(file);
	u16 h=FS_fget16(file);
	c=l|(h<<16);
	return(c);
}

s32 FS_fseek(FS_file_handle file, s32 offset, s32 origin)
{
	return(fseek(file,offset,origin));
}

typedef struct	_bitmap_file_header
{	
	u16		file_type;
	u32		file_size;
	u16		reserved1;
	u16		reserved2;
	u32		bitmap_offset;

}	bitmap_file_header;

typedef struct	_bitmap_info_header
{	
	u32		header_size;
	u32		width;
	u32		height;
	u16		number_of_planes;
	u16		bits_per_pixel;
	u32		compression;
	u32		bitmap_size;
	u32		device_width;
	u32		device_height;
	u32		number_of_colors;
	u32		number_of_important_colors;

}	bitmap_info_header;

byte BMP_palette[256*3];

void BMP_write_line(s32 x,s32 y,s32 width,FS_file_handle file,byte *data,s32 pad_bytes,s32 in_color_depth,s32 out_color_depth)
{	s32		i,j,xx,offset=0;
	s32		color_index;
	s32		w,dd,ii;
	byte	color;
	u16		*data16;
	u16		color16;
	byte	R,G,B;

	switch(out_color_depth)
	{	case 8:		switch(in_color_depth)
					{	case 8:     for(i=0;i<width;i++)
									{
#if(0)
										data[offset]=FS_fgetbyte(file);
										offset++;
#else
										color_index=FS_fgetbyte(file);
										R=BMP_palette[color_index*3+0];
										G=BMP_palette[color_index*3+1];
										B=BMP_palette[color_index*3+2];
										color=R&0xe0;
										color|=(G>>3)&0x1c;
										color|=(B>>6)&0x03;
										data[offset]=color;
										offset++;
#endif
									}
									break;
					}
					break;
#if(0)
		case 16:	data16=(u16*)data;
					switch(in_color_depth)
					{	case 1:		w=(width>>3);
            						if(width&7) w++;
            						for(i=0;i<w;i++)
			        				{	dd=FS_fgetbyte(file);
										for(j=0;j<8;j++)
										{   ii=(i<<3)+j;
											if(ii>=width) break;
											xx=x+ii;
											if((xx>=GFX_clip_x1) && (xx<=GFX_clip_x2))	
                            				{	if(dd & (1<<(7-j))) color16=0xffff;
                            					else color16=0;
                            					data16[offset]=color16;
											}
											offset++;
										}
            						}
									break;

						case 4:		w=(width>>1);
            						if(width&1) w++;
            						for(i=0;i<w;i++)
			        				{   dd=FS_fgetbyte(file);
                        				for(j=0;j<2;j++)
                        				{   ii=(i<<1)+j;
											if(ii>=width) break;
											xx=x+ii;
											if((xx>=GFX_clip_x1) && (xx<=GFX_clip_x2))	
                            				{	color_index=((dd>>((1-j)<<2))&0xf)*3;
												PC_READ_PALETTE(BMP_palette,color_index,R,G,B);
												PC_RGB_TO_D16(color16,R,G,B);
												data16[offset]=color16;
											}
											offset++;
										}
        							}
									break;

						case 8:		for(i=0;i<width;i++)
			        				{	xx=x+i;
										if((xx>=GFX_clip_x1) && (xx<=GFX_clip_x2))
										{	color_index=FS_fgetbyte(file)*3;
											PC_READ_PALETTE(BMP_palette,color_index,R,G,B);
											PC_RGB_TO_D16(color16,R,G,B);
											data16[offset]=color16;
										}
										else
										{	FS_fgetbyte(file);
										}
										offset++;
            						}
									break;

						case 24:	for(i=0;i<width;i++)
        							{	xx=x+i;
										if((xx>=GFX_clip_x1) && (xx<=GFX_clip_x2))
										{	B=FS_fgetbyte(file);
            								G=FS_fgetbyte(file);
											R=FS_fgetbyte(file);
											PC_RGB_TO_D16(color16,R,G,B);
											data16[offset]=color16;
										}
										else
										{	FS_fgetbyte(file);
											FS_fgetbyte(file);
											FS_fgetbyte(file);
										}
										offset++;
            						}
									break;
					}
					break;

		case 24:	switch(in_color_depth)
					{	case 1:		w=(width>>3);
            						if(width&7) w++;
            						for(i=0;i<w;i++)
			        				{	dd=FS_fgetbyte(file);
										for(j=0;j<8;j++)
										{   ii=(i<<3)+j;
											if(ii>=width) break;
											xx=x+ii;
											if((xx>=GFX_clip_x1) && (xx<=GFX_clip_x2))	
                            				{	if(dd & (1<<(7-j))) color=255;
                            					else color=0;
                            					data[offset+2]=color;
                            					data[offset+1]=color;
                            					data[offset+0]=color;
											}
											offset+=3;
										}
            						}
									break;

						case 4:		w=(width>>1);
            						if(width&1) w++;
            						for(i=0;i<w;i++)
			        				{   dd=FS_fgetbyte(file);
                        				for(j=0;j<2;j++)
                        				{   ii=(i<<1)+j;
											if(ii>=width) break;
											xx=x+ii;
											if((xx>=GFX_clip_x1) && (xx<=GFX_clip_x2))	
                            				{	color_index=((dd>>((1-j)<<2))&0xf)*3;
												PC_READ_PALETTE(BMP_palette,color_index,R,G,B);
												PC_WRITE_RGB(data,offset,R,G,B);
											}
											offset+=3;
										}
        							}
									break;

						case 8:		for(i=0;i<width;i++)
			        				{	xx=x+i;
										if((xx>=GFX_clip_x1) && (xx<=GFX_clip_x2))
										{	color_index=FS_fgetbyte(file)*3;
											PC_READ_PALETTE(BMP_palette,color_index,R,G,B);
											PC_WRITE_RGB(data,offset,R,G,B);
										}
										else
										{
											FS_fgetbyte(file);
										}
										offset+=3;
            						}
									break;

						case 24:	for(i=0;i<width;i++)
        							{	xx=x+i;
										if((xx>=GFX_clip_x1) && (xx<=GFX_clip_x2))
										{	B=FS_fgetbyte(file);
            								G=FS_fgetbyte(file);
											R=FS_fgetbyte(file);
											PC_WRITE_RGB(data,offset,R,G,B);
										}
										else
										{	FS_fgetbyte(file);
											FS_fgetbyte(file);
											FS_fgetbyte(file);
										}
										offset+=3;
            						}
									break;
					}
					break;

		case 32:	switch(in_color_depth)
					{	case 1:		w=(width>>3);
            						if(width&7) w++;
            						for(i=0;i<w;i++)
			        				{	dd=FS_fgetbyte(file);
										for(j=0;j<8;j++)
										{   ii=(i<<3)+j;
											if(ii>=width) break;
											xx=x+ii;
											if((xx>=GFX_clip_x1) && (xx<=GFX_clip_x2))	
                            				{	if(dd & (1<<(7-j))) color=255;
                            					else color=0;
                            					data[offset+2]=color;
                            					data[offset+1]=color;
                            					data[offset+0]=color;
											}
											offset+=4;
										}
            						}
									break;

						case 4:		w=(width>>1);
            						if(width&1) w++;
            						for(i=0;i<w;i++)
			        				{   dd=FS_fgetbyte(file);
                        				for(j=0;j<2;j++)
                        				{   ii=(i<<1)+j;
											if(ii>=width) break;
											xx=x+ii;
											if((xx>=GFX_clip_x1) && (xx<=GFX_clip_x2))	
                            				{	color_index=((dd>>((1-j)<<2))&0xf)*3;
												PC_READ_PALETTE(BMP_palette,color_index,R,G,B);
												PC_WRITE_RGB(data,offset,R,G,B);
											}
											offset+=4;
										}
        							}
									break;

						case 8:		for(i=0;i<width;i++)
			        				{	xx=x+i;
										if((xx>=GFX_clip_x1) && (xx<=GFX_clip_x2))
										{	color_index=FS_fgetbyte(file)*3;
											PC_READ_PALETTE(BMP_palette,color_index,R,G,B);
												PC_WRITE_RGB(data,offset,R,G,B);
										}
										else
										{	FS_fgetbyte(file);
										}
										offset+=4;
            						}
									break;

						case 24:	for(i=0;i<width;i++)
        							{	xx=x+i;
										if((xx>=GFX_clip_x1) && (xx<=GFX_clip_x2))
										{	B=FS_fgetbyte(file);
            								G=FS_fgetbyte(file);
											R=FS_fgetbyte(file);
											PC_WRITE_RGB(data,offset,R,G,B);
										}
										else
										{	FS_fgetbyte(file);
											FS_fgetbyte(file);
											FS_fgetbyte(file);
										}
										offset+=4;
            						}
									break;
					}
					break;
#endif
	}
	for(i=0;i<pad_bytes;i++)
	{	FS_fgetbyte(file);
	}
}

void BMP_load_file_header(bitmap_file_header *h,FS_file_handle file)
{	
	h->file_type=FS_fget16(file);
	h->file_size=FS_fget32(file);
	h->reserved1=FS_fget16(file);
	h->reserved2=FS_fget16(file);
	h->bitmap_offset=FS_fget32(file);
}

void BMP_load_info_header(bitmap_info_header *h,FS_file_handle file)
{	
	h->header_size=FS_fget32(file);
	h->width=FS_fget32(file);
	h->height=FS_fget32(file);
	h->number_of_planes=FS_fget16(file);
	h->bits_per_pixel=FS_fget16(file);
	h->compression=FS_fget32(file);
	h->bitmap_size=FS_fget32(file);
	h->device_width=FS_fget32(file);
	h->device_height=FS_fget32(file);
	h->number_of_colors=FS_fget32(file);
	h->number_of_important_colors=FS_fget32(file);
}

byte *b_data;
byte *b_palette;
s32 b_color_depth=8;
s32 b_row_bytes;
s32 b_width;
s32 b_height;
s32 b_size;

byte BMP_load(s32 x,s32 y,FS_file_handle file)
{   bitmap_file_header	file_header;
	bitmap_info_header	info_header;
	s32					ncolors,i,j,k,width,height,offset;
    s32					bitmap_size,row_bytes,used_row_bytes,total_bits;
    s32					dest_row_bytes,pad_bytes;

	FS_fseek(file,0,SEEK_SET);
	BMP_load_file_header(&file_header,file);

	if((file_header.file_type&0xff)!='B')	return(0);
	if((file_header.file_type>>8)!='M')		return(0);
	
	BMP_load_info_header(&info_header,file);
	width=info_header.width;
	height=info_header.height;
	ncolors=1<<info_header.bits_per_pixel;

	if(info_header.bits_per_pixel==24)
	{	used_row_bytes=width*3;
	}
	else
	{	total_bits=width*info_header.bits_per_pixel;
		used_row_bytes=total_bits>>3;				/*total_bits/8	*/
		if((total_bits%8)>0) used_row_bytes+=1;     /*	total_bits%8	*/
	}
	
	if((used_row_bytes%4)>0) row_bytes=used_row_bytes+(4-(used_row_bytes%4));
	else row_bytes=used_row_bytes;
    if(b_color_depth<=8)
    {	dest_row_bytes=width/(8/b_color_depth);
	    if((width%(8/b_color_depth))>0) dest_row_bytes++;
	}
    else
    {	dest_row_bytes=width*(b_color_depth/8);
    }
	bitmap_size=dest_row_bytes*height;
	b_width=width;
	b_height=height;
	b_size=bitmap_size;
	b_data=(byte*)malloc(b_size);
	b_palette=(byte*)malloc(256*3);
	b_row_bytes=dest_row_bytes;
	memset(b_data,0,b_size);
    if(b_color_depth<=8)
    {	if(b_palette==NULL) return(0);
    }
    if(b_data==NULL)
    {   return(0);
    }
	if(info_header.bits_per_pixel<=8)
	{	k=0;
		if(b_color_depth<=8)
		{	
			for(i=0;i<ncolors;i++)
			{	BMP_palette[k+2]=b_palette[k+2]=FS_fgetbyte(file);
				BMP_palette[k+1]=b_palette[k+1]=FS_fgetbyte(file);
				BMP_palette[k+0]=b_palette[k+0]=FS_fgetbyte(file);
				k+=3;
				FS_fgetbyte(file);
			}
		}
		else
		{	for(i=0;i<ncolors;i++)
			{	BMP_palette[k+2]=FS_fgetbyte(file);
				BMP_palette[k+1]=FS_fgetbyte(file);
				BMP_palette[k+0]=FS_fgetbyte(file);
				k+=3;
				FS_fgetbyte(file);
			}
		}
	}
	FS_fseek(file,file_header.bitmap_offset,SEEK_SET);
	offset=(y+height-1)*b_row_bytes+x;
	pad_bytes=row_bytes-used_row_bytes;
	
	for(j=height-1;j>=0;j--)
	{
		BMP_write_line(x,(y+j),width,file,&b_data[offset],pad_bytes,info_header.bits_per_pixel,b_color_depth);
		
		offset-=b_row_bytes;
	}
	
	return(1);
}

void write_buffer(FILE *file_handle)
{
#if(1)
	s32 i;
	fprintf(file_handle,"\n{");
	for(i=0;i<b_size;i++)
	{
		if((i%32)==0) fprintf(file_handle,"\n");
		fprintf(file_handle,"0x%02X,",b_data[i]);
		
	}
	fprintf(file_handle,"\n};");
#else
	s32 i,j,offset;
	char outchars[]={'.','*','#','.','*','#','+','-'};

	for(j=0;j<b_height;j++)
	{
		fprintf(file_handle,"\n");
		for(i=0;i<b_width;i++)
		{
			offset=j*b_width+i;
			fprintf(file_handle,"%c",outchars[b_data[offset]%8]);
		}
	}
#endif
}

int main(int argc,char *argv[])
{
	FILE *input_file_handle;
	FILE *output_file_handle;
	
	if(argc<3)
	{
		printf("\nUsage: converter <input-file> <output-file>");
		exit(0);
	}
	input_file_handle=fopen(argv[1],"rb");
	if(input_file_handle==NULL) 
	{
		printf("\nInput file %s not found",argv[1]);
		exit(0);
	}
	BMP_load(0,0,input_file_handle);
	fclose(input_file_handle);
	output_file_handle=fopen(argv[2],"wb");
	if(output_file_handle==NULL)
	{
		printf("\nUnable to create output file %s",argv[2]);
		exit(0);
	}

	write_buffer(output_file_handle);
	fclose(output_file_handle);
	free(b_data);
	free(b_palette);
	printf("\nWrote bitmap width=%d, height=%d, total=%d bytes",b_width,b_height,b_size);
}
