/***
****  QuBE --- Graphical image manipulation routines.
***/

#include "qube.h"
#include "image.h"

/* This is how Quake stores its pictures. */

static struct {
	char name[16];
	long width;
	long height;
	long scale1pos;
	long scale2pos;
	long scale4pos;
	long scale8pos;
} picture;

/* Again, this table should be dynamic or something */

static long int picptrs[1024];

/* Standard (ugh) Windows BMP file.  Padded with the 2 chars at the top
   so that RISC architectures won't burp trying to use it. */

static struct {
	char dummyspace[2];
	char id[2];
	long int filesize;
	short int reserved[2];
	long int headersize;
	long int infosize;
	long int width;
	long int height;
	short int planes;
	short int bits;
	long int compression;
	long int sizeimage;
	long int xpelspermeter;
	long int ypelspermeter;
	long int colorsused;
	long int colorsimportant;
} bmpfile;

/* Standard Quake palette.  Can this thing change per Quake file?  I haven't
   seen it done, but the possibility exists. */

static unsigned char QuakePalette[] = {
	0x00,0x00,0x00,0x0F,0x0F,0x0F,0x1F,0x1F,0x1F,0x2F,0x2F,0x2F,0x3F,0x3F,0x3F,0x4B,
	0x4B,0x4B,0x5B,0x5B,0x5B,0x6B,0x6B,0x6B,0x7B,0x7B,0x7B,0x8B,0x8B,0x8B,0x9B,0x9B,
	0x9B,0xAB,0xAB,0xAB,0xBB,0xBB,0xBB,0xCB,0xCB,0xCB,0xDB,0xDB,0xDB,0xEB,0xEB,0xEB,
	0x0F,0x0B,0x07,0x17,0x0F,0x0B,0x1F,0x17,0x0B,0x27,0x1B,0x0F,0x2F,0x23,0x13,0x37,
	0x2B,0x17,0x3F,0x2F,0x17,0x4B,0x37,0x1B,0x53,0x3B,0x1B,0x5B,0x43,0x1F,0x63,0x4B,
	0x1F,0x6B,0x53,0x1F,0x73,0x57,0x1F,0x7B,0x5F,0x23,0x83,0x67,0x23,0x8F,0x6F,0x23,
	0x0B,0x0B,0x0F,0x13,0x13,0x1B,0x1B,0x1B,0x27,0x27,0x27,0x33,0x2F,0x2F,0x3F,0x37,
	0x37,0x4B,0x3F,0x3F,0x57,0x47,0x47,0x67,0x4F,0x4F,0x73,0x5B,0x5B,0x7F,0x63,0x63,
	0x8B,0x6B,0x6B,0x97,0x73,0x73,0xA3,0x7B,0x7B,0xAF,0x83,0x83,0xBB,0x8B,0x8B,0xCB,
	0x00,0x00,0x00,0x07,0x07,0x00,0x0B,0x0B,0x00,0x13,0x13,0x00,0x1B,0x1B,0x00,0x23,
	0x23,0x00,0x2B,0x2B,0x07,0x2F,0x2F,0x07,0x37,0x37,0x07,0x3F,0x3F,0x07,0x47,0x47,
	0x07,0x4B,0x4B,0x0B,0x53,0x53,0x0B,0x5B,0x5B,0x0B,0x63,0x63,0x0B,0x6B,0x6B,0x0F,
	0x07,0x00,0x00,0x0F,0x00,0x00,0x17,0x00,0x00,0x1F,0x00,0x00,0x27,0x00,0x00,0x2F,
	0x00,0x00,0x37,0x00,0x00,0x3F,0x00,0x00,0x47,0x00,0x00,0x4F,0x00,0x00,0x57,0x00,
	0x00,0x5F,0x00,0x00,0x67,0x00,0x00,0x6F,0x00,0x00,0x77,0x00,0x00,0x7F,0x00,0x00,
	0x13,0x13,0x00,0x1B,0x1B,0x00,0x23,0x23,0x00,0x2F,0x2B,0x00,0x37,0x2F,0x00,0x43,
	0x37,0x00,0x4B,0x3B,0x07,0x57,0x43,0x07,0x5F,0x47,0x07,0x6B,0x4B,0x0B,0x77,0x53,
	0x0F,0x83,0x57,0x13,0x8B,0x5B,0x13,0x97,0x5F,0x1B,0xA3,0x63,0x1F,0xAF,0x67,0x23,
	0x23,0x13,0x07,0x2F,0x17,0x0B,0x3B,0x1F,0x0F,0x4B,0x23,0x13,0x57,0x2B,0x17,0x63,
	0x2F,0x1F,0x73,0x37,0x23,0x7F,0x3B,0x2B,0x8F,0x43,0x33,0x9F,0x4F,0x33,0xAF,0x63,
	0x2F,0xBF,0x77,0x2F,0xCF,0x8F,0x2B,0xDF,0xAB,0x27,0xEF,0xCB,0x1F,0xFF,0xF3,0x1B,
	0x0B,0x07,0x00,0x1B,0x13,0x00,0x2B,0x23,0x0F,0x37,0x2B,0x13,0x47,0x33,0x1B,0x53,
	0x37,0x23,0x63,0x3F,0x2B,0x6F,0x47,0x33,0x7F,0x53,0x3F,0x8B,0x5F,0x47,0x9B,0x6B,
	0x53,0xA7,0x7B,0x5F,0xB7,0x87,0x6B,0xC3,0x93,0x7B,0xD3,0xA3,0x8B,0xE3,0xB3,0x97,
	0xAB,0x8B,0xA3,0x9F,0x7F,0x97,0x93,0x73,0x87,0x8B,0x67,0x7B,0x7F,0x5B,0x6F,0x77,
	0x53,0x63,0x6B,0x4B,0x57,0x5F,0x3F,0x4B,0x57,0x37,0x43,0x4B,0x2F,0x37,0x43,0x27,
	0x2F,0x37,0x1F,0x23,0x2B,0x17,0x1B,0x23,0x13,0x13,0x17,0x0B,0x0B,0x0F,0x07,0x07,
	0xBB,0x73,0x9F,0xAF,0x6B,0x8F,0xA3,0x5F,0x83,0x97,0x57,0x77,0x8B,0x4F,0x6B,0x7F,
	0x4B,0x5F,0x73,0x43,0x53,0x6B,0x3B,0x4B,0x5F,0x33,0x3F,0x53,0x2B,0x37,0x47,0x23,
	0x2B,0x3B,0x1F,0x23,0x2F,0x17,0x1B,0x23,0x13,0x13,0x17,0x0B,0x0B,0x0F,0x07,0x07,
	0xDB,0xC3,0xBB,0xCB,0xB3,0xA7,0xBF,0xA3,0x9B,0xAF,0x97,0x8B,0xA3,0x87,0x7B,0x97,
	0x7B,0x6F,0x87,0x6F,0x5F,0x7B,0x63,0x53,0x6B,0x57,0x47,0x5F,0x4B,0x3B,0x53,0x3F,
	0x33,0x43,0x33,0x27,0x37,0x2B,0x1F,0x27,0x1F,0x17,0x1B,0x13,0x0F,0x0F,0x0B,0x07,
	0x6F,0x83,0x7B,0x67,0x7B,0x6F,0x5F,0x73,0x67,0x57,0x6B,0x5F,0x4F,0x63,0x57,0x47,
	0x5B,0x4F,0x3F,0x53,0x47,0x37,0x4B,0x3F,0x2F,0x43,0x37,0x2B,0x3B,0x2F,0x23,0x33,
	0x27,0x1F,0x2B,0x1F,0x17,0x23,0x17,0x0F,0x1B,0x13,0x0B,0x13,0x0B,0x07,0x0B,0x07,
	0xFF,0xF3,0x1B,0xEF,0xDF,0x17,0xDB,0xCB,0x13,0xCB,0xB7,0x0F,0xBB,0xA7,0x0F,0xAB,
	0x97,0x0B,0x9B,0x83,0x07,0x8B,0x73,0x07,0x7B,0x63,0x07,0x6B,0x53,0x00,0x5B,0x47,
	0x00,0x4B,0x37,0x00,0x3B,0x2B,0x00,0x2B,0x1F,0x00,0x1B,0x0F,0x00,0x0B,0x07,0x00,
	0x00,0x00,0xFF,0x0B,0x0B,0xEF,0x13,0x13,0xDF,0x1B,0x1B,0xCF,0x23,0x23,0xBF,0x2B,
	0x2B,0xAF,0x2F,0x2F,0x9F,0x2F,0x2F,0x8F,0x2F,0x2F,0x7F,0x2F,0x2F,0x6F,0x2F,0x2F,
	0x5F,0x2B,0x2B,0x4F,0x23,0x23,0x3F,0x1B,0x1B,0x2F,0x13,0x13,0x1F,0x0B,0x0B,0x0F,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC7,0xC3,0x37,0xE7,0xE3,0x57,0x7F,0xBF,0xFF,0xAB,
	0xE7,0xFF,0xD7,0xFF,0xFF,0x67,0x00,0x00,0x8B,0x00,0x00,0xB3,0x00,0x00,0xD7,0x00,
	0x00,0xFF,0x00,0x00,0xFF,0xF3,0x93,0xFF,0xF7,0xC7,0xFF,0xFF,0xFF,0x9F,0x5B,0x53,
};

