//
// BMP Suite (2012 rewrite)
// Copyright (C) 2012 Jason Summers
//
//  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 3 of the License, or
//  (at your option) any later version.
//
//
// Image files generated by this program are in the public domain.
//

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

#include <sys/types.h>
#include <sys/stat.h>

// #define SMALL_IMAGES

#define BMP_MAX_SIZE 100000

#define bmpovl_width 78
#define bmpovl_height 26
#define bmpovl_xpos 25
#define bmpovl_ypos 19

static const char *bmpovl[] = {
 "11111111111111111111.......11....................11...11111111111111111111....",
 "1111111111111111111111.....111..................111...1111111111111111111111..",
 "11222222222222222222111....1111................1111...11222222222222222222111.",
 "112222222222222222222111...11211..............11211...112222222222222222222111",
 "112211111111111111122211...112211............112211...112211111111111111122211",
 "112211111111111111112211...1122211..........1122211...112211111111111111112211",
 "112211...........1112211...11222211........11222211...112211...........1112211",
 "112211............112211...112212211......112212211...112211............112211",
 "112211............112211...1122112211....1122112211...112211............112211",
 "112211...........1112211...11221112211..11221112211...112211...........1112211",
 "11221111111111111112211....112211112211112211112211...112211111111111111112211",
 "1122111111111111112211.....112211.1122112211.112211...112211111111111111122211",
 "112222222222222222211......112211..11222211..112211...112222222222222222222111",
 "112222222222222222211......112211...112211...112211...11222222222222222222111.",
 "1122111111111111112211.....112211....1111....112211...1122111111111111111111..",
 "11221111111111111112211....112211.....11.....112211...11221111111111111111....",
 "112211...........1112211...112211............112211...112211..................",
 "112211............112211...112211............112211...112211..................",
 "112211............112211...112211............112211...112211..................",
 "112211...........1112211...112211............112211...112211..................",
 "112211111111111111112211...112211............112211...112211..................",
 "112211111111111111122211...112211............112211...112211..................",
 "112222222222222222222111...112211............112211...112211..................",
 "11222222222222222222111....112211............112211...112211..................",
 "1111111111111111111111.....111111............111111...111111..................",
 "11111111111111111111.......111111............111111...111111.................."
};

struct context {
	const char *filename;
	unsigned char *mem;
	size_t mem_used;
	int bpp;
	int pal_entries;
	int clr_used;
	int headersize;
	int bitfieldssize;
	int palettesize;
	int extrabytessize;
	int bitsoffset; // Offset from beginning of file
	int bitssize;
	int profile_offset; // Offset from beginning of file
	int profile_size;
	int w, h;
	int rowsize;
	int xpelspermeter, ypelspermeter;
#define CMPR_RLE8 1
#define CMPR_RLE4 2
#define CMPR_JPEG 4
#define CMPR_PNG  5
#define BI_BITFIELDS      3
#define BI_ALPHABITFIELDS 6
	int compression;

	int pal_gs; // grayscale palette
	int pal_wb; // 2-color, palette[0] = white
	int pal_bg; // 2-color, blue & green
	int pal_p1; // 1-color
	unsigned int bf_r, bf_g, bf_b, bf_a; // used if compression==3
	unsigned int nbits_r, nbits_g, nbits_b, nbits_a;
	unsigned int bf_shift_r, bf_shift_g, bf_shift_b, bf_shift_a;
	int dither;
	int topdown;
	int embed_profile;
	int link_profile;
	int alphahack32;
	int halfheight;
	int zero_biSizeImage;
	int bad_biSizeImage;
	int bad_bfSize;
	int bad_width;
	int bad_reallybig;
	int bad_bitcount;
	int bad_planes;
	int bad_palettesize;
	int bad_rle;
	int bad_rle_bis;
	int bad_rle_ter;
	int cut_rle;
	int bad_eof;
	int rletrns;
	int palette_reserve; // Reserve palette color #0
	int cbsize_flag;
};

static void set_int16(struct context *c, size_t offset, int v)
{
	c->mem[offset] = v&0xff;
	c->mem[offset+1] = (v>>8)&0xff;
}

static void set_int32(struct context *c, size_t offset, int v)
{
	c->mem[offset] = v&0xff;
	c->mem[offset+1] = (v>>8)&0xff;
	c->mem[offset+2] = (v>>16)&0xff;
	c->mem[offset+3] = (v>>24)&0xff;
}

static void set_uint32(struct context *c, size_t offset, unsigned int v)
{
	c->mem[offset] = v&0xff;
	c->mem[offset+1] = (v>>8)&0xff;
	c->mem[offset+2] = (v>>16)&0xff;
	c->mem[offset+3] = (v>>24)&0xff;
}

// Returns an int between 0 and m, inclusive.
static int scale_to_int(double x, int m)
{
	int s;
#define BMPSUITE_EPSILON 0.0000001
	s = (int)(0.5+BMPSUITE_EPSILON+x*m);
	if(s<0) s=0;
	if(s>m) s=m;
	return s;
}

static double srgb_to_linear(double v_srgb)
{
	if(v_srgb<=0.04045) {
		return v_srgb/12.92;
	}
	else {
		return pow( (v_srgb+0.055)/(1.055) , 2.4);
	}
}

static void get_pixel_color(struct context *c, int x1, int y1,
	double *pr, double *pg, double *pb, double *pa)
{
	unsigned char t;
	int x, y;

#ifdef SMALL_IMAGES
	x = x1+21;
	y = y1+16;
#else
	x = x1;
	y = y1;
#endif

	if(x>=bmpovl_xpos && x<(bmpovl_xpos+bmpovl_width) &&
	   y>=bmpovl_ypos && y<(bmpovl_ypos+bmpovl_height))
	{
		t = bmpovl[y-bmpovl_ypos][x-bmpovl_xpos];
		if(t=='1') {
			*pr = 0.0; *pg = 0.0; *pb = 0.0; *pa = 1.0;
			return;
		}
		else if(t=='2') {
			if(c->bf_a) {
				// Make the inside of the overlay transparent, if possible.
				if( (y-bmpovl_ypos)<(bmpovl_height/2) ) {
					// Make the top half complete transparent ("transparent green").
					*pr = 0.0; *pg = 1.0; *pb = 0.0; *pa = 0.0;
				}
				else {
					// Make the bottom half a red gradient from transparent to opaque.
					*pr = 1.0; *pg = 0.0; *pb = 0.0;
					*pa = 2*((double)(y-bmpovl_ypos)) /(bmpovl_height) -1.0;
				}
			}
			else if(c->rletrns) {
				*pr = 1.0; *pg = 1.0; *pb = 1.0; *pa = 0.0;
			}
			else {
				*pr = 1.0; *pg = 1.0; *pb = 1.0; *pa = 1.0;
			}
			return;
		}
	}

	*pa = 1.0;

	// Standard truecolor image
	if(x<32) {
		*pr = ((double)(63-y))/63.0;
		*pg = ((double)(x%32))/31.0;
		*pb = ((double)(x%32))/31.0;
	}
	else if(x<64) {
		*pr = ((double)(x%32))/31.0;
		*pg = ((double)(63-y))/63.0;
		*pb = ((double)(x%32))/31.0;
	}
	else if(x<96) {
		*pr = ((double)(x%32))/31.0;
		*pg = ((double)(x%32))/31.0;
		*pb = ((double)(63-y))/63.0;
	}
	else {
		*pr = ((double)(159-y))/255.0;
		*pg = ((double)(159-y))/255.0;
		*pb = ((double)(159-y + x%32))/255.0;
	}
}

