/************************************************************************\
* Portable pixmap to C64 Flickering FLI converter 8-Jun-92               *
* By Pasi 'Albert' Ojala © 1991-1998                                     *
*    albert@cs.tut.fi  albert@cc.tut.fi                                  *
*                                                                        *
* 26-Sep-92  Changed the palette a bit                                   *
* 05-Sep-94  Redid the palette, new color selection, 'pure' ANSI-C       *
* 17-Sep-94  Changed the format a bit, load address $3bff/$3aff+'f'      *
*                                                                        *
\************************************************************************/

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

#define UBYTE unsigned char
#define MAXINT 0x7FFFFFFF

char *vers = "\0$VER: ppmtoFFLI 1.3 (30.7.1998)\n";

UBYTE pic[200][160];    /* here we store the C64 flickering color values (64) */

#define Y(r,g,b) ((30*r+59*g+11*b)/100)
#define U(y,b)   (3*493*(b-y)/4096+128)
#define V(y,r)   (3*877*(r-y)/4096+128)

#define ERROR(y,y1,u,u1,v,v1,f) (abs(y-y1)+3*abs(u-u1)+3*abs(v-v1)+f/4)

#define COLORS    136


const UBYTE flashcol[COLORS+1][9] = {
/* col0 col1   R   G   B     Y   U   V  flash */
    { 0,  0,    0,  0,  0,    0,128,128,    0},
    { 0,  1,  120,120,120,  120,128,128,  240},
    { 1,  1,  240,240,240,  240,128,128,    0},
    { 0,  2,  120,  0,  0,   36,116,181,   72},
    { 1,  2,  240,120,120,  156,116,181,  168},
    { 2,  2,  240,  0,  0,   72,103,235,    0},
    { 0,  3,    0,120,120,   84,140, 75,  168},
    { 1,  3,  120,240,240,  204,140, 75,   72},
    { 2,  3,  120,120,120,  120,128,128,   96},
    { 3,  3,    0,240,240,  168,153, 21,    0},
    { 0,  4,  120,  0,120,   49,153,173,   98},
    { 1,  4,  240,120,240,  169,153,173,  142},
    { 2,  4,  240,  0,120,   85,140,227,   26},
    { 3,  4,  120,120,240,  133,166,120,   70},
    { 4,  4,  240,  0,240,   98,179,219,    0},
    { 0,  5,    0,120,  0,   70,103, 84,  141},
    { 1,  5,  120,240,120,  190,103, 84,   99},
    { 2,  5,  120,120,  0,  106, 90,136,   69},
    { 3,  5,    0,240,120,  154,116, 30,   27},
    { 4,  5,  120,120,120,  120,128,128,   43},
    { 5,  5,    0,240,  0,  141, 78, 38,    0},
    { 0,  6,    0,  0,120,   13,166,120,   26},
    { 1,  6,  120,120,240,  133,166,120,  214},
    { 2,  6,  120,  0,120,   49,153,173,   46},
    { 3,  6,    0,120,240,   97,179, 66,  142},
    { 4,  6,  120,  0,240,   62,192,165,   72},
    { 5,  6,    0,120,120,   84,140, 75,  115},
    { 6,  6,    0,  0,240,   26,205,112,    0},
    { 0,  7,  120,120,  0,  106, 90,136,  213},
    { 1,  7,  240,240,120,  226, 90,136,   27},
    { 2,  7,  240,120,  0,  142, 77,190,  141},
    { 3,  7,  120,240,120,  190,103, 84,   45},
    { 4,  7,  240,120,120,  156,116,181,  115},
    { 5,  7,  120,240,  0,  177, 65, 92,   72},
    { 6,  7,  120,120,120,  120,128,128,  187},
    { 7,  7,  240,240,  0,  213, 52,145,    0},
    { 0,  8,  120, 48,  0,   64,105,163,  128},
    { 1,  8,  240,168,120,  184,105,163,  112},
    { 2,  8,  240, 48,  0,  100, 92,217,   56},
    { 3,  8,  120,168,120,  148,118,111,   40},
    { 4,  8,  240, 48,120,  113,130,209,   30},
    { 5,  8,  120,168,  0,  135, 80,119,   13},
    { 6,  8,  120, 48,120,   77,143,155,  102},
    { 7,  8,  240,168,  0,  171, 67,172,   85},
    { 8,  8,  240, 96,  0,  128, 82,199,    0},
    { 0,  9,   80, 32,  0,   42,113,152,   85},
    { 1,  9,  200,152,120,  162,113,152,  155},
    { 2,  9,  200, 32,  0,   78,100,206,   13},
    { 3,  9,   80,152,120,  126,126, 99,   83},
    { 4,  9,  200, 32,120,   92,138,197,   13},
    { 5,  9,   80,152,  0,  113, 88,107,   56},
    { 6,  9,   80, 32,120,   56,151,143,   59},
    { 7,  9,  200,152,  0,  149, 75,160,  128},
    { 8,  9,  200, 80,  0,  107, 90,187,   43},
    { 9,  9,  160, 64,  0,   85, 98,176,    0},
    { 0, 10,  120, 56, 56,   75,122,156,  150},
    { 1, 10,  240,176,176,  195,122,156,   90},
    { 2, 10,  240, 56, 56,  111,109,210,   78},
    { 3, 10,  120,176,176,  159,134,103,   18},
    { 4, 10,  240, 56,176,  124,146,202,   52},
    { 5, 10,  120,176, 56,  146, 96,112,    9},
    { 6, 10,  120, 56,176,   88,159,148,  124},
    { 7, 10,  240,176, 56,  182, 83,165,   63},
    { 8, 10,  240,104, 56,  139, 99,192,   22},
    { 9, 10,  200, 88, 56,  118,106,180,   65},
    {10, 10,  240,112,112,  150,115,185,    0},
    { 0, 11,   40, 40, 40,   40,128,128,   80},
    { 1, 11,  160,160,160,  160,128,128,  160},
    { 2, 11,  160, 40, 40,   76,116,181,    8},
    { 3, 11,   40,160,160,  124,140, 75,   88},
    { 4, 11,  160, 40,160,   89,153,173,   18},
    { 5, 11,   40,160, 40,  110,103, 84,   61},
    { 6, 11,   40, 40,160,   53,166,120,   54},
    { 7, 11,  160,160, 40,  146, 90,136,  133},
    { 8, 11,  160, 88, 40,  104,105,163,   48},
    { 9, 11,  120, 72, 40,   82,113,152,    5},
    {10, 11,  160, 96, 96,  115,122,156,   70},
    {11, 11,   80, 80, 80,   80,128,128,    0},
    { 0, 12,   64, 64, 64,   64,128,128,  128},
    { 1, 12,  184,184,184,  184,128,128,  112},
    { 2, 12,  184, 64, 64,  100,116,181,   56},
    { 3, 12,   64,184,184,  148,140, 75,   40},
    { 4, 12,  184, 64,184,  113,153,173,   30},
    { 5, 12,   64,184, 64,  134,103, 84,   13},
    { 6, 12,   64, 64,184,   77,166,120,  102},
    { 7, 12,  184,184, 64,  170, 90,136,   85},
    { 8, 12,  184,112, 64,  128,105,163,    0},
    { 9, 12,  144, 96, 64,  106,113,152,   43},
    {10, 12,  184,120,120,  139,122,156,   22},
    {11, 12,  104,104,104,  104,128,128,   48},
    {12, 12,  128,128,128,  128,128,128,    0},
    { 0, 13,   72,120, 72,  100,118,111,  200},
    { 1, 13,  192,240,192,  220,118,111,   40},
    { 2, 13,  192,120, 72,  136,105,163,  128},
    { 3, 13,   72,240,192,  184,130, 57,   32},
    { 4, 13,  192,120,192,  149,143,155,  102},
    { 5, 13,   72,240, 72,  171, 93, 65,   59},
    { 6, 13,   72,120,192,  113,156,102,  174},
    { 7, 13,  192,240, 72,  207, 80,119,   13},
    { 8, 13,  192,168, 72,  164, 95,145,   72},
    { 9, 13,  152,152, 72,  143,103,133,  115},
    {10, 13,  192,176,128,  175,112,138,   50},
    {11, 13,  112,160,112,  140,118,111,  120},
    {12, 13,  136,184,136,  164,118,111,   72},
    {13, 13,  144,240,144,  200,108, 93,    0},
    { 0, 14,   72, 72,120,   77,143,125,  154},
    { 1, 14,  192,192,240,  197,143,125,   86},
    { 2, 14,  192, 72,120,  113,130,178,   82},
    { 3, 14,   72,192,240,  161,156, 71,   14},
    { 4, 14,  192, 72,240,  126,169,170,   56},
    { 5, 14,   72,192,120,  148,118, 80,   13},
    { 6, 14,   72, 72,240,   90,182,117,  128},
    { 7, 14,  192,192,120,  184,105,133,   59},
    { 8, 14,  192,120,120,  141,121,160,   26},
    { 9, 14,  152,104,120,  120,128,148,   69},
    {10, 14,  192,128,176,  152,136,153,    4},
    {11, 14,  112,112,160,  117,143,125,   74},
    {12, 14,  136,136,184,  141,143,125,   26},
    {13, 14,  144,192,192,  177,133,107,   46},
    {14, 14,  144,144,240,  154,159,122,    0},
    { 0, 15,   88, 88, 88,   88,128,128,  176},
    { 1, 15,  208,208,208,  208,128,128,   64},
    { 2, 15,  208, 88, 88,  124,116,181,  104},
    { 3, 15,   88,208,208,  172,140, 75,    8},
    { 4, 15,  208, 88,208,  137,153,173,   78},
    { 5, 15,   88,208, 88,  158,103, 84,   35},
    { 6, 15,   88, 88,208,  101,166,120,  150},
    { 7, 15,  208,208, 88,  194, 90,136,   37},
    { 8, 15,  208,136, 88,  152,105,163,   48},
    { 9, 15,  168,120, 88,  130,113,152,   91},
    {10, 15,  208,144,144,  163,122,156,   26},
    {11, 15,  128,128,128,  128,128,128,   96},
    {12, 15,  152,152,152,  152,128,128,   48},
    {13, 15,  160,208,160,  188,118,111,   24},
    {14, 15,  160,160,208,  165,143,125,   22},
    {15, 15,  176,176,176,  176,128,128,    0},
    {0,   0,    0,  0,  0,    0,  0,  0,    0} /* One extra for NOT_USED */
};

