//-----------------------------------------------------------------------------
//
//  $Logfile:: /Source/r_image.c                                              $
// $Revision:: 3                                                              $
//   $Author:: Burgermac                                                      $
//     $Date:: 8/8/01 4:42 PM                                                 $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
//

#include "r_local.h"

#define MAX_RIMAGES 1024
#define SKIN_BUFFER_PREPAD ( 128 * 3 )

image_t r_images[MAX_RIMAGES];
int numr_images;

void R_ImageList_f(void);

unsigned char *d_16to8table;
static char palette_file[256];
int palette_sequence=0;

/*
===============
R_ImageList_f
===============
*/
void	R_ImageList_f (void)
{
	int i;
	image_t *image;
	int texels;

	ri.Con_Printf (PRINT_ALL, "------------------\n");
	texels = 0;
	image=r_images;
	for (i=0; i<numr_images; i++) {
		if (image->registration_sequence > 0) {
			texels += image->width*image->height;
			switch (image->type) {
			case it_skin:
				ri.Con_Printf (PRINT_ALL, "M");
				break;
			case it_sprite:
				ri.Con_Printf (PRINT_ALL, "S");
				break;
			case it_wall:
				ri.Con_Printf (PRINT_ALL, "W");
				break;
			case it_pic:
				ri.Con_Printf (PRINT_ALL, "P");
				break;
			default:
				ri.Con_Printf (PRINT_ALL, " ");
				break;
			}
			ri.Con_Printf (PRINT_ALL,  " %3i %3i : %s\n", image->width, image->height, image->name);
		}
		++image;
	}
	ri.Con_Printf (PRINT_ALL, "Total texel count: %i\n", texels);
}


/*
=================================================================

PCX LOADING

=================================================================
*/

/*
==============
LoadPCX
==============
*/
void LoadPCX (const char *filename, byte **pic, byte **palette, int *width, int *height)
{
	void *buf;
	byte *raw;
	pcx_t *pcx;
	int x, y;
	int len;
	int dataByte, runLength;
	byte *out, *pix;

	*pic = NULL;

	//
	// load the file
	//
	len = ri.FS_LoadFile (filename, &buf);
	if (!buf) {
		ri.Con_Printf (PRINT_DEVELOPER, "Bad pcx file %s\n", filename);
		return;
	}

	//
	// parse the PCX file
	//
	pcx = (pcx_t *)buf;

	pcx->xmin = LittleShort(pcx->xmin);
	pcx->ymin = LittleShort(pcx->ymin);
	pcx->xmax = LittleShort(pcx->xmax);
	pcx->ymax = LittleShort(pcx->ymax);
	pcx->hres = LittleShort(pcx->hres);
	pcx->vres = LittleShort(pcx->vres);
	pcx->bytes_per_line = LittleShort(pcx->bytes_per_line);
	pcx->palette_type = LittleShort(pcx->palette_type);

	if (pcx->manufacturer != 0x0a
		|| pcx->version != 5
		|| pcx->encoding != 1
		|| pcx->bits_per_pixel != 8
		|| pcx->xmax >= 640
		|| pcx->ymax >= 480)
	{
		ri.Con_Printf (PRINT_ALL, "Bad pcx file %s\n", filename);
		ri.FS_FreeFile (buf);
		return;
	}

	raw = &pcx->data;

	out = (byte *) malloc((pcx->ymax + 1) * (pcx->xmax + 1));
	*pic = out;
	pix = out;

	if (palette) {
		*palette = (byte *) malloc(768);
		memcpy(*palette, (byte *)pcx + len - 768, 768);
	}

	if (width) *width = pcx->xmax + 1;
	if (height) *height = pcx->ymax + 1;

	for (y = 0; y <= pcx->ymax; y++, pix += pcx->xmax + 1) {
		for (x = 0; x <= pcx->xmax; ) {
			dataByte = *raw++;

			if ((dataByte & 0xC0) == 0xC0) {
				runLength = dataByte & 0x3F;
				dataByte = *raw++;
			}
			else {
				runLength = 1;
			}

			while (runLength-- > 0) {
				pix[x++] = dataByte;
			}
		}
	}

	if (raw - (byte *)pcx > len) {
		ri.Con_Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename);
		free(*pic);
		*pic = NULL;
	}
	ri.FS_FreeFile (buf);
}

/*
=========================================================

TARGA LOADING

=========================================================
*/