static int ordered_dither_lowlevel(double fraction, int x, int y)
{
	double threshold;
	static const float pattern[64] = {
		 0.5/64,48.5/64,12.5/64,60.5/64, 3.5/64,51.5/64,15.5/64,63.5/64,
		32.5/64,16.5/64,44.5/64,28.5/64,35.5/64,19.5/64,47.5/64,31.5/64,
		 8.5/64,56.5/64, 4.5/64,52.5/64,11.5/64,59.5/64, 7.5/64,55.5/64,
		40.5/64,24.5/64,36.5/64,20.5/64,43.5/64,27.5/64,39.5/64,23.5/64,
		 2.5/64,50.5/64,14.5/64,62.5/64, 1.5/64,49.5/64,13.5/64,61.5/64,
		34.5/64,18.5/64,46.5/64,30.5/64,33.5/64,17.5/64,45.5/64,29.5/64,
		10.5/64,58.5/64, 6.5/64,54.5/64, 9.5/64,57.5/64, 5.5/64,53.5/64,
		42.5/64,26.5/64,38.5/64,22.5/64,41.5/64,25.5/64,37.5/64,21.5/64
	 };

	threshold = pattern[(x%8) + 8*(y%8)];
	return (fraction >= threshold) ? 1 : 0;
}

// 'v' is on a scale from 0.0 to 1.0.
// maxcc is the max color code; e.g. 255.
// This returns the output color code on a scale of 0 to maxcc.
static int ordered_dither(double v_to_1, int maxcc, int x, int y)
{
	double v_to_1_linear;
	double v_to_maxcc;
	double floor_to_maxcc, ceil_to_maxcc;
	double floor_to_1, ceil_to_1;
	double floor_to_1_linear, ceil_to_1_linear;
	double fraction;

	v_to_maxcc = v_to_1*maxcc;
	floor_to_maxcc = floor(v_to_maxcc);
	if(floor_to_maxcc>=(double)maxcc) return maxcc;
	ceil_to_maxcc = floor_to_maxcc+1.0;

	// The two possible values to return are floor_to_maxcc and ceil_to_maxcc.
	// v_to_maxcc's brightness is some fraction of the way between
	// floor_to_maxcc's brightness and ceil_to_maxcc's brightness, and we need
	// to calculate that fraction. To do that, convert everything to a linear
	// colorspace.

	floor_to_1 = floor_to_maxcc/maxcc;
	ceil_to_1 = ceil_to_maxcc/maxcc;

	floor_to_1_linear = srgb_to_linear(floor_to_1);
	v_to_1_linear = srgb_to_linear(v_to_1);
	ceil_to_1_linear = srgb_to_linear(ceil_to_1);

	fraction = (v_to_1_linear-floor_to_1_linear)/(ceil_to_1_linear-floor_to_1_linear);

	if(ordered_dither_lowlevel(fraction,x,y))
		return (int)ceil_to_maxcc;
	else
		return (int)floor_to_maxcc;
}

static void set_pixel(struct context *c, int x, int y,
  double r, double g, double b, double a)
{
	unsigned int r2, g2, b2, a2;
	int tmp1, tmp2, tmp3;
	int p;
	size_t row_offs;
	size_t offs;
	double tmpd;
	unsigned int u;

	if(c->topdown)
		row_offs = y*c->rowsize;
	else
		row_offs = (c->h-y-1)*c->rowsize;

	if(c->bpp==32) {
		offs = row_offs + 4*x;
		r2 = scale_to_int(r,(1<<c->nbits_r)-1);
		g2 = scale_to_int(g,(1<<c->nbits_g)-1);
		b2 = scale_to_int(b,(1<<c->nbits_b)-1);
		if(c->alphahack32) a = 1.0 - ((double)y)/63.0;
		if(c->bf_a || c->alphahack32) a2 = scale_to_int(a,(1<<c->nbits_a)-1);
		else a2 = 0;
		u = (r2<<c->bf_shift_r) | (g2<<c->bf_shift_g) | (b2<<c->bf_shift_b);
		if(c->bf_a) u |= a2<<c->bf_shift_a;
		else if(c->alphahack32) u |= a2<<24;
		c->mem[c->bitsoffset+offs+0] = (unsigned char)(u&0xff);
		c->mem[c->bitsoffset+offs+1] = (unsigned char)((u>>8)&0xff);
		c->mem[c->bitsoffset+offs+2] = (unsigned char)((u>>16)&0xff);
		c->mem[c->bitsoffset+offs+3] = (unsigned char)((u>>24)&0xff);
	}
	else if(c->bpp==24) {
		offs = row_offs + 3*x;
		r2 = (unsigned char)scale_to_int(r,255);
		g2 = (unsigned char)scale_to_int(g,255);
		b2 = (unsigned char)scale_to_int(b,255);
		c->mem[c->bitsoffset+offs+0] = b2;
		c->mem[c->bitsoffset+offs+1] = g2;
		c->mem[c->bitsoffset+offs+2] = r2;
	}
	else if(c->bpp==16) {
		offs = row_offs + 2*x;
		if(c->dither) {
			r2 = ordered_dither(r,(1<<c->nbits_r)-1,x,y);
			g2 = ordered_dither(g,(1<<c->nbits_g)-1,x,y);
			b2 = ordered_dither(b,(1<<c->nbits_b)-1,x,y);
		}
		else {
			r2 = scale_to_int(r,(1<<c->nbits_r)-1);
			g2 = scale_to_int(g,(1<<c->nbits_g)-1);
			b2 = scale_to_int(b,(1<<c->nbits_b)-1);
		}
		if(c->bf_a) a2 = scale_to_int(a,(1<<c->nbits_a)-1);

		u = (r2<<c->bf_shift_r) | (g2<<c->bf_shift_g) | (b2<<c->bf_shift_b);
		if(c->bf_a) u |= a2<<c->bf_shift_a;
		c->mem[c->bitsoffset+offs+0] = (unsigned char)(u&0xff);
		c->mem[c->bitsoffset+offs+1] = (unsigned char)((u>>8)&0xff);
	}
	else if(c->bpp==8) {
		offs = row_offs + x;
		if (c->pal_gs) {
			double prev = 0.0;
			double current = 0.0;
			int entries = c->pal_entries - c->palette_reserve - 1;

			tmpd = srgb_to_linear(r)*0.212655
			 + srgb_to_linear(g)*0.715158
			 + srgb_to_linear(b)*0.072187;

			p = 1;
			current = srgb_to_linear(p / (double)entries);
			while ((tmpd > current) && (p < entries))
			{
				p++;
				prev = current;
				current = srgb_to_linear(p / (double)entries);
			}
			if (!ordered_dither_lowlevel((tmpd-prev)/(current-prev),x,y))
				p--;
			if(c->palette_reserve) {
				if(p<c->pal_entries) p++;
			}
		}
		else {
			tmp1 = ordered_dither(r,5,x,y);
			tmp2 = ordered_dither(g,6,x,y);
			tmp3 = ordered_dither(b,5,x,y);
			p = tmp1 + tmp2*6 + tmp3*42;
			if(c->palette_reserve) {
				if(p<255) p++;
			}
		}
		c->mem[c->bitsoffset+offs] = p;
	}
	else if(c->bpp==4) {
		offs = row_offs + x/2;
		if (c->pal_gs) {
			double prev = 0.0;
			double current = 0.0;
			int entries = c->pal_entries - c->palette_reserve - 1;

			tmpd = srgb_to_linear(r)*0.212655
			 + srgb_to_linear(g)*0.715158
			 + srgb_to_linear(b)*0.072187;

			p = 1;
			current = srgb_to_linear(p / (double)entries);
			while ((tmpd > current) && (p < entries))
			{
				p++;
				prev = current;
				current = srgb_to_linear(p / (double)entries);
			}
			if (!ordered_dither_lowlevel((tmpd-prev)/(current-prev),x,y))
				p--;
			if(c->palette_reserve) {
				if(p<c->pal_entries) p++;
			}
		}
		else {
			tmp1 = ordered_dither(r,1,x,y);
			tmp2 = ordered_dither(g,2,x,y);
			tmp3 = ordered_dither(b,1,x,y);
			p = tmp1 + tmp2*2 + tmp3*6;
		}
		if(x%2)
			c->mem[c->bitsoffset+offs] |= p;
		else
			c->mem[c->bitsoffset+offs] |= p<<4;
	}
	else if(c->bpp==2) {
		const double gray1 = 0.0908417111646935; // = srgb_to_linear(1/3)
		const double gray2 = 0.4019777798321956; // = srgb_to_linear(2/3)
		offs = row_offs + x/4;
		tmpd = srgb_to_linear(r)*0.212655
			 + srgb_to_linear(g)*0.715158
			 + srgb_to_linear(b)*0.072187;

		if(tmpd<gray1) {
			tmp1 = ordered_dither_lowlevel(tmpd/gray1,x,y) ? 1 : 0;
		}
		else if(tmpd<gray2) {
			tmp1 = ordered_dither_lowlevel((tmpd-gray1)/(gray2-gray1),x,y) ? 2 : 1;
		}
		else {
			tmp1 = ordered_dither_lowlevel((tmpd-gray2)/(1.0-gray2),x,y) ? 3 : 2;
		}
		c->mem[c->bitsoffset+offs] |= tmp1<<(2*(3-x%4));
	}
	else if(c->bpp==1) {
		offs = row_offs + x/8;
		tmpd = srgb_to_linear(r)*0.212655
			 + srgb_to_linear(g)*0.715158
			 + srgb_to_linear(b)*0.072187;
		tmp1 = ordered_dither_lowlevel(tmpd,x,y);
		if(c->pal_wb) tmp1 = 1-tmp1; // Palette starts with white, so invert the colors.
		if(c->pal_p1) tmp1 = 0;
		if(tmp1) {
			c->mem[c->bitsoffset+offs] |= 1<<(7-x%8);
		}
	}
}

