/************************************************************************\
* C64 FLI,BFLI and Koala to portable pixmap converter                    *
* By Pasi 'Albert' Ojala (c) 1994                                        *
*    albert@cs.tut.fi  albert@cc.tut.fi                                  *
*                                                                        *
* 05-Sep-94 New palette                                                  *
* 16-Sep-94 BFLI added                                                   *
* 28-Jan-95 BFLI size extended to 400 lines                              *
* 12-Feb-95 AFLI added (load address $4000, color memories first)        *
* 13-Feb-95 Increased the BFLI display window - the file format changed! *
*                                                                        *
\************************************************************************/

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

typedef unsigned char UBYTE;

const char *vers = "\0$VER: c64toppm 1.5 (30.7.98)\n";

UBYTE *gfxmem;        /* 16384 */
UBYTE *chrmem[8];     /*  2048 */
UBYTE *colmem;        /*  1024 */
UBYTE bgcol[256];

const UBYTE col[16][6] = {
/*    R   G   B     Y   U   V */
    {  0,  0,  0,    0,128,128},
    {240,240,240,  240,128,128},
    {240,  0,  0,   72,103,235},
    {  0,240,240,  168,153, 21},
    {240,  0,240,   98,179,219},
    {  0,240,  0,  141, 78, 38},
    {  0,  0,240,   26,205,112},
    {240,240,  0,  213, 52,145},
    {240, 96,  0,  128, 82,199},
    {160, 64,  0,   85, 98,176},
    {240,112,112,  150,115,185},
    { 80, 80, 80,   80,128,128},
    {128,128,128,  128,128,128},
    {144,240,144,  200,108, 93},
    {144,144,240,  154,159,122},
    {176,176,176,  176,128,128}
};

#define MODE_FLI	1
#define MODE_KOALA	2
#define MODE_BFLI	3
#define MODE_AFLI	4

void Convert(int mode,char *name);