typedef struct _TargaHeader {
	unsigned char id_length, colormap_type, image_type;
	unsigned short colormap_index, colormap_length;
	unsigned char colormap_size;
	unsigned short x_origin, y_origin, width, height;
	unsigned char pixel_size, attributes;
} TargaHeader;

/*
=============
LoadTGA
=============
*/
static qboolean LoadTGA (const char *name, byte **pic, int *width, int *height, byte **palette, qboolean *hasalpha)
{
	int columns, rows, numPixels;
	byte *pixbuf;
	int row, column;
	byte *buf_p;
	void *buffer;
	TargaHeader targa_header;
	byte *targa_rgba;
	byte *targa_palette;
	int i;

	*pic = NULL;
	*palette = NULL;
	*hasalpha = false;

	//
	// load the file
	//
	if (ri.FS_LoadFile(name, &buffer) < 0) {
		ri.Con_Printf (PRINT_DEVELOPER, "Bad tga file %s\n", name);
		return false;
	}

	buf_p = (byte *) buffer;

	targa_header.id_length = *buf_p++;
	targa_header.colormap_type = *buf_p++;
	targa_header.image_type = *buf_p++;

	targa_header.colormap_index  = LittleShort(*(short *)buf_p); buf_p += 2;
	targa_header.colormap_length = LittleShort(*(short *)buf_p); buf_p += 2;
	targa_header.colormap_size = *buf_p++ / 8;
	targa_header.x_origin = LittleShort(*(short *)buf_p); buf_p += 2;
	targa_header.y_origin = LittleShort(*(short *)buf_p); buf_p += 2;
	targa_header.width    = LittleShort(*(short *)buf_p); buf_p += 2;
	targa_header.height   = LittleShort(*(short *)buf_p); buf_p += 2;
	targa_header.pixel_size = *buf_p++;
	targa_header.attributes = *buf_p++;

	if (targa_header.image_type != 1 && targa_header.image_type != 2 && targa_header.image_type != 10) {
		ri.Con_Printf (PRINT_DEVELOPER, "LoadTGA: Only type 1, 2 and 10 targa RGB images supported\n");
		return false;
	}

	if ((targa_header.colormap_type != 0 && targa_header.colormap_type != 1) ||
	    (targa_header.pixel_size != 32 && targa_header.pixel_size != 24 && targa_header.pixel_size != 8)) {
		ri.Con_Printf (PRINT_DEVELOPER, "LoadTGA: Only 24, 32 and colormap images supported (no colormaps)\n");
		return false;
	}

	columns = targa_header.width;
	rows = targa_header.height;
	if (width) *width = columns;
	if (height) *height = rows;

	numPixels = columns * rows;
	if (targa_header.image_type != 1)
		numPixels *= 4;
	targa_rgba = (byte *) malloc(numPixels);
	*pic = targa_rgba;

	if (targa_header.id_length != 0)
		buf_p += targa_header.id_length; // skip TARGA image comment

	if (targa_header.image_type == 1) {
		targa_palette = (byte *) malloc(256*4);
		*palette = targa_palette;
		for (i = 0; i < targa_header.colormap_length; i++) {
			byte blue  = *buf_p++;
			byte green = *buf_p++;
			byte red   = *buf_p++;
			byte alpha = 0xff;

			if (targa_header.colormap_size == 4) {
				alpha = *buf_p++;
				if (alpha != 0xff)
					*hasalpha = true;
			}
			targa_palette[i*4+0] = red;
			targa_palette[i*4+1] = green;
			targa_palette[i*4+2] = blue;
			targa_palette[i*4+3] = alpha;
		}
		for (row = rows - 1; row >= 0; row--) {
			pixbuf = targa_rgba + row*columns;
			for (column = 0; column < columns; column++) {
				switch (targa_header.pixel_size) {
				case 8:
					*pixbuf++ = *buf_p++;
					break;
				}
			}
		}
	}
	else if (targa_header.image_type == 2) { // Uncompressed, RGB images
		for (row = rows - 1; row >= 0; row--) {
			pixbuf = targa_rgba + row*columns*4;
			for (column = 0; column < columns; column++) {
				unsigned char red,green,blue,alphabyte;
				switch (targa_header.pixel_size) {
				case 24:
					blue = *buf_p++;
					green = *buf_p++;
					red = *buf_p++;
					*pixbuf++ = red;
					*pixbuf++ = green;
					*pixbuf++ = blue;
					*pixbuf++ = 255;
					break;
				case 32:
					blue = *buf_p++;
					green = *buf_p++;
					red = *buf_p++;
					alphabyte = *buf_p++;
					*pixbuf++ = red;
					*pixbuf++ = green;
					*pixbuf++ = blue;
					*pixbuf++ = alphabyte;
					if (alphabyte != 0xff)
						*hasalpha = true;
					break;
				}
			}
		}
	}
	else if (targa_header.image_type == 10) { // Runlength encoded RGB images
		unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
		for (row = rows - 1; row >= 0; row--) {
			pixbuf = targa_rgba + row*columns*4;
			for (column = 0; column < columns; ) {
				packetHeader= *buf_p++;
				packetSize = 1 + (packetHeader & 0x7f);
				if (packetHeader & 0x80) { // run-length packet
					switch (targa_header.pixel_size) {
					case 24:
						blue = *buf_p++;
						green = *buf_p++;
						red = *buf_p++;
						alphabyte = 255;
						break;
					case 32:
						blue = *buf_p++;
						green = *buf_p++;
						red = *buf_p++;
						alphabyte = *buf_p++;
						if (alphabyte != 0xff)
							*hasalpha = true;
						break;
					default: /* silence compiler */
						blue = red = green = alphabyte = 0;
						break;
					}

					for (j = 0; j < packetSize; j++) {
						*pixbuf++ = red;
						*pixbuf++ = green;
						*pixbuf++ = blue;
						*pixbuf++ = alphabyte;
						column++;
						if (column == columns) { // run spans across rows
							column = 0;
							if (row > 0) row--;
							else goto breakOut;
							pixbuf = targa_rgba + row*columns*4;
						}
					}
				}
				else { // non run-length packet
					for (j = 0; j < packetSize; j++) {
						switch (targa_header.pixel_size) {
						case 24:
							blue = *buf_p++;
							green = *buf_p++;
							red = *buf_p++;
							*pixbuf++ = red;
							*pixbuf++ = green;
							*pixbuf++ = blue;
							*pixbuf++ = 255;
							break;
						case 32:
							blue = *buf_p++;
							green = *buf_p++;
							red = *buf_p++;
							alphabyte = *buf_p++;
							*pixbuf++ = red;
							*pixbuf++ = green;
							*pixbuf++ = blue;
							*pixbuf++ = alphabyte;
							if (alphabyte != 0xff)
								*hasalpha = true;
							break;
						}
						column++;
						if (column == columns) { // pixel packet run spans across rows
							column = 0;
							if (row > 0) row--;
							else goto breakOut;
							pixbuf = targa_rgba + row*columns*4;
						}
					}
				}
			}
breakOut:   ;
		}
	}

	ri.FS_FreeFile (buffer);
	return true;
}