static void calc_rle_run_lens(struct context *c, const unsigned char *row,
    int *run_lens, int pixels_per_row)
{
	int i,k,n;

	for(i=0;i<pixels_per_row;i++) {
		n=0;
		for(k=i;k<pixels_per_row;k++) {
			if(n>=255) break;

			if(c->rletrns && row[k]==0) {
				// A "transparent" pixel.
				if(k-i==0) { n++; continue; } // start of a transparent run
				if(row[k-1]==0) { n++; continue; } // continuing a transparent run;
				break; // transparent pixel stops a nontransparent run.
			}

			if(c->rletrns && k-i>=1) {
				if(row[k]!=0 && row[k-1]==0) {
					// nontransparent pixel stops a transparent run.
					break;
				}
			}

			if(c->compression==CMPR_RLE4 && k-i<=1) { n++; continue; } // (RLE4) First two pixels can always be part of the run
			if(c->compression==CMPR_RLE8 && k-i<=0) { n++; continue; } // (RLE8) First pixels can always be part of the run
			if(c->compression==CMPR_RLE4 && row[k]==row[k-2]) { n++; continue; }
			if(c->compression==CMPR_RLE8 && row[k]==row[k-1]) { n++; continue; }
			break;
		}
		run_lens[i] = n;
	}
}

// Note: This is not a particularly good BMP RLE compression algorithm.
// I don't recommend copying it.
static int write_bits_rle(struct context *c)
{
	size_t curpos; // where in c->mem to write to next
	size_t rowpos;
	size_t pixels_per_row;
	unsigned char *row;
	int *run_lens;
	size_t i,j;
	size_t j_logical;
	int k;
	int tmp1, tmp2, tmp3;
	double r,g,b,a;
	int unc_len;
	int unc_len_padded;
	int thresh;

	curpos = c->bitsoffset;
	pixels_per_row = c->w;
	row = malloc(pixels_per_row);
	if(!row) return 0;
	run_lens = (int*)malloc(sizeof(int)*pixels_per_row);
	if(!run_lens) return 0;
	thresh = c->compression==CMPR_RLE4 ? 5 : 4;

	for(j=0;j< (c->cut_rle ? c->h - 5 : c->h);j++) {
		j_logical = c->topdown ? j : c->h-1-j;

		// Temporarily store the palette indices in row[]
		for(i=0;i<c->w;i++) {
			get_pixel_color(c,i,j_logical, &r,&g,&b,&a);

			if(c->compression==CMPR_RLE4) {
				tmp1 = ordered_dither(r,1,i,j_logical);
				tmp2 = ordered_dither(g,2,i,j_logical);
				tmp3 = ordered_dither(b,1,i,j_logical);
				row[i] = c->palette_reserve + tmp1 + tmp2*2 + tmp3*6;
			}
			else {
				tmp1 = ordered_dither(r,5,i,j_logical);
				tmp2 = ordered_dither(g,6,i,j_logical);
				tmp3 = ordered_dither(b,5,i,j_logical);
				row[i] = c->palette_reserve + tmp1 + tmp2*6 + tmp3*42;
			}
			if(c->rletrns && a<0.5) {
				row[i] = 0;
			}
		}

		// Figure out the largest possible run length for each starting pixel.
		calc_rle_run_lens(c,row,run_lens,pixels_per_row);

		rowpos = 0; // index into row[]

		while(rowpos < pixels_per_row) {

			if(c->rletrns && row[rowpos]==0) { // transparent pixel
				c->mem[curpos++] = 0;
				c->mem[curpos++] = 2;
				c->mem[curpos++] = run_lens[rowpos] + (unsigned char)(c->bad_rle?5:0) + (unsigned char)(c->bad_rle_bis?127:0); // x delta
				c->mem[curpos++] = (unsigned char)(c->bad_rle?2:0) + (unsigned char)(c->bad_rle_ter?1:0); // y delta
				rowpos += run_lens[rowpos];
				continue;
			}
			if (c->cut_rle && (rowpos > (pixels_per_row/2))) {
				if ((j & 3) == 0) {
					break;
				}
			}

			if(run_lens[rowpos]<thresh) {
				// Consider writing an uncompressed segment

				// Find next run that's 'thresh' or larger
				for(k=rowpos;k<pixels_per_row;k++) {
					if(c->rletrns && row[k]==0) {  break; } // Also have to stop at a transparent pixel
					if(run_lens[k]>=thresh) { break; }
				}
				// If there's at least 3(?) pixels before it, write an uncompressed segment.
				if(k != -1 && k-rowpos >= 3) {
					unc_len = k-rowpos;

					if(c->compression==CMPR_RLE4)
						unc_len_padded = unc_len + (3-(unc_len+3)%4);
					else
						unc_len_padded = unc_len + (unc_len%2);

					c->mem[curpos++] = 0;
					c->mem[curpos++] = unc_len;
					for(i=0;i<unc_len_padded;i++) {
						unsigned char v;
						if(i<unc_len) v=row[rowpos++];
						else v=0; // padding

						if(c->compression==CMPR_RLE4) {
							if(i%2==0)
								c->mem[curpos] = v<<4;
							else
								c->mem[curpos++] |= v;
						}
						else {
							c->mem[curpos++] = v;
						}
					}
					continue;
				}
			}

			if(rowpos>=pixels_per_row) break;

			if(c->rletrns && row[rowpos]==0) break;

			// Write a compressed segment
			c->mem[curpos++] = run_lens[rowpos] + (unsigned char)(c->bad_rle?1:0);

			if(c->compression==CMPR_RLE4) {
				c->mem[curpos] = row[rowpos]<<4;
				if(run_lens[rowpos]>=2)
					c->mem[curpos] |= row[rowpos+1];
			}
			else {
				c->mem[curpos] = row[rowpos];
			}
			curpos++;
			rowpos += run_lens[rowpos];
		}

		// Write EOL (0 0) or EOBMP (0 1) marker.
		c->mem[curpos++] = 0;
		c->mem[curpos++] = (j==c->h-1) ? 1 : 0;
	}
	if (c->cut_rle) {
		c->mem[curpos++] = 0;
		c->mem[curpos++] = 1;
	}

	free(row);
	c->bitssize = curpos - c->bitsoffset;
	c->mem_used = c->bitsoffset + c->bitssize;
	return 1;
}