int main(int argc,char *argv[]) {
    UBYTE temp[10];
    int i;
    FILE *handle;
    UBYTE *BASE;
    char *name;
#define ALLOC_SIZE    (33*1024)

    if (argc<4) {
	name = NULL;
	if (argc==3)
	    name = argv[2];
	if (!(BASE = calloc(ALLOC_SIZE, 1))) {
	    fprintf(stderr,
		    "Memory allocation error for %d bytes!\n",
		    ALLOC_SIZE);
	    return EXIT_FAILURE;
	}
	gfxmem = BASE;
	for (i=0; i<8; i++)
	    chrmem[i] = BASE+16384+i*2048;
	colmem = BASE+32768;

	if (argc>1) {
	    if ((handle = fopen(argv[1], "rb"))) {
		fprintf(stderr, "%s: ", argv[1]);
	    } else {
		fprintf(stderr, "Could not access %s\n", argv[1]);
		free(BASE);
		return EXIT_FAILURE;
	    }
	} else {
	    handle = stdin;
	}
	fread(temp, 1, 2, handle);
	if (temp[0]==0 && (temp[1]==0x3b || temp[1]==0x3c)) {
	    if (temp[1]==0x3b) {
		fprintf(stderr, "FLI file with background colors\n");
		fread(bgcol, 256, 1, handle);
	    } else {
		fprintf(stderr, "FLI file without background colors\n");
		for (i=0; i<256; i++)
		    bgcol[i] = 0;
	    }
	    fread(colmem, 1, 1024, handle);

	    fread(chrmem[0], 1, 1024, handle);
	    fread(chrmem[1], 1, 1024, handle);
	    fread(chrmem[2], 1, 1024, handle);
	    fread(chrmem[3], 1, 1024, handle);
	    fread(chrmem[4], 1, 1024, handle);
	    fread(chrmem[5], 1, 1024, handle);
	    fread(chrmem[6], 1, 1024, handle);
	    fread(chrmem[7], 1, 1024, handle);

	    i = fread(gfxmem, 1, 8000, handle);
	    if (handle != stdin)
		fclose(handle);
	    if (i>=8000) {
		Convert(MODE_FLI, name);
		free(BASE);
		return 0;
	    }
	    fprintf(stderr, "Short file!\n");
	    free(BASE);
	    return EXIT_FAILURE;
	}
	if (temp[0]==0xff && temp[1]==0x3b) {
	    fread(temp+2, 1, 1, handle);
	    if (temp[2]=='b') {
		fprintf(stderr, "BFLI file\n");

		/* Color mem is strange */
		fread(colmem, 1, 1024, handle);

		/* 1st FLI color mem */
		fread(chrmem[0], 1, 1000, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[1], 1, 1000, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[2], 1, 1000, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[3], 1, 1000, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[4], 1, 1000, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[5], 1, 1000, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[6], 1, 1000, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[7], 1, 1000, handle);
		fread(bgcol, 1, 24, handle);

		/* 1st gfx mem */
		fread(gfxmem, 1, 8000, handle);
		fread(bgcol, 1, 192, handle);

		/* 2nd FLI color mem */
		fread(chrmem[0]+1024, 1, 976, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[0]+1000, 1, 24, handle);
		fread(chrmem[1]+1024, 1, 976, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[1]+1000, 1, 24, handle);
		fread(chrmem[2]+1024, 1, 976, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[2]+1000, 1, 24, handle);
		fread(chrmem[3]+1024, 1, 976, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[3]+1000, 1, 24, handle);
		fread(chrmem[4]+1024, 1, 976, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[4]+1000, 1, 24, handle);
		fread(chrmem[5]+1024, 1, 976, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[5]+1000, 1, 24, handle);
		fread(chrmem[6]+1024, 1, 976, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[6]+1000, 1, 24, handle);
		fread(chrmem[7]+1024, 1, 976, handle);
		fread(bgcol, 1, 24, handle);
		fread(chrmem[7]+1000, 1, 24, handle);

		/* 2nd gfx mem */
		fread(gfxmem+8192, 1, 7808, handle);
		fread(bgcol, 1, 192, handle);
		fread(gfxmem+8000, 1, 192, handle);

		Convert(MODE_BFLI, name);
		free(BASE);
		return 0;
	    } else if (temp[2]=='f') {
		fprintf(stderr, "Can not convert FFLI pictures (too big palette)\n");
		if (handle != stdin)
		    fclose(handle);
		free(BASE);
		return 0;
	    } else {
		fprintf(stderr, "Unknown file format ID (%c)!\n", temp[2]);
		if (handle != stdin)
		    fclose(handle);
		free(BASE);
		return 0;
	    }
	}
	if (temp[0]==0 && temp[1]==0x40) {
	    fprintf(stderr, "AFLI file\n");

	    fread(chrmem[0], 1, 1024, handle);
	    fread(chrmem[1], 1, 1024, handle);
	    fread(chrmem[2], 1, 1024, handle);
	    fread(chrmem[3], 1, 1024, handle);
	    fread(chrmem[4], 1, 1024, handle);
	    fread(chrmem[5], 1, 1024, handle);
	    fread(chrmem[6], 1, 1024, handle);
	    fread(chrmem[7], 1, 1024, handle);

	    i = fread(gfxmem, 1, 8000, handle);
	    if (handle != stdin)
		fclose(handle);
	    if (i>=8000) {
		Convert(MODE_AFLI, name);
		free(BASE);
		return 0;
	    }
	    fprintf(stderr, "Short file!\n");
	    free(BASE);
	    return EXIT_FAILURE;
	}
	if (temp[0]==0 || temp[1]!=0x60) {
	    fprintf(stderr, "Koalapainter format\n");

	    fread(gfxmem, 1, 8000, handle);
	    fread(chrmem[0], 1, 1000, handle);
	    fread(colmem, 1, 1000, handle);
	    i = fread(bgcol, 1, 1, handle);
	    if (handle != stdin)
		fclose(handle);
	    if (i) {
		Convert(MODE_KOALA, name);
		free(BASE);
		return 0;
	    }
	    fprintf(stderr, "Short file!\n");
	    free(BASE);
	    return EXIT_FAILURE;
	}
	fprintf(stderr, "Unknown file format\n");
	if (handle != stdin)
	    fclose(handle);
	free(BASE);
	return 0;
    } else {
	fprintf(stderr, "%s", vers+7);
	fprintf(stderr, "Usage: %s [c64gfxfile [ppmfile]]\n", argv[0]);
	return 0;
    }
}