//=======================================================

static image_t *R_FindFreeImage (void)
{
	image_t *image;
	int i;

	// find a free image_t
	image=r_images;
	for (i=0; i<numr_images; i++) {
		if (!image->registration_sequence) {
			break;
		}
		++image;
	}
	if (i == numr_images) {
		if (numr_images == MAX_RIMAGES) {
			ri.Sys_Error (ERR_DROP, "MAX_RIMAGES");
		}
		numr_images++;
	}
	return image;
}

/*
================
R2_LoadPic

================
*/
static image_t *R2_LoadPic (const char *name, byte *pic, int width, int height, imagetype_t type)
{
	image_t *image;
	int i, c, b;

	image = R_FindFreeImage ();
	if (strlen(name) >= sizeof(image->name))
		ri.Sys_Error (ERR_DROP, "Draw_LoadPic: \"%s\" is too long", name);
	strcpy (image->name, name);
	image->registration_sequence = registration_sequence;
	image->palette_sequence = palette_sequence;

	image->width = width;
	image->height = height;
	image->type = type;

	c = width*height;
	image->pixels[0] = (byte *) malloc(c);
	image->transparent = false;

	for (i=0; i<c; i++)
	{
		b = pic[i];
		if (b == 255)
			image->transparent = true;
		image->pixels[0][i] = b;
	}

	return image;
}