static int write_bits_fromfile(struct context *c, const char *fn)
{
	int retval = 0;
	FILE *f = NULL;

	f=fopen(fn,"rb");
	if(!f) goto done;

	c->bitssize = fread(&c->mem[c->bitsoffset], 1, 100000-c->bitsoffset, f);
	if(c->bitssize<1) goto done;
	c->mem_used = c->bitsoffset + c->bitssize;

	retval = 1;
done:
	if(f) fclose(f);
	return retval;
}

static int write_bits(struct context *c)
{
	int i,j;
	double r, g, b, a;

	c->rowsize = (((c->w * c->bpp)+31)/32)*4;
	c->bitssize = c->rowsize*c->h;

	c->mem_used = c->bitsoffset + c->bitssize;

	for(j=0;j<c->h;j++) {
		for(i=0;i<c->w;i++) {
			get_pixel_color(c,i,c->halfheight ? j*2 : j, &r,&g,&b,&a);
			set_pixel(c,i,j,r,g,b,a);
		}
	}
	return 1;
}

static int write_profile(struct context *c, const char *fn)
{
	int retval = 0;
	FILE *f = NULL;

	f=fopen(fn,"rb");
	if(!f) goto done;

	// The "+20" leaves a gap of 20 unused bytes between the bits and the
	// profile, just to be difficult.
	c->profile_offset = c->bitsoffset + c->bitssize + 20;

	c->profile_size = fread(&c->mem[c->profile_offset], 1, 100000-c->profile_offset, f);
	if(c->profile_size<1) goto done;
	c->mem_used = c->profile_offset + c->profile_size;

	retval = 1;
done:
	if(f) fclose(f);
	return retval;
}

static int write_lprofile(struct context *c)
{
	static const char prof_fn[] = "C:\\temp\\test\x95\xeb.icc";

	c->profile_offset = c->bitsoffset + c->bitssize + 10;
	c->profile_size = 1+strlen(prof_fn);
	memcpy(&c->mem[c->profile_offset], prof_fn, c->profile_size);
	c->mem_used = c->profile_offset + c->profile_size;
	return 1;
}

static void write_bitfields(struct context *c)
{
	size_t offs;
	if(c->bitfieldssize!=12 && c->bitfieldssize!=16) return;
	offs = 14+c->headersize;
	set_uint32(c,offs  ,c->bf_r);
	set_uint32(c,offs+4,c->bf_g);
	set_uint32(c,offs+8,c->bf_b);
	if(c->bitfieldssize==16) {
		set_uint32(c,offs+12,c->bf_a);
	}
}

static void write_palette(struct context *c)
{
	size_t offs;
	int i, ii;
	int r,g,b;
	int bppe; // bytes per palette entry

	offs = 14+c->headersize+c->bitfieldssize;
	bppe = (c->headersize<=12) ? 3 : 4;

	if(c->bpp==8) {
		if(c->palette_reserve) {
			c->mem[offs+2] = 128;
			c->mem[offs+1] = 0;
			c->mem[offs+0] = 255;
		}

		if(c->pal_gs) {
			// Grayscale palette
			for(i=c->palette_reserve;i<c->pal_entries;i++) {
				ii = i-c->palette_reserve;
				if(i>=252+c->palette_reserve) continue;

				c->mem[offs+bppe*i+2] = scale_to_int(ii/(double)(c->pal_entries - c->palette_reserve - 1), 255);
				c->mem[offs+bppe*i+1] = scale_to_int(ii/(double)(c->pal_entries - c->palette_reserve - 1), 255);
				c->mem[offs+bppe*i+0] = scale_to_int(ii/(double)(c->pal_entries - c->palette_reserve - 1), 255);
			}
		}
		else {
			// R6G7B6 palette
			// Entry for a given (R,G,B) is R + G*6 + B*42
			for(i=c->palette_reserve;i<c->pal_entries;i++) {
				ii = i-c->palette_reserve;
				if(i>=252+c->palette_reserve) continue;
				r = ii%6;
				g = (ii%42)/6;
				b = ii/42;
				c->mem[offs+bppe*i+2] = scale_to_int( ((double)r)/5.0, 255);
				c->mem[offs+bppe*i+1] = scale_to_int( ((double)g)/6.0, 255);
				c->mem[offs+bppe*i+0] = scale_to_int( ((double)b)/5.0, 255);
			}
		}
	}
	else if(c->bpp==4) {
		if(c->palette_reserve) {
			c->mem[offs+2] = 128;
			c->mem[offs+1] = 0;
			c->mem[offs+0] = 255;
		}

		if(c->pal_gs) {
			// Grayscale palette
			for(i=c->palette_reserve;i<c->pal_entries;i++) {
				ii = i-c->palette_reserve;
				c->mem[offs+bppe*i+2] = scale_to_int(ii/(double)(c->pal_entries - c->palette_reserve - 1), 255);
				c->mem[offs+bppe*i+1] = scale_to_int(ii/(double)(c->pal_entries - c->palette_reserve - 1), 255);
				c->mem[offs+bppe*i+0] = scale_to_int(ii/(double)(c->pal_entries - c->palette_reserve - 1), 255);
			}
		}
		else {
			for(i=c->palette_reserve;i<c->pal_entries;i++) {
				ii = i-c->palette_reserve;
				r = ii%2;
				g = (ii%6)/2;
				b = ii/6;
				c->mem[offs+4*i+2] = scale_to_int( ((double)r)/1.0, 255);
				c->mem[offs+4*i+1] = scale_to_int( ((double)g)/2.0, 255);
				c->mem[offs+4*i+0] = scale_to_int( ((double)b)/1.0, 255);
			}
		}
	}
	else if(c->bpp==2) {
		if(c->pal_gs) {
			for(i=0;i<4;i++) {
				// A 4-shade grayscale palette
				c->mem[offs+4*i+2] = 85*i;
				c->mem[offs+4*i+1] = 85*i;
				c->mem[offs+4*i+0] = 85*i;
			}
		}
		else {
			for(i=0;i<4;i++) {
				r = i%2;
				g = (i == 3) ? 1 : 0;
				b = i/2;
				c->mem[offs+4*i+2] = scale_to_int( ((double)r)/1.0, 255);
				c->mem[offs+4*i+1] = scale_to_int( ((double)g)/1.0, 255);
				c->mem[offs+4*i+0] = scale_to_int( ((double)b)/1.0, 255);
			}
		}
	}
	else if(c->bpp==1) {
		if(c->pal_entries==2) {
			if(c->pal_wb) {
				c->mem[offs+4*0+2] = 255;
				c->mem[offs+4*0+1] = 255;
				c->mem[offs+4*0+0] = 255;
			}
			else if(c->pal_bg) {
				c->mem[offs+4*0+2] = 64;
				c->mem[offs+4*0+1] = 64;
				c->mem[offs+4*0+0] = 255;
				c->mem[offs+4*1+2] = 64;
				c->mem[offs+4*1+1] = 255;
				c->mem[offs+4*1+0] = 64;
			}
			else {
				c->mem[offs+4*1+2] = 255;
				c->mem[offs+4*1+1] = 255;
				c->mem[offs+4*1+0] = 255;
			}
		}
		else { // assuming c->pal_p1
			c->mem[offs+4*0+2] = 64;
			c->mem[offs+4*0+1] = 64;
			c->mem[offs+4*0+0] = 255;
		}
	}
	else if(c->bpp>8) {
		// Write a 'suggested' palette.
		for(i=0;i<c->pal_entries;i++) {
			if(i<=255) {
				c->mem[offs+4*i+2] = (unsigned char)i;
				c->mem[offs+4*i+1] = (unsigned char)i;
				c->mem[offs+4*i+0] = (unsigned char)i;
			}
		}

	}
}