void PicList(void);
void PicExtract(void);

static void writebmp(FILE *out, long int width, long int height);
static void extract(char *name, char *size);

/*
**  PicList.  Dump the pictures.
*/

void PicList(void)
{
	long int i, j;

	if (header.id != 0x17)
		Error("Not a valid .BSP file");

	/* Go to the picture table */

        fseek(fi, header.pictures, SEEK_SET);

	/* Get the picture count */

	fread(&i, 4, 1, fi);
	printf("Pictures: %ld\n", i);

	/* Read in the pointers */

        for (j = 0; j < i; j++) {
		fread(picptrs+j, 4, 1, fi);
		picptrs[j] += header.pictures;
	}

	/* And dump their data */

	for (j = 0; j < i; j++) {
		fseek(fi, picptrs[j], SEEK_SET);
		fread(&picture, 40, 1, fi);

                printf("%3ld. %-16s ", j, picture.name);

                printf("\t%4ldx%-4ld ", picture.width, picture.height);

		printf("%08lX ", picture.scale1pos + picptrs[j]);
		printf("%08lX ", picture.scale2pos + picptrs[j]);
		printf("%08lX ", picture.scale4pos + picptrs[j]);
		printf("%08lX ", picture.scale8pos + picptrs[j]);

                printf("\n");
        }
}