/*
================
R_Get8bitPixels
================
*/
static void R_Get8bitPixels(image_t * image)
{
	unsigned short *psrc;
	int i, c;

	if (!image) {
		ri.Sys_Error (ERR_DROP, "R_Get8bitPixels: image was null.\n");
	}
	if (image->pixels_8bit)
		return;
	image->pixels_8bit = (byte *) malloc(image->width * image->height);
	if (!image->pixels_8bit) {
		ri.Sys_Error (ERR_DROP, "R_Get8bitPixels: could not allocate 8bitpixels.\n");
	}
	c = image->width*image->height;
	psrc = (unsigned short *)image->pixels[0];
	for (i = 0; i < c; i++)
	{
		int pix;
		unsigned int r, g, b, col;

		pix = *psrc++;

		if (pix == TRANSPARENT_COLOR16)
		{
			image->pixels_8bit[i] = 255;
		}
		else
		{
			r = UNPACKRED16BIT(pix);
			g = UNPACKGREEN16BIT(pix);
			b = UNPACKBLUE16BIT(pix);

			r = (r >> 3) & 31;
			g = (g >> 2) & 63;
			b = (b >> 3) & 31;
			col = r | (g << 5) | (b << 11);
			image->pixels_8bit[i] = d_16to8table[col];
		}
	}
}


/*
================
R_CalculateColormap
================
*/
static void R_CalculateColormap(image_t * image)
{
	int l,c;
	unsigned short *newpal;
	byte *refpal;
	int red,green,blue;
	float frac;

	if (!image) {
		ri.Sys_Error (ERR_DROP, "R_CalculateColormap: image was null.\n");
	}
	if (!image->colormap16)
		return;

	if (!image->palette) {
		R_Get8bitPixels(image);
	}
	//
	// Create colormap
	//
	for (l=0; l<NUMLIGHTMAPLEVELS; l++) {
		frac = 1.0f - (float)l/(NUMLIGHTMAPLEVELS-1);

		if (image->palette) {
			refpal = image->palette;
		} else {
			refpal = d_8to24table;
		}

		newpal = image->colormap16 + l * 256;
		c = 256;
		do {
			red   = refpal[0];
			green = refpal[1];
			blue  = refpal[2];

			red   = ((float)red   * frac + 0.5);
			green = ((float)green * frac + 0.5);
			blue  = ((float)blue  * frac + 0.5);

			red = sw_state.gammatable[red];
			green = sw_state.gammatable[green];
			blue = sw_state.gammatable[blue];

			*newpal++ = COLOR16BIT(red, green, blue);

			refpal += 4;
		} while(--c);
	}
}


/*
================
R_GetColormap
================
*/
void R_GetColormap(image_t * image)
{
	if (!image) {
		ri.Sys_Error (ERR_DROP, "R_GetColormap: image was null.\n");
	}
	if (image->colormap16)
		return;
	image->colormap16 = (unsigned short *) malloc(NUMLIGHTMAPLEVELS << 9);
	if (!image->colormap16) {
		ri.Sys_Error (ERR_DROP, "R_GetColormap: could not allocate colormap16.\n");
	}
	R_CalculateColormap(image);
}

/*
===============
R_GammaChanged
===============
*/
void	R_GammaChanged (void)
{
	int i;
	image_t *image;
	image=r_images;
	for (i=0; i<numr_images; i++) {
		if (image->registration_sequence) {
			R_CalculateColormap(image);
		}
		++image;
	}
}