static void write_fileheader(struct context *c)
{
	c->mem[0]='B';
	c->mem[1]='M';
	if(c->bad_bfSize)
		set_int32(c,2,0x7ddddddd);
	else if(c->cbsize_flag)
		set_int32(c,2,14+c->headersize);
	else
		set_int32(c,2,(int)c->mem_used);
	set_int32(c,10,c->bitsoffset);
}

static void write_bitmapcoreheader(struct context *c)
{
	set_int32(c,14+0,c->headersize);
	set_int16(c,14+4,c->w);
	set_int16(c,14+6,c->h);
	set_int16(c,14+8,1); // planes
	set_int16(c,14+10,c->bpp);
}

static void write_bitmapinfoheader(struct context *c)
{
	double gamma;

	set_int32(c,14+0,c->headersize);
	set_int32(c,14+4,(c->bad_width) ? -c->w : c->w); // biWidth
	set_int32(c,14+8,(c->topdown) ? -c->h : c->h);
	if(c->bad_reallybig) {
		set_int32(c,14+4,3000000);
		set_int32(c,14+8,2000000);
	}
	set_int16(c,14+12,(c->bad_planes) ? 30000 : 1); // biPlanes
	set_int16(c,14+14,(c->bad_bitcount) ? 30000 : c->bpp); // biBitCount

	if(c->headersize>=40) {
		set_int32(c,14+16,c->compression);
		if(c->zero_biSizeImage)
			set_int32(c,14+20,0);
		else if(c->bad_biSizeImage)
			set_int32(c,14+20,0x7eeeeeee);
		else
			set_int32(c,14+20,c->bitssize);

		set_int32(c,14+24,c->xpelspermeter);
		set_int32(c,14+28,c->ypelspermeter);
		set_int32(c,14+32,(c->bad_palettesize) ? 0x12341234 : c->clr_used); // biClrUsed
		set_int32(c,14+36,0); // biClrImportant
	}
	if(c->headersize>=52) {
		if(c->compression==3) {
			set_uint32(c,14+40,c->bf_r);
			set_uint32(c,14+44,c->bf_g);
			set_uint32(c,14+48,c->bf_b);
		}
	}
	if(c->headersize>=56) {
		if(c->compression==3) {
			set_uint32(c,14+52,c->bf_a);
		}
	}
	if(c->headersize>=108) {

		if(c->headersize==108) {
			// Modern documentation lists LCS_CALIBRATED_RGB as the only legal
			// CSType for v4 bitmaps.
			set_uint32(c,14+56,0); // CSType = LCS_CALIBRATED_RGB

			// Chromaticity endpoints.
			// I don't know much about what should go here. These are the
			// chromaticities for sRGB.
			// These values are in 2.30 fixed-point format.
			set_uint32(c,14+60, (unsigned int)(0.5+0.6400*1073741824.0)); // red-x
			set_uint32(c,14+64, (unsigned int)(0.5+0.3300*1073741824.0)); // red-y
			set_uint32(c,14+68, (unsigned int)(0.5+0.0300*1073741824.0)); // red-z
			set_uint32(c,14+72, (unsigned int)(0.5+0.3000*1073741824.0)); // green-x
			set_uint32(c,14+76, (unsigned int)(0.5+0.6000*1073741824.0)); // green-y
			set_uint32(c,14+80, (unsigned int)(0.5+0.1000*1073741824.0)); // green-z
			set_uint32(c,14+84, (unsigned int)(0.5+0.1500*1073741824.0)); // blue-x
			set_uint32(c,14+88, (unsigned int)(0.5+0.0600*1073741824.0)); // blue-y
			set_uint32(c,14+92, (unsigned int)(0.5+0.7900*1073741824.0)); // blue-z

			// I'm not sure if this is supposed to be the "image file gamma", like
			// 1.0/2.2, or the "display gamma", like 2.2. "Image file gamma" is my
			// best guess.
			gamma = 1.0/2.2;
			// These values are in 16.16 fixed-point format.
			set_uint32(c,14+ 96, (unsigned int)(0.5+gamma*65536.0)); // bV4GammaRed
			set_uint32(c,14+100, (unsigned int)(0.5+gamma*65536.0)); // bV4GammaGreen
			set_uint32(c,14+104, (unsigned int)(0.5+gamma*65536.0)); // bV4GammaBlue
		}
		else {
			if(c->embed_profile) {
				set_uint32(c,14+56,0x4d424544); // CSType = PROFILE_EMBEDDED
			}
			else if(c->link_profile) {
				set_uint32(c,14+56,0x4c494e4b); // CSType = PROFILE_LINKED
			}
			else {
				set_uint32(c,14+56,0x73524742); // CSType = sRGB
			}
		}
	}
	if(c->headersize>=124) {
		set_uint32(c,14+108,4); // Rendering intent = Perceptual
		if(c->embed_profile || c->link_profile) {
			set_uint32(c,14+112,c->profile_offset-14);
			set_uint32(c,14+116,c->profile_size);
		}
	}
}

