/* E3 Demo CD 

   pcx.c
   (c)2000-2001 Dan Potter

   PCX image loader
*/

static char id[] = "E3CD $Id$";

#include <kallisti/stdtypes.h>
#include <kallisti/abi/fs.h>
#include <malloc.h>
#include "pcx.h"

/* Provided by main program */
extern abi_fs_t *fs;


typedef struct {
	char   mfg;               /* manufacturer, always 0xa0		*/
	char   ver;               /* encoder version number (5)		*/
	char   enc;               /* encoding code, always 1		*/
	char   bpp;               /* bits per pixel, 8 in mode 0x13	*/
	uint16 xmin,ymin;         /* image origin, usually 0,0		*/
	uint16 xmax,ymax;         /* image dimensions			*/
	uint16 hres;              /* horizontal resolution value	*/
	uint16 vres;              /* vertical resolution value		*/
	char   pal[48];           /* palette (not in mode 0x13)		*/
	char   reserved;          /* who knows?				*/
	char   clrplanes;         /* number of planes, 1 in mode 0x13	*/
	uint16 bpl;               /* bytes per line, 80 in mode 0x13	*/
	uint16 pltype;            /* Grey or Color palette flag		*/
	char   filler[58];        /* Zsoft wanted a 128 byte header	*/
} pcx_hdr;

uint32 get_sr() {
	asm("stc	sr,r0");
}

Image *pcx_load(const char *fn) {
	pcx_hdr	pcxh;
	uint32	fd;
	int	bytes = 0;	/* counts unpacked bytes */
	uint8	c;		/* byte being processed */
	int	runlen, i;	/* length of packet */
	int	num_bytes;
	uint8	*pcxpal;
	uint8	*image;
	Image	*rv;
	int	r, g, b, v;

	/* Open the file */
	fd = fs->open(fn, O_RDONLY);
	if (fd == 0) {
		printf("pcx_load(%s): Couldn't open file\r\n", fn);
		return NULL;
	}

	/* Load the PCX header */
	fs->read(fd, &pcxh, sizeof(pcxh));
	if (pcxh.bpp != 8) {
		printf("pcx_load(%s): PCX data is not 8bpp\r\n", fn);
		fs->close(fd);
		return 0;
	}

	/* Allocate the output structure */
	rv = (Image*)malloc(sizeof(Image));
	rv->width = pcxh.xmax + 1;
	rv->height = pcxh.ymax + 1;
	num_bytes = rv->width * rv->height;
	printf("pcx_load(%s): image %08x is %dx%d (%d bytes)\r\n", fn, rv, rv->width, rv->height, num_bytes);
	rv->pixel_data = (uint16 *)malloc(num_bytes * 2);

	/* Do the RLE decoding (allocate a temp buffer + palette) */
	image = malloc(num_bytes + 768);
	if (image == NULL) {
		printf("pcx_load(%s): Can't allocate temp space of %d bytes\r\n", fn, num_bytes);
		fs->close(fd);
		return NULL;
	}

	printf("  decoding into buffer at %08x\r\n", image);
	do {
		fs->read(fd, &c, 1);	/* Read one byte */

		if ((c & 0xc0) == 0xc0) {	/* high 2 bits set is packet */
			runlen = (c & 0x3f);	/* AND off the high bits */
			fs->read(fd, &c, 1);
			while(runlen--)
				image[bytes++] = c;
		}
		else
			image[bytes++] = c;
	} while (bytes < num_bytes);
	

	/* Load the palette */
	fs->read(fd, &c, 1);		/* This is a marker before the palette */
	pcxpal = image + num_bytes;
	fs->read(fd, pcxpal, 768);
	fs->close(fd);
	
	/* Decode the image into RGB565 */
	for (i=0; i<num_bytes; i++) {
		v = image[i];
		r = pcxpal[v*3+0];
		g = pcxpal[v*3+1];
		b = pcxpal[v*3+2];
			
		v = (((r >> 3) & 0x1f) << 11)
			| (((g >> 2) & 0x3f) << 5)
			| (((b >> 3) & 0x1f) << 0);
		rv->pixel_data[i] = v;
	}

	/* Free temp buffers */
	free(image);
	return rv;
}

void pcx_free(Image *img) {
	free(img->pixel_data);
	free(img);
}