/*
================
R_LoadTGA
================
*/
static image_t *R_LoadTGA (const char *name, byte *pic, byte * palette, int width, int height, imagetype_t type, qboolean has_alpha)
{
	image_t *image;
	int i, c;

	image = R_FindFreeImage ();
	if (strlen(name) >= sizeof(image->name))
		ri.Sys_Error (ERR_DROP, "Draw_LoadPic: \"%s\" is too long", name);
	strcpy (image->name, name);
	image->registration_sequence = registration_sequence;
	image->palette_sequence = palette_sequence;

	image->width = width;
	image->height = height;
	image->type = type;

	c = width*height;
	image->pixels[0] = (byte *) malloc(c);
	image->transparent = has_alpha;

	if (vid.bitdepth <= 8)
	{
		if (palette)
		{
			for (i = 0; i < c; i++)
			{
				int index;
				unsigned int r, g, b, a, col;
				index = pic[i];
				r = (palette[4*index + 0] >> 3) & 31;
				g = (palette[4*index + 1] >> 2) & 63;
				b = (palette[4*index + 2] >> 3) & 31;
				a = palette[4*index + 3];
				if ((a < 128) || (r==31 && g==0 && b==31))
				{
					image->pixels[0][i] = 255;
					image->transparent = true;
				}
				else
				{
					col = r | (g << 5) | (b << 11);
					image->pixels[0][i] = d_16to8table[col];
				}
			}
		}
		else
		{
			for (i = 0; i < c; i++)
			{
				unsigned int r, g, b, a, col;
				r = (pic[4*i + 0] >> 3) & 31;
				g = (pic[4*i + 1] >> 2) & 63;
				b = (pic[4*i + 2] >> 3) & 31;
				col = r | (g << 5) | (b << 11);
				a = pic[4*i + 3];
				if ((a < 128) || (r==31 && g==0 && b==31))
				{
					image->pixels[0][i] = 255;
					image->transparent = true;
				}
				else
					image->pixels[0][i] = d_16to8table[col];
			}
		}
	}
	else
	{
		if (palette)
		{
			if (type == it_pic || type == it_sky)
			{
				unsigned short * ptr;
				free(image->pixels[0]);
				c = width*height;
				image->pixels[0] = (byte *) malloc (c<<1);
				ptr = (unsigned short *)image->pixels[0];
				for (i = 0; i < c; i++)
				{
					int index;
					unsigned int r, g, b, a;
					index = pic[i];
					r = palette[4*index + 0];
					g = palette[4*index + 1];
					b = palette[4*index + 2];
					a = palette[4*index + 3];
					if (a < 128)
						*ptr++ = COLOR16BIT(255,0,255);
					else
						*ptr++ = COLOR16BIT(r,g,b);
				}
			}
			else
			{
				image->palette = (byte *) malloc(1024);
				if (!image->palette) {
					ri.Sys_Error (ERR_DROP, "R_LoadTGA: Failed on memory allocation of 1024 bytes");
				}
				memcpy(image->palette, palette, 1024);
				for (i = 0; i < c; i++)
				{
					int index;
					unsigned int r, g, b, a;
					index = pic[i];
					r = palette[4*index + 0];
					g = palette[4*index + 1];
					b = palette[4*index + 2];
					a = palette[4*index + 3];
					if ((a < 128) || (r==255 && g==0 && b==255))
					{
						image->pixels[0][i] = 255;
						image->transparent = true;
					}
					//else if (index == 255)
					//	image->pixels[0][i] = 0;
					else
						image->pixels[0][i] = index;
				}
			}
		}
		else
		{
			unsigned short * ptr;
			if (type == it_skin || type == it_sprite)
			{
				//ri.Con_Printf (PRINT_DEVELOPER, "R_LoadTGA: %s is 16-bit, it needs to be paletted, converting to 8-bit.\n", name);
				image->palette = (byte *) malloc(1024);
				if (!image->palette) {
					ri.Sys_Error (ERR_DROP, "R_LoadTGA: Failed on memory allocation of 1024 bytes");
				}
				memcpy(image->palette, d_8to24table, 1024);
				c = width*height;
				for (i = 0; i < c; i++)
				{
					unsigned int r, g, b, a, col;
					r = (pic[4*i + 0] >> 3) & 31;
					g = (pic[4*i + 1] >> 2) & 63;
					b = (pic[4*i + 2] >> 3) & 31;
					col = r | (g << 5) | (b << 11);
					a = pic[4*i + 3];
					if ((a < 128) || (r==31 && g==0 && b==31))
					{
						image->pixels[0][i] = 255;
						image->transparent = true;
					}
					else
						image->pixels[0][i] = d_16to8table[col];
				}
				return image;
			}
			free(image->pixels[0]);
			c = width*height;
			image->pixels[0] = (byte *) malloc(c<<1);
			ptr = (unsigned short *)image->pixels[0];
			for (i = 0; i < c; i++) {
				unsigned int r, g, b, a;
				r = pic[4*i + 0];
				g = pic[4*i + 1];
				b = pic[4*i + 2];
				a = pic[4*i + 3];
				if (a < 128) {
					*ptr++ = COLOR16BIT(255,0,255);
				} else {
					*ptr++ = COLOR16BIT(r,g,b);
				}
			}
		}
	}

	if (vid.bitdepth > 8 && type == it_skin) {
		R_GetColormap(image);
	}
	return image;
}

/*
================
R_LoadWal
================
*/
static image_t *R_LoadWal (const char *name)
{
	void *buf;
	miptex_t    *mt;
	int ofs;
	image_t     *image;
	int size;

	ri.FS_LoadFile (name, &buf);
	mt = (miptex_t *) buf;
	if (!mt)
	{
		ri.Con_Printf (PRINT_ALL, "R_LoadWal: can't load %s\n", name);
		return r_notexture_mip;
	}

	image = R_FindFreeImage ();
	strcpy (image->name, name);
	image->width = LittleLong (mt->width);
	image->height = LittleLong (mt->height);
	image->type = it_wall;
	image->registration_sequence = registration_sequence;
	image->palette_sequence = palette_sequence;

	size = image->width*image->height * (256+64+16+4)/256;
	image->pixels[0] = (byte *) malloc(size);
	image->pixels[1] = image->pixels[0] + image->width*image->height;
	image->pixels[2] = image->pixels[1] + image->width*image->height/4;
	image->pixels[3] = image->pixels[2] + image->width*image->height/16;

	ofs = LittleLong (mt->offsets[0]);
	memcpy (image->pixels[0], (byte *)mt + ofs, size);

	ri.FS_FreeFile (buf);

	return image;
}