static int make_bmp(struct context *c)
{
	int ret;

	write_bitfields(c);
	write_palette(c);
	if(c->compression==CMPR_RLE4 || c->compression==CMPR_RLE8)
		ret = write_bits_rle(c);
	else if(c->compression==CMPR_JPEG && c->headersize>40)
		ret = write_bits_fromfile(c,"data/image.jpg");
	else if(c->compression==CMPR_PNG)
		ret = write_bits_fromfile(c,"data/image.png");
	else
		ret = write_bits(c);
	if(!ret) {
		fprintf(stderr,"Failed to generate image for %s\n",c->filename);
		return 0;
	}
	if(c->embed_profile) {
		write_profile(c,"data/srgb.icc");
	}
	else if(c->link_profile) {
		write_lprofile(c);
	}
	if(c->headersize<=12)
		write_bitmapcoreheader(c);
	else
		write_bitmapinfoheader(c);
	write_fileheader(c);
	return 1;
}

static int write_image_file(struct context *c)
{
	FILE *f;

	fprintf(stderr,"Writing %s\n",c->filename);
	f = fopen(c->filename,"wb");
	if(!f) {
		fprintf(stderr,"Can't write %s\n",c->filename);
		return 0;
	}
	if(c->bad_eof) {
		fwrite(c->mem,1,273,f);
	}
	else {
		fwrite(c->mem,1,c->mem_used,f);
	}
	fclose(f);
	return 1;
}

static int make_bmp_file(struct context *c)
{
	if(!make_bmp(c)) return 0;
	if(!write_image_file(c)) return 0;
	return 1;
}

static void set_calculated_fields(struct context *c)
{

	if(c->bpp==8 && c->pal_entries!=256) {
		c->clr_used = c->pal_entries;
	}
	else if(c->bpp==4 && c->pal_entries!=16) {
		c->clr_used = c->pal_entries;
	}
	else if(c->bpp==1 && c->pal_entries!=2) {
		c->clr_used = c->pal_entries;
	}
	else {
		c->clr_used = c->pal_entries;
	}

	if(c->headersize<=12) {
		c->clr_used = c->pal_entries;
		c->palettesize = c->pal_entries*3;
	}
	else {
		c->palettesize = c->pal_entries*4;
	}

	c->bitsoffset = 14 + c->headersize + c->bitfieldssize + c->palettesize + c->extrabytessize;
}

static void defaultbmp(struct context *c)
{
	memset(c->mem,0,BMP_MAX_SIZE);
	c->mem_used = 0;
#ifdef SMALL_IMAGES
	c->w = 31;
	c->h = 32;
#else
	c->w = 127;
	c->h = 64;
#endif
	c->bpp = 8;
	c->pal_entries = 252;
	c->clr_used = 0;
	c->headersize = 40;
	c->bitssize = 0;
	c->bitfieldssize = 0;
	c->rowsize = 0;
	c->filename = "noname.bmp";
	c->compression = 0; // BI_RGB
	c->xpelspermeter = 2835; // = about 72dpi
	c->ypelspermeter = 2835;
	c->pal_gs = 0;
	c->pal_wb = 0;
	c->pal_bg = 0;
	c->pal_p1 = 0;
	c->dither = 0;
	c->topdown = 0;
	c->embed_profile = 0;
	c->link_profile = 0;
	c->profile_offset = 0;
	c->profile_size = 0;
	c->alphahack32 = 0;
	c->halfheight = 0;
	c->zero_biSizeImage = 0;
	c->bad_biSizeImage = 0;
	c->bad_bfSize = 0;
	c->bad_width = 0;
	c->bad_reallybig = 0;
	c->bad_bitcount = 0;
	c->bad_planes = 0;
	c->bad_palettesize = 0;
	c->bad_rle = 0;
	c->bad_rle_bis = 0;
	c->bad_rle_ter = 0;
	c->cut_rle = 0;
	c->bad_eof = 0;
	c->extrabytessize = 0;
	c->palette_reserve = 0;
	c->rletrns = 0;
	c->bf_r = c->bf_g = c->bf_b = c->bf_a = 0;
	c->nbits_r = c->nbits_g = c->nbits_b = c->nbits_a = 0;
	c->bf_shift_r = c->bf_shift_g = c->bf_shift_b = c->bf_shift_a = 0;
	c->cbsize_flag = 0;
	set_calculated_fields(c);
}

