#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "defs.h"
#include "externs.h"

char  *pcx_buf;
char   pcx_pal[256][3];
char   pcx_name[128];
struct pcx_header pcx;
int    pcx_w, pcx_h;
short  buffer[256];

/* .incchr/spr/pal pseudo */

do_pcx(int *ip)
{
	FILE *f;
	char c;
	int val[5];
	int size;
	int i, n = 0;

	labldef(loccnt, 1);
	if (!get_filename(ip))
		return;
	for (;;) {
		while (isspace(c = prlnbuf[(*ip)++]));
		if ((c != ',') && (c != ';') && (c != 0)) {
			error("Syntax error!");
			return;
		}
		if (c != ',')
			break;
		if (!evaluate(ip, 0))
			return;
		val[n++] = value;
		if (n == 5)
			break;
	}			
	if (optype & (1 << n)) {
		error("Invalid number of arguments!");
		return;
	}
	if (!load_pcx(filename))
		return;
	if (pass == LAST_PASS) {
		loadlc(loccnt, 0);
		println();
	}
	switch (opval) {
	case 8:
		do_chr(n, val);
		break;
	case 9:
		do_spr(n, val);
   		break;
   	case 10:
   		do_pal(n, val);
   		break;
	}
}

/*  convert a pcx to characters  */

int do_chr(int n, unsigned int *val)
{
	unsigned char *ptr;
	unsigned int   i, j, k, l, x, y, w, h;
	unsigned int   a, b;
	unsigned char  c;

	switch (n) {
	case 0:
		x = 0; y = 0;
		w = (pcx_w / 8); h = (pcx_h / 8);
		break;
	case 2:
		x = 0; y = 0;
		w = val[0]; h = val[1];
		if (((w * 8) > pcx_w) || ((h * 8) > pcx_h)) {
			error("Index out of range!");
			return;
		}
		break;
	case 4:
		x = val[0]; y = val[1];
		w = val[2]; h = val[3];
		if (((x + w * 8) > pcx_w) || ((y + h * 8) > pcx_h)) {
			error("Index out of range!");
			return;
		}
		break;
	}
	if (pass == LAST_PASS) {
		for (i = 0; i < h; i++) {
			for (j = 0; j < w; j++) {
				ptr = pcx_buf + (x + (j * 8)) + ((y + (i * 8)) * pcx_w);
				for (k = 0; k < 8; k++) {
					a = 0;
					b = 0;
					for (l = 0; l < 8; l++) {
						c = ptr[l];
						a = (a << 1) | ((c & 0x01))      | ((c & 0x02) << 7);
						b = (b << 1) | ((c & 0x04) >> 2) | ((c & 0x08) << 5);
					}				
					buffer[k] = a;
					buffer[k+8] = b;
					ptr += pcx_w;
				}
				copy_buffer(32);
			}
		}
	}
	else
		copy_buffer(32 * w * h);
}

/*  convert a pcx to sprites  */

int do_spr(int n, unsigned int *val)
{
	unsigned char *ptr;
	unsigned int   i, j, k, l, x, y, w, h;
	unsigned int   a, b, c, d;
	unsigned char  e, f;

	switch (n) {
	case 0:
		x = 0; y = 0;
		w = (pcx_w / 16); h = (pcx_h / 16);
		break;
	case 2:
		x = 0; y = 0;
		w = val[0]; h = val[1];
		if (((w * 16) > pcx_w) || ((h * 16) > pcx_h)) {
			error("Index out of range!");
			return;
		}
		break;
	case 4:
		x = val[0]; y = val[1];
		w = val[2]; h = val[3];
		if (((x + w * 16) > pcx_w) || ((y + h * 16) > pcx_h)) {
			error("Index out of range!");
			return;
		}
		break;
	}
	if (pass == LAST_PASS) {
		for (i = 0; i < h; i++) {
			for (j = 0; j < w; j++) {
				ptr = pcx_buf + (x + (j * 16)) + ((y + (i * 16)) * pcx_w);
				for (k = 0; k < 16; k++) {
					a = 0; b = 0;
					c = 0; d = 0;
					for (l = 0; l < 8; l++) {
						e = ptr[l + 8]; f = ptr[l];
						a = (a << 1) | ((e & 0x01))      | ((f & 0x01) << 8);
						b = (b << 1) | ((e & 0x02) >> 1) | ((f & 0x02) << 7);
						c = (c << 1) | ((e & 0x04) >> 2) | ((f & 0x04) << 6);
						d = (d << 1) | ((e & 0x08) >> 3) | ((f & 0x08) << 5);
					}				
					buffer[k     ] = a;
					buffer[k + 16] = b;
					buffer[k + 32] = c;
					buffer[k + 48] = d;
					ptr += pcx_w;
				}
				copy_buffer(128);
			}
		}
	}
	else
		copy_buffer(128 * w * h);
}