/*
================
R_LoadSWL
================
*/
static image_t *R_LoadSWL (const char *name)
{
	void *buf;
	sinmiptex_t *mt;
	int ofs;
	image_t     *image;
	int size;
	int i;
	byte        *src;
	byte        *dest;
	byte        *pal;
	unsigned int r, g, b, /*a,*/ c;

	ri.FS_LoadFile (name, &buf);
	mt = (sinmiptex_t *) buf;
	if (!mt)
	{
		ri.Con_Printf (PRINT_ALL, "R_LoadSWL: can't load %s\n", name);
		return r_notexture_mip;
	}

	image = R_FindFreeImage ();
	strcpy (image->name, name);
	image->width = LittleLong (mt->width);
	image->height = LittleLong (mt->height);
	image->type = it_wall;
	image->registration_sequence = registration_sequence;
	image->palette_sequence = palette_sequence;

	size = image->width*image->height * (256+64+16+4)/256;
	image->pixels[0] = (byte *) malloc(size);
	image->pixels[1] = image->pixels[0] + image->width*image->height;
	image->pixels[2] = image->pixels[1] + image->width*image->height/4;
	image->pixels[3] = image->pixels[2] + image->width*image->height/16;
	image->palette = (byte *) malloc (1024);
	if (!image->palette) {
		ri.Sys_Error (ERR_DROP, "R_LoadSWL: Failed on memory allocation of 1024 byte");
	}
	memcpy(image->palette, mt->palette, 1024);

	ofs = LittleLong (mt->offsets[0]);

	if (vid.bitdepth <= 8)
	{
		src = (byte *)mt + ofs;
		dest = image->pixels[0];
		pal = mt->palette;
		for (i = 0; i < size; i++)
		{
			c = src[i] * 4;

			r = pal[c + 0];
			g = pal[c + 1];
			b = pal[c + 2];
			/*a = pal[c + 3];*/

			if (r==255 && g==0 && b==255)
			{
				dest[i] = 255;
				image->transparent = true;
			}
			else
			{
				r = (r >> 3) & 31;
				g = (g >> 2) & 63;
				b = (b >> 3) & 31;

				c = r | (g << 5) | (b << 11);

				dest[i] = d_16to8table[c];
			}
		}
	}
	else
	{
		src = (byte *)mt + ofs;
		dest = image->pixels[0];
		pal = mt->palette;
		memcpy(dest, src, size);

		//
		// check palette for masked color
		//
		for (i = 0; i < 256; i++)
		{
			c = i * 4;

			r = pal[c + 0];
			g = pal[c + 1];
			b = pal[c + 2];
			/*a = pal[c + 3];*/

			if (r==255 && g==0 && b==255)
			{
				image->masked_color = i;
				image->palette[c + 0] = 0;
				image->palette[c + 2] = 0;
				image->transparent = true;
			}
		}
	}

	ri.FS_FreeFile (buf);

	return image;
}

static void R_FixupSkin(image_t * image)
{
	int length, newlength;
	byte * data;

	length = image->width * image->height;
	newlength = length + (2 * SKIN_BUFFER_PREPAD);

	data = image->pixels[0];

	image->pixels[0] = (byte*) calloc(1, newlength);
	image->pixels[0] += SKIN_BUFFER_PREPAD;
	memcpy(image->pixels[0], data, length);

	free(data);
}