static int run(struct context *c)
{
	int retval = 0;

	mkdir("g",0755);
	mkdir("q",0755);
	mkdir("b",0755);

	defaultbmp(c);
	c->filename = "g/pal8.bmp";
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal8gs.bmp";
	c->pal_gs = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal8os2.bmp";
	c->headersize = 12;
	c->pal_entries = 256;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal8os2-sz.bmp";
	c->headersize = 12;
	c->pal_entries = 256;
	c->cbsize_flag = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal8os2sp.bmp";
	c->headersize = 12;
	c->pal_entries = 252;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal8os2v2-16.bmp";
	c->headersize = 16;
	c->pal_entries = 256;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal8os2v2.bmp";
	c->headersize = 64;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal8os2v2-40sz.bmp";
	c->headersize = 40;
	c->cbsize_flag = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal8os2v2-sz.bmp";
	c->headersize = 64;
	c->cbsize_flag = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal8v4.bmp";
	c->headersize = 108;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal8v5.bmp";
	c->headersize = 124;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal8w124.bmp";
#ifdef SMALL_IMAGES
	c->w = 28; c->h = 29;
#else
	c->w = 124; c->h = 61;
#endif
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal8w125.bmp";
#ifdef SMALL_IMAGES
	c->w = 29; c->h = 30;
#else
	c->w = 125; c->h = 62;
#endif
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal8w126.bmp";
#ifdef SMALL_IMAGES
	c->w = 30; c->h = 31;
#else
	c->w = 126; c->h = 63;
#endif
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal8-0.bmp";
	c->pal_entries = 256;
	c->xpelspermeter = 0;
	c->ypelspermeter = 0;
	c->zero_biSizeImage = 1;
	set_calculated_fields(c);
	c->clr_used = 0;
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal8offs.bmp";
	c->extrabytessize = 100;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal8oversizepal.bmp";
	c->pal_entries = 300;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/pal8badindex.bmp";
	c->pal_entries = 101;
	c->palette_reserve = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badpalettesize.bmp";
	c->bad_palettesize = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal8topdown.bmp";
	c->topdown = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal8nonsquare.bmp";
	c->halfheight = 1;
	c->ypelspermeter = 1417;
#ifdef SMALL_IMAGES
	c->h = 16;
#else
	c->h = 32;
#endif
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal8rle.bmp";
	c->compression = CMPR_RLE8;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal8rlecut.bmp";
	c->compression = CMPR_RLE8;
	c->rletrns = 1;
	c->pal_entries = 253;
	c->palette_reserve = 1;
	c->cut_rle = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badrle.bmp";
	c->compression = CMPR_RLE8;
	c->rletrns = 1;
	c->pal_entries = 253;
	c->palette_reserve = 1;
	c->bad_rle = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badrlebis.bmp";
	c->compression = CMPR_RLE8;
	c->rletrns = 1;
	c->pal_entries = 253;
	c->palette_reserve = 1;
	c->bad_rle_bis = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badrleter.bmp";
	c->compression = CMPR_RLE8;
	c->rletrns = 1;
	c->pal_entries = 253;
	c->palette_reserve = 1;
	c->bad_rle_bis = 1;
	c->bad_rle_ter = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badrle4.bmp";
	c->bpp = 4;
	c->compression = CMPR_RLE4;
	c->rletrns = 1;
	c->pal_entries = 13;
	c->palette_reserve = 1;
	c->bad_rle = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badrle4bis.bmp";
	c->bpp = 4;
	c->compression = CMPR_RLE4;
	c->rletrns = 1;
	c->pal_entries = 13;
	c->palette_reserve = 1;
	c->bad_rle_bis = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badrle4ter.bmp";
	c->bpp = 4;
	c->compression = CMPR_RLE4;
	c->rletrns = 1;
	c->pal_entries = 13;
	c->palette_reserve = 1;
	c->bad_rle_bis = 1;
	c->bad_rle_ter = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/rletopdown.bmp";
	c->topdown = 1;
	c->compression = CMPR_RLE8;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal8rletrns.bmp";
	c->compression = CMPR_RLE8;
	c->rletrns = 1;
	c->pal_entries = 253;
	c->palette_reserve = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal4.bmp";
	c->bpp = 4;
	c->pal_entries = 12;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal4gs.bmp";
	c->bpp = 4;
	c->pal_entries = 12;
	c->pal_gs = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal4rle.bmp";
	c->bpp = 4;
	c->compression = CMPR_RLE4;
	c->pal_entries = 12;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal4rlecut.bmp";
	c->bpp = 4;
	c->compression = CMPR_RLE4;
	c->rletrns = 1;
	c->pal_entries = 13;
	c->palette_reserve = 1;
	c->cut_rle = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal4rletrns.bmp";
	c->bpp = 4;
	c->compression = CMPR_RLE4;
	c->rletrns = 1;
	c->pal_entries = 13;
	c->palette_reserve = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal1.bmp";
	c->bpp = 1;
	c->pal_entries = 2;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal1wb.bmp";
	c->bpp = 1;
	c->pal_entries = 2;
	c->pal_wb = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/pal1bg.bmp";
	c->bpp = 1;
	c->pal_entries = 2;
	c->pal_bg = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal1p1.bmp";
	c->bpp = 1;
	c->pal_entries = 1;
	c->pal_p1 = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badbitssize.bmp";
	c->bpp = 1;
	c->pal_entries = 2;
	c->bad_biSizeImage = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badfilesize.bmp";
	c->bpp = 1;
	c->pal_entries = 2;
	c->bad_bfSize = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/shortfile.bmp";
	c->bpp = 1;
	c->pal_entries = 2;
	c->bad_eof = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badwidth.bmp";
	c->bpp = 1;
	c->pal_entries = 2;
	c->bad_width = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/reallybig.bmp";
	c->bpp = 24;
	c->pal_entries = 0;
	c->bad_reallybig = 1;
	c->bad_biSizeImage = 1;
	c->bad_bfSize = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badbitcount.bmp";
	c->bpp = 1;
	c->pal_entries = 2;
	c->bad_bitcount = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badplanes.bmp";
	c->bpp = 1;
	c->pal_entries = 2;
	c->bad_planes = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/badheadersize.bmp";
	c->bpp = 1;
	c->pal_entries = 2;
	c->headersize = 66;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/baddens1.bmp";
	c->bpp = 1;
	c->pal_entries = 2;
	c->xpelspermeter = 30000000;
	c->ypelspermeter = 3;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/baddens2.bmp";
	c->bpp = 1;
	c->pal_entries = 2;
	c->xpelspermeter = 3;
	c->ypelspermeter = 30000000;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal2.bmp";
	c->bpp = 2;
	c->pal_entries = 4;
	c->pal_gs = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/pal2color.bmp";
	c->bpp = 2;
	c->pal_entries = 4;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/rgb16.bmp";
	c->bpp = 16;
	c->nbits_r = c->nbits_g = c->nbits_b = 5;
	c->pal_entries = 0;
	c->nbits_r = 5; c->bf_shift_r = 10;
	c->nbits_g = 5; c->bf_shift_g = 5;
	c->nbits_b = 5; c->bf_shift_b = 0;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/rgb16-565.bmp";
	c->bpp = 16;
	c->pal_entries = 0;
	c->compression = BI_BITFIELDS;
	c->bf_r = 0x0000f800; c->nbits_r = 5; c->bf_shift_r = 11;
	c->bf_g = 0x000007e0; c->nbits_g = 6; c->bf_shift_g = 5;
	c->bf_b = 0x0000001f; c->nbits_b = 5; c->bf_shift_b = 0;
	c->bitfieldssize = 12;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "b/rgb16-880.bmp";
	c->bpp = 16;
	c->pal_entries = 0;
	c->compression = BI_BITFIELDS;
	c->bf_r = 0x0000ff00; c->nbits_r = 8; c->bf_shift_r = 8;
	c->bf_g = 0x000000ff; c->nbits_g = 8; c->bf_shift_g = 0;
	c->bf_b = 0x00000000; c->nbits_b = 0; c->bf_shift_b = 0;
	c->bitfieldssize = 12;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/rgb16-565pal.bmp";
	c->bpp = 16;
	c->pal_entries = 256;
	c->compression = BI_BITFIELDS;
	c->bf_r = 0x0000f800; c->nbits_r = 5; c->bf_shift_r = 11;
	c->bf_g = 0x000007e0; c->nbits_g = 6; c->bf_shift_g = 5;
	c->bf_b = 0x0000001f; c->nbits_b = 5; c->bf_shift_b = 0;
	c->bitfieldssize = 12;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgba16-4444.bmp";
	c->headersize = 124;
	c->bpp = 16;
	c->pal_entries = 0;
	c->compression = BI_BITFIELDS;
	c->bf_r = 0x00000f00; c->nbits_r = 4; c->bf_shift_r = 8;
	c->bf_g = 0x000000f0; c->nbits_g = 4; c->bf_shift_g = 4;
	c->bf_b = 0x0000000f; c->nbits_b = 4; c->bf_shift_b = 0;
	c->bf_a = 0x0000f000; c->nbits_a = 4; c->bf_shift_a = 12;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgba16-1924.bmp";
	c->headersize = 124;
	c->bpp = 16;
	c->pal_entries = 0;
	c->compression = BI_BITFIELDS;
	c->bf_r = 0x00000800; c->nbits_r =  1; c->bf_shift_r = 11;
	c->bf_g = 0x000001ff; c->nbits_g =  9; c->bf_shift_g =  0;
	c->bf_b = 0x00000600; c->nbits_b =  2; c->bf_shift_b =  9;
	c->bf_a = 0x0000f000; c->nbits_a =  4; c->bf_shift_a = 12;
	c->dither = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgb16-231.bmp";
	c->bpp = 16;
	c->pal_entries = 0;
	c->compression = BI_BITFIELDS;
	c->bf_r = 0x00000030; c->nbits_r = 2; c->bf_shift_r = 4;
	c->bf_g = 0x0000000e; c->nbits_g = 3; c->bf_shift_g = 1;
	c->bf_b = 0x00000001; c->nbits_b = 1; c->bf_shift_b = 0;
	c->bitfieldssize = 12;
	c->dither = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgb16-3103.bmp";
	c->bpp = 16;
	c->pal_entries = 0;
	c->compression = BI_BITFIELDS;
	c->bf_r = 0x00000038; c->nbits_r =  3; c->bf_shift_r = 3;
	c->bf_g = 0x0000ffc0; c->nbits_g = 10; c->bf_shift_g = 6;
	c->bf_b = 0x00000007; c->nbits_b =  3; c->bf_shift_b = 0;
	c->bitfieldssize = 12;
	c->dither = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/rgb24.bmp";
	c->bpp = 24;
	c->pal_entries = 0;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgb24prof.bmp";
	c->headersize = 124;
	c->bpp = 24;
	c->pal_entries = 0;
	c->embed_profile = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgb24lprof.bmp";
	c->headersize = 124;
	c->bpp = 24;
	c->pal_entries = 0;
	c->link_profile = 1;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/rgb24pal.bmp";
	c->bpp = 24;
	c->pal_entries = 256;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgb24largepal.bmp";
	c->bpp = 24;
	c->pal_entries = 300;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/rgb32.bmp";
	c->bpp = 32;
	c->pal_entries = 0;
	c->nbits_r = 8; c->bf_shift_r = 16;
	c->nbits_g = 8; c->bf_shift_g = 8;
	c->nbits_b = 8; c->bf_shift_b = 0;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "g/rgb32bf.bmp";
	c->bpp = 32;
	c->compression = BI_BITFIELDS;
	c->pal_entries = 0;
	c->bf_r = 0xff000000; c->nbits_r = 8; c->bf_shift_r = 24;
	c->bf_g = 0x00000ff0; c->nbits_g = 8; c->bf_shift_g = 4;
	c->bf_b = 0x00ff0000; c->nbits_b = 8; c->bf_shift_b = 16;
	c->bitfieldssize = 12;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgb32fakealpha.bmp";
	c->bpp = 32;
	c->pal_entries = 0;
	c->alphahack32 = 1;
	c->nbits_r = 8; c->bf_shift_r = 16;
	c->nbits_g = 8; c->bf_shift_g = 8;
	c->nbits_b = 8; c->bf_shift_b = 0;
	c->nbits_a = 8;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgb32-111110.bmp";
	c->bpp = 32;
	c->compression = BI_BITFIELDS;
	c->pal_entries = 0;
	c->bf_r = 0xffe00000; c->nbits_r = 11; c->bf_shift_r = 21;
	c->bf_g = 0x001ffc00; c->nbits_g = 11; c->bf_shift_g = 10;
	c->bf_b = 0x000003ff; c->nbits_b = 10; c->bf_shift_b = 0;
	c->bitfieldssize = 12;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgb32-7187.bmp";
	c->bpp = 32;
	c->compression = BI_BITFIELDS;
	c->pal_entries = 0;
	c->bf_r = 0xfe000000; c->nbits_r = 7; c->bf_shift_r = 25;
	c->bf_g = 0x01ffff80; c->nbits_g = 18; c->bf_shift_g = 7;
	c->bf_b = 0x0000007f; c->nbits_b = 7; c->bf_shift_b = 0;
	c->bitfieldssize = 12;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgba32.bmp";
	c->headersize = 124;
	c->bpp = 32;
	c->compression = BI_BITFIELDS;
	c->bf_r = 0xff000000; c->nbits_r = 8; c->bf_shift_r = 24;
	c->bf_g = 0x0000ff00; c->nbits_g = 8; c->bf_shift_g = 8;
	c->bf_b = 0x000000ff; c->nbits_b = 8; c->bf_shift_b = 0;
	c->bf_a = 0x00ff0000; c->nbits_a = 8; c->bf_shift_a = 16;
	c->pal_entries = 0;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgba32-81284.bmp";
	c->headersize = 124;
	c->bpp = 32;
	c->compression = BI_BITFIELDS;
	c->bf_r = 0x0000ff00; c->nbits_r =  8; c->bf_shift_r =  8;
	c->bf_g = 0x0fff0000; c->nbits_g = 12; c->bf_shift_g = 16;
	c->bf_b = 0x000000ff; c->nbits_b =  8; c->bf_shift_b =  0;
	c->bf_a = 0xf0000000; c->nbits_a =  4; c->bf_shift_a = 28;
	c->pal_entries = 0;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgba32-61754.bmp";
	c->headersize = 124;
	c->bpp = 32;
	c->compression = BI_BITFIELDS;
	c->bf_r = 0x0fc00000; c->nbits_r =  6; c->bf_shift_r = 22;
	c->bf_g = 0x003fffe0; c->nbits_g = 17; c->bf_shift_g =  5;
	c->bf_b = 0x0000001f; c->nbits_b =  5; c->bf_shift_b =  0;
	c->bf_a = 0xf0000000; c->nbits_a =  4; c->bf_shift_a = 28;
	c->pal_entries = 0;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgb32h52.bmp";
	c->headersize = 52;
	c->bpp = 32;
	c->compression = BI_BITFIELDS;
	c->bf_r = 0xff000000; c->nbits_r = 8; c->bf_shift_r = 24;
	c->bf_g = 0x0000ff00; c->nbits_g = 8; c->bf_shift_g = 8;
	c->bf_b = 0x000000ff; c->nbits_b = 8; c->bf_shift_b = 0;
	c->pal_entries = 0;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgba32h56.bmp";
	c->headersize = 56;
	c->bpp = 32;
	c->compression = BI_BITFIELDS;
	c->bf_r = 0xff000000; c->nbits_r = 8; c->bf_shift_r = 24;
	c->bf_g = 0x0000ff00; c->nbits_g = 8; c->bf_shift_g = 8;
	c->bf_b = 0x000000ff; c->nbits_b = 8; c->bf_shift_b = 0;
	c->bf_a = 0x00ff0000; c->nbits_a = 8; c->bf_shift_a = 16;
	c->pal_entries = 0;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgba32abf.bmp";
	c->bpp = 32;
	c->compression = BI_ALPHABITFIELDS;
	c->bf_r = 0xff000000; c->nbits_r = 8; c->bf_shift_r = 24;
	c->bf_g = 0x0000ff00; c->nbits_g = 8; c->bf_shift_g = 8;
	c->bf_b = 0x000000ff; c->nbits_b = 8; c->bf_shift_b = 0;
	c->bf_a = 0x00ff0000; c->nbits_a = 8; c->bf_shift_a = 16;
	c->bitfieldssize = 16;
	c->pal_entries = 0;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgb24jpeg.bmp";
	c->headersize = 124;
	c->bpp = 0;
	c->compression = CMPR_JPEG;
	c->pal_entries = 0;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	defaultbmp(c);
	c->filename = "q/rgb24png.bmp";
	c->headersize = 124;
	c->bpp = 0;
	c->compression = CMPR_PNG;
	c->pal_entries = 0;
	set_calculated_fields(c);
	if(!make_bmp_file(c)) goto done;

	retval = 1;
done:
	return retval;
}

int main(int argc, char **argv)
{
	struct context c;
	int ret;

	memset(&c,0,sizeof(struct context));
	c.mem = malloc(BMP_MAX_SIZE);
	ret = run(&c);

	return ret ? 0 : 1;
}