/* Indexes to the starts of each group (col1 changes) */
const UBYTE fixedind[16] = {
    0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105, 120
};

const UBYTE merge[16] = {
		/* 01 -> 10 */
    0x00,	/* 0000 -> 0000 */
    0x02,	/* 0001 -> 0010 */
    0x02,	/* 0010 -> 0010 */
    0x03,	/* 0011 -> 0011 */
    0x08,	/* 0100 -> 1000 */
    0x0a,	/* 0101 -> 1010 */
    0x0a,	/* 0110 -> 1010 */
    0x0b,	/* 0111 -> 1011 */
    0x08,	/* 1000 -> 1000 */
    0x0a,	/* 1001 -> 1010 */
    0x0a,	/* 1010 -> 1010 */
    0x0b,	/* 1011 -> 1011 */
    0x0c,	/* 1100 -> 1100 */
    0x0e,	/* 1101 -> 1110 */
    0x0e,	/* 1110 -> 1110 */
    0x0f	/* 1111 -> 1111 */
};

#define MODE_USEWHOLESCREEN	1    /* will convert the leftmost 12 pixels too */
#define MODE_MERGE		2    /* will merge FLI colors, if it means less error */

#define STAT_ERROR		32
#define STAT_USAGE		64

int  Convert(char *name, int mode, int lumratio);