void Convert(int mode, char *name) {
    int x, y, ind, pos, bits, ysize = 200, xsize = 160;
    UBYTE a, b = 0;
    const UBYTE bitmask[4] = {0xc0, 0x30, 0x0c, 0x03};
    const UBYTE bitshift[4] = {0x40, 0x10, 0x04, 0x01};
    static char linebuf[3*320];
    char *p;
    FILE *fp;

    if (mode==MODE_BFLI)
	ysize = 400;
    if (mode==MODE_AFLI)
	xsize = 320;

    fprintf(stderr, "Converting picture\n");
    if (!name) {
	fp = stdout;
    } else {
	if (!(fp = fopen(name, "wb"))) {
	    fprintf(stderr, "could not open \"%s\" for writing!\n", name);
	    return;
	}
    }
    fprintf(fp, "P6\n%d %d\n255\n", xsize, ysize);
    for (y=0; y<ysize; y++) {
	if (mode == MODE_AFLI) {
	    p = &linebuf[0];
	    for (x=0; x<320; x++) {
		ind = x/8+(y/8)*40;		/* color memory index */
		pos = y%8+(x/8)*8+(y/8)*320;	/* grafix memory byte */
		bits = 7-(x%8);			/* bit numbers */

		a = (gfxmem[pos]>>bits) & 1;
		if (x<24) {
		    b = 0xf;
		} else {
		    if (a)
			b=chrmem[y%8][ind]/16;
		    else
			b=chrmem[y%8][ind]%16;
		}
		*p++ = col[b][0];
		*p++ = col[b][1];
		*p++ = col[b][2];
	    }
	    fwrite(linebuf, 1, 3*320, fp);
	} else {
	    p = &linebuf[0];
	    for (x=0; x<160; x++) {
		ind = x/4+(y/8)*40;		/* color memory index */
		pos = y%8+(x/4)*8+(y/8)*320;	/* grafix memory byte */
		bits = (x%4);			/* bit numbers */

		a = (gfxmem[pos] & bitmask[bits])/bitshift[bits];
		if (mode == MODE_FLI) {
		    switch (a) {
		    case 0:
			b = bgcol[y+6];
			break;
		    case 1:
			if (x<12) {
			    b = 15;
			} else {
			    b = chrmem[y%8][ind]/16;
			}
			break;
		    case 2:
			if (x<12) {
			    b = 15;
			} else {
			    b = chrmem[y%8][ind]%16;
			}
			break;
		    case 3:
			if (x<12) {
			    b = 9;
			} else {
			    b = colmem[ind]%16;
			}
			break;
		    }
		} else if (mode == MODE_BFLI) {
		    switch (a) {
		    case 0:
			b = 0;
			break;
		    case 1:
			if (x<12) {
			    b = 15;
			} else {
			    b = chrmem[y%8][ind]/16;
			}
			break;
		    case 2:
			if (x<12) {
			    b = 15;
			} else {
			    b = chrmem[y%8][ind]%16;
			}
			break;
		    case 3:
			if (x<12) {
			    b = 9;
			} else {
			    b = colmem[(ind&1023)]%16;
			}
			break;
		    }
		} else if (mode == MODE_KOALA) {
		    switch (a) {
		    case 0:
			b = bgcol[0];
			break;
		    case 1:
			b = chrmem[0][ind]/16;
			break;
		    case 2:
			b = chrmem[0][ind]%16;
			break;
		    case 3:
			b = colmem[ind]%16;
			break;
		    }
		}
		*p++ = col[b][0];
		*p++ = col[b][1];
		*p++ = col[b][2];
	    }
	    fwrite(linebuf, 1, 3*160, fp);
	}
    }
    if (fp != stdout)
	fclose(fp);
}