/*
**  PicXtract.	Top-level function for extracting by wildcard filename.
*/

void PicXtract(int argnum, char **argv)
{
	if (header.id != 0x17)
		Error("Not a valid .BSP file");

        extract(argv[argnum+1], argv[argnum+2]);
}

/*
**  PicXtract2.  Top-level function for extracting all pictures.
*/

void PicXtract2(void)
{
	if (header.id != 0x17)
		Error("Not a valid .BSP file");

        extract("*", "*");
}

/*
**  extract.  Actual extraction function.  Iterates down the list
**	one by one and rips out those that match.
*/

static void extract(char *name, char *size)
{
	long int i, j, width, height;
	FILE *fp;
	long int cur, cur2, total;
        char filename[1024];

        fseek(fi, header.pictures, SEEK_SET);

	fread(&total, 4, 1, fi);

	for (j = 0; j < total; j++) {
		fread(picptrs+j, 4, 1, fi);
		picptrs[j] += header.pictures;
	}

	/* Start at the top and check all of 'em */
	for (cur = 0; cur < total; cur++) {

                fseek(fi, picptrs[cur], SEEK_SET);
		fread(&picture, 40, 1, fi);

		if (verbose) printf("Attempting to match picture #%ld: %s\n", cur, picture.name);

                /* If the filename matches the wildcards... */
                if (MatchName(name, picture.name)) {

                        /* ...do its separate sub-files */
                        for (cur2 = 1; cur2 <= 8; cur2 <<= 1) {

				/* Only extract those that match the size */

                                sprintf(filename, "%ld", cur2);

				if (verbose) printf("Attempting to match size %s\n", filename);

                                if (MatchName(size, filename)) {

					strcpy(filename, picture.name);
					strcat(filename, ".bm");

					printf("Extracting %s: normally %ldx%ld, this is ",
						picture.name, picture.width, picture.height);

					/* Select the picture's dimensions */

                                        switch (cur2) {
					case 1: printf("%ldx%ld", width = picture.width, height = picture.height);
						strcat(filename, "p");
						i = picture.scale1pos;
						break;
					case 2: printf("%ldx%ld", width = picture.width/2, height = picture.height/2);
						strcat(filename, "2");
						i = picture.scale2pos;
						break;
					case 4: printf("%ldx%ld", width = picture.width/4, height = picture.height/4);
						strcat(filename, "4");
						i = picture.scale4pos;
						break;
					case 8: printf("%ldx%ld", width = picture.width/8, height = picture.height/8);
						strcat(filename, "8");
						i = picture.scale8pos;
						break;
					default: printf("invalid");
						break;
					}
					printf("\n");

					fseek(fi, i + picptrs[cur], SEEK_SET);

					/* Clean up that nasty little asterisk before the animated ones */

					if (filename[0] == '*') filename[0] = '_';

					/* Write out a .BMP.  Other file formats would be good additions */

					if (verbose) printf("  Creating file...\n");

					if ((fp = fopen(filename, "wb")) == NULL)
						Error("Unable to get permission to write files.");
                                        writebmp(fp, width, height);

					if (verbose) printf("  Closing file...\n");

                                        fclose(fp);
				}
                        }
                }
        }
}