/*  convert a pcx to palette  */

int do_pal(int n, unsigned int *val)
{
	int i, start, nb;
	int r, g, b;

	switch (n) {
	case 0:
		start = 0;
		nb = 256;
		break;
	case 1:
		if (val[0] > 15) {
			error("Index out of range!");
			return;
		}
		start = val[0] << 4;
		nb = 16;
		break;
	case 2:
		if (((val[0] + val[1]) > 16) || (val[1] == 0)) {
			error("Index out of range!");
			return;
		}
		start = val[0] << 4;
		nb = val[1] << 4;
		break;
	}
	if (pass == LAST_PASS) {
		for (i = 0; i < nb; i++) {
			r = pcx_pal[start + i][0];
			g = pcx_pal[start + i][1];
			b = pcx_pal[start + i][2];
			buffer[i] = ((r & 0xE0) >> 2) | ((g & 0xE0) << 1) | ((b & 0xE0) >> 5);
		}
	}
	copy_buffer(nb << 1);
}

/* copy a buffer to the current location */

copy_buffer(int size)
{
	if (((bank << 13) + loccnt + size) > (8192 * 128)) {
		error("File too big!");
		return;
	}
	if (pass == LAST_PASS) {
		memcpy(&rom[bank][loccnt], buffer, size);
	}
	bank  += (loccnt + size) >> 13;
	loccnt = (loccnt + size) & 0x1FFF;
	if (bank > max_bank) {
		if (loccnt)
			max_bank = bank;
		else
			max_bank = bank - 1;
	}
}

/*  load a pcx image and unpack it  */

int load_pcx(char *name)
{
	int   i, c, x, y, w, h;
	char *ptr;
	FILE *f;

	if (!strcmp(pcx_name, name) && strlen(name))
		return (1);
	else {
		if (pcx_buf)
			free(pcx_buf);
		pcx_buf = NULL;
		pcx_name[0] = '\0';
	}
	if ((f = fopen(name, "rb")) == NULL) {
		error("Can not open file!");
		return (0);
	}
	fread(&pcx, 128, 1, f);
	x = 0;
	y = 0;
	pcx_w = (pcx.xmax - pcx.xmin + 1);
	pcx_h = (pcx.ymax - pcx.ymin + 1);
	if ((pcx_w <= 640) && (pcx_h <= 480) && (pcx_w >= 16) && (pcx_h >= 16)) {
	    pcx_buf = malloc(pcx_w * pcx_h);
		if ((pcx.encoding == 1) && (pcx.bpp == 8) && (pcx.np == 1) && (pcx_buf)) {
			strcpy(pcx_name, name);
			ptr = pcx_buf;
			do {
				c = fgetc(f);
				if (c == EOF)
					break;
				if ((c & 0xC0) == 0xC0) {
					i = (c & 0x3F);
					c = fgetc(f);
				} else
					i = 1;
				do {
					*ptr++ = c;
					x++;
					if (x == pcx_w) {
						x = 0;
						y++;
					}
				} while (--i);
			} while (y < pcx_h);
			if (c != EOF)
				c = fgetc(f);
			while ((c != 12) && (c != EOF))
				c = fgetc(f);
			if (c == 12)
				fread(pcx_pal, 768, 1, f);
			fclose(f);
			return (1);
		}
	}
	fclose(f);
	if (pcx_buf)
		error("Can not load file, invalid format!");
	else
		error("Can not load file, not enough memory!");
	return (0);
}