/*
===============
R_FindImage

Finds or loads the given image
===============
*/
image_t	*R_FindImage (const char *name, imagetype_t type, qboolean force)
{
	image_t *image;
	int i, len;
	byte    *pic, *palette;
	int width, height;

	if (!name) {
		return NULL; // ri.Sys_Error(ERR_DROP, "R_FindImage: NULL name");
	}
	len = (int)strlen(name);
	if (len<5) {
		return NULL; // ri.Sys_Error(ERR_DROP, "R_FindImage: bad name: %s", name);
	}
	// look for it
	image = r_images;
	for (i=0; i<numr_images; i++)
	{
		if (!Q_stricmp(name, image->name))
		{
			if (force  || (image->palette_sequence != palette_sequence))
			{	// need to reload due to palette or force
				image->registration_sequence = 0;
				break;
			}
			image->registration_sequence = registration_sequence;
			return image;
		}
		++image;
	}

	//
	// load the pic from disk
	//
	pic = NULL;
	palette = NULL;
	if (!Q_stricmp(name+len-4, ".pcx"))
	{
		LoadPCX (name, &pic, &palette, &width, &height);
		if (!pic)
			return NULL; // ri.Sys_Error(ERR_DROP, "R_FindImage: can't load %s", name);
		image = R2_LoadPic (name, pic, width, height, type);

		if (type == it_skin || type == it_sprite)
			R_FixupSkin(image);
	}
	else if (!Q_stricmp(name+len-4, ".wal"))
	{
		image = R_LoadWal (name);
	}
	else if (!Q_stricmp(name+len-4, ".swl"))
	{
		// Check to see if .swl file exists, if it doesn't
		// then fall back to a .wal
		image = r_notexture_mip;
		if (ri.FS_LoadFile(name, NULL) == -1)
		{
			char temp[128];
			strcpy(temp, name);
			temp[len-4] = 0;
			strcat(temp, ".wal");
			// Check to see if .wal exists, if it doesn't then error.
			if (ri.FS_LoadFile (temp, NULL) == -1)
			{
				temp[len-4] = 0;
				ri.Con_Printf (PRINT_ALL, "R_LoadPic: can't load %s\n", name);
			}
			else
			{
				image = R_LoadWal (temp);
			}
		}
		else
		{
			image = R_LoadSWL (name);
		}
	}
	else if (!Q_stricmp(name+len-4, ".tga"))
	{
		qboolean has_alpha;
		LoadTGA (name, &pic, &width, &height, &palette, &has_alpha);
		if (!pic)
			return NULL; // ri.Sys_Error(ERR_DROP, "R_FindImage: can't load %s", name);
		image = R_LoadTGA (name, pic, palette, width, height, type, has_alpha);

		if (type == it_skin || type == it_sprite)
			R_FixupSkin(image);
	}
	else
		return NULL; // ri.Sys_Error(ERR_DROP, "R_FindImage: bad extension on: %s", name);

	free(pic);
	free(palette);

	return image;
}

/*
===============
R_RegisterSkin
===============
*/
struct image_s *R_RegisterSkin (const char *name)
{
	return R_FindImage (name, it_skin, false);
}

/*
================
R_FreeUnusedImages

Any image that was not touched on this registration sequence
will be freed.
================
*/
void R_FreeUnusedImages (void)
{
	int i;
	image_t *image;

	Draw_InitLocal();

	image=r_images;
	for (i=0; i<numr_images; i++, image++) {
		if (image->registration_sequence == registration_sequence) {
			Com_PageInMemory ((byte *)image->pixels[0], image->width*image->height);
			continue; // used this sequence
		}
		if (!image->registration_sequence)
			continue; // free texture
		if (image->type == it_none)
			continue;
		free(image->pixels_8bit);
		image->pixels_8bit = NULL;
		if (image->type == it_pic)
			continue; // don't free pics
		if (image->type == it_skin || image->type == it_sprite) {
			// subtract out our buffer
			image->pixels[0] -= SKIN_BUFFER_PREPAD;
		}
		free(image->pixels[0]);
		free(image->palette);
		free(image->colormap16);
		memset (image, 0, sizeof(*image));
		image->type = it_none;
	}
	//
	// make sure we have our environment maps loaded in
	//
	r_envmap  = R_FindImage ("env/envmap.tga", it_skin, false);
	r_goldenvmap  = R_FindImage ("env/goldenvmap.tga", it_skin, false);
	// 2015 code
	r_gogglesmap  = R_FindImage ("env/gogglesmap.tga", it_skin, false);
	if(!r_gogglesmap) // just incase it can't find the proper image
		r_gogglesmap  = R_FindImage ("env/envmap.tga", it_skin, false);
}