/*
**  writebmp.  This writes out a .BMP file, architecture-independent.
**	Don't ask how the hell it works; look in a graphics manual.
*/

static void writebmp(FILE *out, long int width, long int height)
{
	long int i, j;
	long int size = width * height;
	unsigned char *temp;
	long int readsize;
	unsigned char **list;

        bmpfile.id[0] = 'B';
	bmpfile.id[1] = 'M';
	bmpfile.infosize = 0x28;
	bmpfile.width = width;
	bmpfile.height = height;
	bmpfile.planes = 1;
	bmpfile.compression = 0;
	bmpfile.bits = 8;
	bmpfile.xpelspermeter = 300;
	bmpfile.ypelspermeter = 300;
	bmpfile.colorsused = 256;
	bmpfile.colorsimportant = 256;

	bmpfile.filesize = size + sizeof(bmpfile) + 1022L;
	bmpfile.headersize = sizeof(bmpfile) + 1022L;
	bmpfile.sizeimage = size;

	if (verbose) printf("  Writing BMP file header...\n");

        fwrite(((char *)(&bmpfile))+2, sizeof(bmpfile)-2, 1L, out);

	if (verbose) printf("  Writing palette...\n");

	for (i = 0L; i < 256; i++) {
		fputc(QuakePalette[i*3 + 2], out);
		fputc(QuakePalette[i*3 + 1], out);
		fputc(QuakePalette[i*3 + 0], out);
		fputc(0, out);
        }

	if (verbose) printf("  Allocating memory...\n");

	list = Qmalloc(sizeof(char *) * height);

	if (verbose) printf("  Constructing image...\n");

	for (i = 0L; i < height; i++) {
		list[i] = Qmalloc(width);
		fread(list[i], 1, width, fi);
	}

	if (verbose) printf("  Writing image...\n");

	for (i = height - 1; i >= 0; i--) {
		fwrite(list[i], 1, width, out);
		Qfree(list[i]);
        }

	if (verbose) printf("  Releasing memory...\n");

	Qfree(list);
}