int main(int argc, char *argv[]) {
    FILE *handle;
    long imerr = 0L;
    UBYTE *b, *a, *buffer, color = 0, apu[100], ty, tu, tv;
    int i, x, y, error, temp, width, height, w, h;
    int flashval = 4, fthreshold = 130, lumratio = 7;
    int dither = 0, status = 0, inf = 0, ouf = 0, shift = 0, maxval;


#if 0
    {
    static const int R[16] = {0,15,15,0,15,0,0,15,15,10,15,5,8,9,9,11};
    static const int G[16] = {0,15,0,15,0,15,0,15,6,4,7,5,8,15,9,11};
    static const int B[16] = {0,15,0,15,15,0,15,0,0,0,7,5,8,9,15,11};

    int x, y, z;

    for (y=0; y<16; y++) {
	for (x=0; x<=y; x++) {
	    z = Y(8*(R[x]+R[y]),8*(G[x]+G[y]),8*(B[x]+B[y]));
	    printf("%2d, %2d,  %3d,%3d,%3d,  %3d,%3d,%3d,  %3d,\n",
		x, y, 8*(R[x]+R[y]), 8*(G[x]+G[y]), 8*(B[x]+B[y]),
		z,
		U(z,8*(B[x]+B[y])),
		V(z,8*(R[x]+R[y])),
		abs(Y(16*R[x],16*G[x],16*B[x])-Y(16*R[y],16*G[y],16*B[y]))
		);
	}
    }
    exit(0);
    }
#endif

    for (i=1; i<argc; i++) {
	if (argv[i][0]=='-') {
	    x = 1;
	    while (argv[i][x]) {
		switch (argv[i][x]) {
		/* dither -d<value> , default value is 5 */
		case 'd':
		    if (!(dither = atoi(argv[i]+x+1)))
			dither = 5;
		    x = strlen(argv[i])-1;
		    break;
		/* flashy -f<value> , default value is 4 */
		case 'f':
		    flashval = atoi(argv[i]+x+1);
		    if (flashval<0 || flashval>8) {
			fprintf(stderr, "Flash value should be 0..8!\n");
			status |= STAT_ERROR;
		    }
		    x = strlen(argv[i])-1;
		    break;
		case 'l':
		    lumratio = atoi(argv[i]+x+1);
		    if (lumratio<0 || lumratio>20) {
			fprintf(stderr, "Luminance ratio should be 0..20!\n");
			status |= STAT_ERROR;
		    }
		    x = strlen(argv[i])-1;
		    break;
		/* -t<value> , default value is 130 */
		case 't':
		    fthreshold = atoi(argv[i]+x+1);
		    if (fthreshold<0 || fthreshold>255) {
			fprintf(stderr, "Flash threshold should be 0..255!\n");
			status |= STAT_ERROR;
		    }
		    x = strlen(argv[i])-1;
		    break;
		/* use the whole screen */
		case 'w':
		    status |= MODE_USEWHOLESCREEN;
		    break;
		/* use merge mode */
		case 'm':
		    status |= MODE_MERGE;
		    break;
		/* shift the picture */
		case 's':
		    shift = 12;
		    break;
		default:
		    status |= STAT_ERROR;
		    break;
		}
		x++;
	    }
	    if ((status & STAT_ERROR)) {
		fprintf(stderr, "Error in the options!\n");
		break;
	    }
	} else if (!inf) {
	    inf = i;
	} else if (!ouf) {
	    ouf = i;
	} else {
	    status |= STAT_ERROR;
	    fprintf(stderr, "Too many filenames!\n");
	    break;
	}
    }
    if ((status & STAT_ERROR) || !inf || !ouf) {
	if (!inf || !ouf)
	    fprintf(stderr, "This program needs two filenames!\n");

	fprintf(stderr, "%s\n", vers+7);
	fprintf(stderr, "Usage: %s  [-mswd<val> -f<val> -t<val> -l<val>] ppmfile fflifile\n", argv[0]);
	fprintf(stderr, "       d = do a simple dither in the color conversion\n");
	fprintf(stderr, "       f = the ratio of flicker tolerated (0..8), default is 4\n");
	fprintf(stderr, "       t = the amount of flicker tolerated in colors (0..255), default is 130\n");
	fprintf(stderr, "       l = the importance of luminance in color conversion (0..20), default is 7\n");
	fprintf(stderr, "       w = use the 12 leftmost pixels too (will use approximation)\n");
	fprintf(stderr, "       s = shift the picture by 12 pixeld before converting\n");
	fprintf(stderr, "       m = merge FLI colors if it means less errors\n");
	return 5;
    }
    if ((handle = fopen(argv[inf], "rb"))) {
	fgets((char *)apu, 10, handle);
	if (!strncmp((char *)apu, "P6", 2)) {
	    fgets((char *)apu, 20, handle);
	    width = atoi((char *)apu);
	    i = 0;
	    while (apu[i]!=' ' && apu[i]!='\t' && apu[i])
		i++;
	    height = atoi((char *)apu+i);
	    fprintf(stderr, "Input file seems to be %d x %d\n", width, height);
	    fgets((char *)apu, 20, handle);    /* just get the maxval..*/
	    maxval = atol((char *)apu);

	    if ((buffer = (UBYTE *)calloc(3*width*height, 1))) {
		if (!fread((char *)buffer, 3*width*height, 1, handle))
		    fprintf(stderr, "Short file! Result may be quite original.. \n");

		fclose(handle);
		if (width>160-shift || height>200)
		    fprintf(stderr, "Output picture will be sized %d x 200\n", 160-shift);

		w = width;
		h = height;
		if (w<148)
		    shift = 12;
		if (w>160-shift)
		    w = 160-shift;
		if (h>200)
		    h = 200;
		if (shift)
		    fprintf(stderr, "Shifting the picture by %d pixels\n", shift);
		fprintf(stderr, "Converting colors\n");
		if (maxval != 255) {
		    for(i=3*width*height-1; i>=0; i--) {
			buffer[i] = buffer[i]*255/maxval;
		    }
		}
		for (y=0; y<h; y++) {
		    a = buffer+3*width*y;
		    for (x=0; x<w; x++) {
			/* Target YUV */
			ty = Y(*a, *(a+1), *(a+2));
			tu = U(ty, *(a+2));
			tv = V(ty, *a);

			error = MAXINT;
			for (i=0; i<COLORS; i++) {
			    if (flashcol[i][8] <= fthreshold) {
				temp = ERROR(lumratio*ty, lumratio*flashcol[i][5],
					     tu, flashcol[i][6],
					     tv, flashcol[i][7],
					     flashval*flashcol[i][8]);
				if (temp<error) {
				    color = i;
				    error = temp;
				    if (error<10)    /* we are very satisfied if the error is less than .. */
					break;
				}
			    }
			}
			pic[y][x+shift] = color;

			if (dither && x<w-1) {
			    /* Propagate right and down */
			    b = a+3;
			    temp = (*a-flashcol[color][2])/dither/2+*b;
			    if (temp>=0 && temp<256)
				*b = temp;
			    b++;
			    temp = (*(a+1)-flashcol[color][3])/dither/2+*b;
			    if (temp>=0 && temp<256)
				*b = temp;
			    b++;
			    temp = (*(a+2)-flashcol[color][4])/dither/2+*b;
			    if (temp>=0 && temp<256)
				*b = temp;

			    if (y+1<h) {
				b = a+3*width;
				temp = (*a-flashcol[color][2])/dither/2+*b;
				if (temp>=0 && temp<256)
				    *b = temp;
				b++;
				temp = (*(a+1)-flashcol[color][3])/dither/2+*b;
				if (temp>=0 && temp<256)
				    *b = temp;
				b++;
				temp = (*(a+2)-flashcol[color][4])/dither/2+*b;
				if (temp>=0 && temp<256)
				    *b = temp;
			    }
			}
			imerr += error;
			a += 3;
		    }
		    fprintf(stderr, "\rLine: %d Color error: %ld", y, imerr);
		    fflush(stderr); /* !!! BUG ?! */
		}
		fprintf(stderr, "\n");
		free(buffer);

		return Convert(argv[ouf], status, lumratio);
	    }
	    fprintf(stderr, "Memory allocation error (%d bytes)\n", 3*width*height);
	    fclose(handle);
	    return 42;
	}
	fprintf(stderr, "Don't know how to handle this type of ppm ..(%s)\n", apu);
	fclose(handle);
	return 24;
    }
    fprintf(stderr, "Could not access %s\n", argv[inf]);
    return 10;
}