/*
===============
R_LoadPalFile
===============
*/
void R_LoadPalFile(const char * name)
{
	void * buf;
	byte * pal;
	int i, size;
	unsigned *pal32;

	//
	// see if we need to load a palette in
	//
	if (!Q_stricmp(name, palette_file))
	{
		ri.Con_Printf(PRINT_DEVELOPER, "R_LoadPalFile: already loaded %s\n", name);
		return;
	}
	strcpy(palette_file, name);
	palette_sequence++;
	ri.FS_LoadFile(name, &buf);
	if (!buf)
	{
		ri.Con_Printf(PRINT_DEVELOPER, "R_LoadPalFile: couldn't load %s\n", name);
		ri.FS_LoadFile("pics/default.pal", &buf);
		if (!buf)
		{
			ri.Sys_Error(ERR_FATAL, "Couldn't load pics/default.pal\n");
		}

		strcpy(palette_file, "pics/default.pal");
	}

	//
	// load colormap
	//
	free(vid.colormap);
	size = (64+256) * 256;
	vid.colormap = (byte *) malloc (size);
	if (!vid.colormap) {
		ri.Sys_Error(ERR_FATAL, "Couldn't allocate vid.colormap\n");
	}
	memcpy(vid.colormap, buf, size);
	vid.alphamap = vid.colormap + 64*256;

	//
	// load 16to8 table
	//
	free(d_16to8table);
	d_16to8table = (byte *) malloc(65536);
	if (!d_16to8table) {
		ri.Sys_Error(ERR_FATAL, "Couldn't allocate d_16to8table\n");
	}
	memcpy(d_16to8table, (byte *)buf + size, 65536);
	//
	// load palette
	//
	size += 65536;
	pal = (byte *)buf + size;
	for (i=0, pal32=(unsigned*)d_8to24table; i<256; i++)
	{
		int r, g, b;
		unsigned v;
		r = pal[i*3+0];
		g = pal[i*3+1];
		b = pal[i*3+2];
#ifdef macintosh /* or all big endian? */
		v = (r<<24) + (g<<16) + (b<<8);     //	CJ: MacOS uses RGB not xBGR
#else
		v = (r<<0) + (g<<8) + (b<<16);
#endif
		pal32[i] = v;
	}
	if (vid.bitdepth == 8) {
		image_t * image;
		image = r_images;
		for (i=0; i<numr_images; i++)
		{
			image->palette_sequence = 0;
			++image;
		}
	}
	ri.Con_Printf(PRINT_DEVELOPER, "R_LoadPalFile: loaded %s\n", palette_file);
	ri.FS_FreeFile (buf);
}

/*
================
R_FreeImage

================
*/
static void R_FreeImage(image_t * image)
{
	if (image->type == it_none)
		return;
	free(image->pixels_8bit);
	image->pixels_8bit = NULL;
	if (image->type == it_skin || image->type == it_sprite) {
		// subtract out our buffer
		image->pixels[0] -= SKIN_BUFFER_PREPAD;
	}
	free(image->pixels[0]);
	free(image->palette);
	free(image->colormap16);
	memset(image, 0, sizeof(*image));
	image->type = it_none;
}

/*
===============
R_LoadPalFile_f
===============
*/
void R_LoadPalFile_f(void)
{
	int i;
	image_t *image;
	char texturename[128];
	int type;
	char palfile[1024];

	if (ri.Cmd_Argc() < 2)
	{
		ri.Con_Printf(PRINT_ALL, "usage: loadpal <palfilename>\n");
		return;
	}
	strcpy(palfile, ri.Cmd_Argv(1));
	R_LoadPalFile(palfile);
	R_GammaCorrectAndSetPalette(d_8to24table);
	image=r_images;
	for (i=0; i<numr_images; i++) {
		strcpy(texturename, image->name);
		type = image->type;
		R_FreeImage(image);
		R_FindImage(texturename, (imagetype_t)type, false);
		++image;
	}
}

/*
===============
R_InitImages
===============
*/
void R_InitImages (void)
{
	registration_sequence = 1;
	palette_sequence = 1;
	R_LoadPalFile("pics/default.pal");
	R_GammaCorrectAndSetPalette(d_8to24table);
	r_envmap  = R_FindImage ("env/envmap.tga", it_skin, false);
	r_goldenvmap  = R_FindImage ("env/goldenvmap.tga", it_skin, false);

	// 2015 code
	r_gogglesmap  = R_FindImage ("env/gogglesmap.tga", it_skin, false);
	if(!r_gogglesmap) // just incase it can't find the proper image
		r_gogglesmap  = R_FindImage ("env/envmap.tga", it_skin, false);
}

/*
===============
R_ShutdownImages
===============
*/
void R_ShutdownImages (void)
{
	int i;
	image_t *image;

	image = r_images;
	for (i=0; i<numr_images; i++) {
		R_FreeImage(image);
		++image;
	}
	numr_images = 0;
}