int Convert(char *name, int mode, int lumratio) {
    int memind, x, y, ind, pos, bits, d[4], e, error, start = 12, merges = 0;
    UBYTE    *BASE, a, b, c;
    UBYTE    *colmemhist[16];/* [16][1024]*/
    UBYTE    *linehist[16];    /* [16][256] */
    UBYTE    *gfxmem, *buf;    /* 2x[8192]  */
    UBYTE    *FLI1, *FLI2;    /* 2x[8192]     */
    UBYTE    *colmem;	/* [1024]    */
    UBYTE    *bgcol;	    /* [256]     */
    FILE *handle;
    const UBYTE bitmask[4] = {0x3f, 0xcf, 0xf3, 0xfc};
    const UBYTE bitshift[4]= {0x40, 0x10, 0x04, 0x01};

    if ((mode & MODE_USEWHOLESCREEN))
	start = 0;

#define ALLOC_SIZE    53*1024+256	/* size of the allocation */
#define NOT_USED    COLORS+1

    if ((BASE = (UBYTE *)calloc(ALLOC_SIZE, 1))) {
	/* initialize the pointers */
	for (x=0; x<16; x++)
	    colmemhist[x] = BASE+x*1024;
	for (x=0; x<16; x++)
	    linehist[x] = BASE+16*1024+x*256;
	gfxmem = BASE+16*1024+16*256;
	FLI1 = gfxmem+8192;
	FLI2 = FLI1+8192;
	colmem = FLI2+8192;
	buf = colmem+1024;
	bgcol = buf+8192;

	fprintf(stderr, "Making histograph (matrix)\n");
	for (y=0; y<200; y++) {
	    for (x=0; x<40; x++) {
		error = 1;
		a = pic[y][4*x+0];
		b = pic[y][4*x+1];
		c = pic[y][4*x+2];
		e = pic[y][4*x+3];

		if (b!=a)
		    error++;
		if (c!=a && c!=b)
		    error++;
		if (e!=a && e!=b && e!=c)
		    error++;
		/* if there is more than 2 colors in the same line, count them. */
		/* if there is only two colors, we can use the FLI colors and   */
		/* there is no need to use color memory ! */
		if (error>2) {
		    ind = x+(y/8)*40;
		    if (flashcol[a][0]==flashcol[a][1])
			colmemhist[flashcol[a][0]][ind] += 1;
		    if (flashcol[b][0]==flashcol[b][1])
			colmemhist[flashcol[b][0]][ind] += 1;
		    if (flashcol[c][0]==flashcol[c][1])
			colmemhist[flashcol[c][0]][ind] += 1;
		    if (flashcol[e][0]==flashcol[e][1])
			colmemhist[flashcol[e][0]][ind] += 1;
		}
	    }
	}
	fprintf(stderr, "Selecting colors for color memory.\n");
	for (y=0; y<1000; y++) {
	    if (y%40<3) {
		/* FLI has a 'feature' in the first three char columns (12 pixels) */
		/* bitpair 11 will be brown, 01 and 10 will be light gray          */
		/* here we select those colors - conversion will try to use them   */
		colmem[y] = 9;
		FLI1[y]=FLI1[y+1024]=FLI1[y+2048]=FLI1[y+3072]=
		FLI1[y+4096]=FLI1[y+5120]=FLI1[y+6144]=FLI1[y+7168] = 0xfc;
		FLI2[y]=FLI2[y+1024]=FLI2[y+2048]=FLI2[y+3072]=
		FLI2[y+4096]=FLI2[y+5120]=FLI2[y+6144]=FLI2[y+7168] = 0xfc;
	    } else {
		b = NOT_USED;
		a = 0;
		for (x=1; x<16; x++) {
		    /* we take the color that is used mostly as the third color  */
		    /* or the best possible in that respect, 0=black don't count */
		    if (colmemhist[x][y]>a) {
			b = x;
			a = colmemhist[x][y];
		    }
		}
		/* if b==NOT_USED, there are no non-flashing colors in this cell */

		colmem[y] = b;
		FLI1[y]=FLI1[y+1024]=FLI1[y+2048]=FLI1[y+3072]=
		FLI1[y+4096]=FLI1[y+5120]=FLI1[y+6144]=FLI1[y+7168] = NOT_USED;
		FLI2[y]=FLI2[y+1024]=FLI2[y+2048]=FLI2[y+3072]=
		FLI2[y+4096]=FLI2[y+5120]=FLI2[y+6144]=FLI2[y+7168] = NOT_USED;
	    }
	}

	fprintf(stderr, "Creating FFLI-picture\n");
	for (y=0; y<200; y++) {
	    for (x=start; x<160; x++) {
		ind = x/4+(y/8)*40;    /* color memory index */
		pos = y%8+ind*8;	/* grafix memory byte */
		bits = (x%4);	    /* bit numbers */
		memind = (y%8)*1024+ind;

		c = pic[y][x];
		if (c==bgcol[y+6]) {
		    b = 0;
		} else if (flashcol[c][0]==colmem[ind] &&
			   flashcol[c][1]==colmem[ind]) {
		    /* this MUST BE here, not after those b=1 and 2 !!! */
		    b = 3;
		} else if (c==FLI2[memind]) {
		    b = 2;
		} else if (c==FLI1[memind]) {
		    b = 1;
		} else {
		    if (FLI2[memind]==NOT_USED) {
			b = 2;
			FLI2[memind] = c;
		    } else if (FLI1[memind]==NOT_USED) {
			b = 1;
			FLI1[memind] = c;
		    } else {
			/* INDEX is important, see previous b values up there.. */
			e = bgcol[y+6];
			d[0]= ERROR(lumratio*flashcol[c][5], lumratio*flashcol[e][5],
				    flashcol[c][6], flashcol[e][6],
				    flashcol[c][7], flashcol[e][7],
				    0); /* Flashyness has nothing to do with it now */
			e = FLI1[memind];
			d[1]= ERROR(lumratio*flashcol[c][5], lumratio*flashcol[e][5],
				    flashcol[c][6], flashcol[e][6],
				    flashcol[c][7], flashcol[e][7],
				    0); /* Flashyness has nothing to do with it now */
			e = FLI2[memind];
			d[2]= ERROR(lumratio*flashcol[c][5], lumratio*flashcol[e][5],
				    flashcol[c][6], flashcol[e][6],
				    flashcol[c][7], flashcol[e][7],
				    0); /* Flashyness has nothing to do with it now */

			if (colmem[ind]==NOT_USED) {
			    /* Randomly select a color */
			    /* TODO: use some criteria */
			    colmem[ind] = flashcol[c][1];
fprintf(stderr, "%d selected to colmem (%d,%d)\n", colmem[ind], flashcol[c][0], flashcol[c][1]);
			}
			e = fixedind[colmem[ind]]+colmem[ind];
			d[3]= ERROR(lumratio*flashcol[c][5], lumratio*flashcol[e][5],
				    flashcol[c][6], flashcol[e][6],
				    flashcol[c][7], flashcol[e][7],
				    0); /* Flashyness has nothing to do with it now */

			error = MAXINT;
			b = 0;
			for (e=0; e<4; e++) {
			    if (d[e]<error) {
				b = e;
				error = d[e];
			    }
			}
			if (x>12 && (mode & MODE_MERGE)) {
			    /* check if FLI color merge will cause less error */
			    e =ERROR(lumratio*flashcol[FLI1[memind]][5], lumratio*flashcol[FLI2[memind]][5],
				    flashcol[FLI1[memind]][6], flashcol[FLI2[memind]][6],
				    flashcol[FLI1[memind]][7], flashcol[FLI2[memind]][7],
				    0); /* Flashyness has nothing to do with it now */

			    if (e<error) {
				gfxmem[pos] = (merge[gfxmem[pos]>>4]<<4)+merge[gfxmem[pos]&15];
				merges++;
				b = 1;
				FLI1[memind] = c;
			    }
			}
		    }
		}
		gfxmem[pos] = (gfxmem[pos] & bitmask[bits]) | (bitshift[bits] * b);
	    }
	}

	if (merges)
	    fprintf(stderr, "Merged %d times\n", merges);
	fprintf(stderr, "Saving %s\n", name);
	if ((handle = fopen(name, "wb"))) {
	    fwrite("\377\072f", 1, 3, handle);    /* load address $3b00 */

	    for (x=0; x<256; x++)
		buf[x] = flashcol[bgcol[x]][(x&1)^1];
	    fwrite(buf, 1, 256, handle);

	    fwrite(colmem, 1, 1024, handle);

	    for (x=0; x<8192; x++) {
		if ((x/1024) & 1)
		    buf[x] = 16*flashcol[FLI1[x]][0]+flashcol[FLI2[x]][0];
		else
		    buf[x] = 16*flashcol[FLI1[x]][1]+flashcol[FLI2[x]][1];
	    }
	    fwrite(buf, 1, 8192, handle);
	    fwrite(gfxmem, 1, 8192, handle);

	    for (x=0; x<8192; x++) {
		if ((x/1024) & 1)
		    buf[x] = 16*flashcol[FLI1[x]][1]+flashcol[FLI2[x]][1];
		else
		    buf[x] = 16*flashcol[FLI1[x]][0]+flashcol[FLI2[x]][0];
	    }
	    fwrite(buf, 1, 8192, handle);

	    for (x=0; x<256; x++)
		buf[x] = flashcol[bgcol[x]][x&1];
	    fwrite(buf, 1, 256, handle);
	    fclose(handle);
	} else {
	    fprintf(stderr, "Write failed!\n");
	    free(BASE);
	    return 21;
	}
	free(BASE);
	return 0;
    } else {
	fprintf(stderr, "calloc failed! (%d bytes)\n", ALLOC_SIZE);
	return 42;
    }
}

