/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// gl_draw.c -- this is the only file outside the refresh that touches the vid buffer

#include "quakedef.h"

extern	unsigned d_8to24table2[256];

extern cvar_t vid_conwidth;
extern cvar_t vid_conheight;
extern cvar_t con_textsize;

int	texture_extension_number = 1;

cvar_t	gl_picmip = {"gl_picmip", "0"};
cvar_t	gl_conalpha = {"gl_conalpha", "0.8"};	// by joe

cvar_t	gl_texCompression	= {"gl_texCompression", "0", true}; //rww

qboolean OnChange_Conback_Image (cvar_t *var, char *string);
cvar_t	gl_conback_image = {"gl_conback_image", "gfx/conback", false, false, OnChange_Conback_Image};

qboolean OnChange_gl_max_size (cvar_t *var, char *string);
cvar_t	gl_max_size = {"gl_max_size", "1024", false, false, OnChange_gl_max_size};

qboolean OnChange_gl_texturemode (cvar_t *var, char *string);
cvar_t	gl_texturemode = {"gl_texturemode", "GL_NEAREST", false, false, OnChange_gl_texturemode};
cvar_t	gl_mipmap_lightmaps = {"gl_mipmap_lightmaps", "0", true};//By default lightmaps are filtered to GL_LINEAR_MIPMAP_NEAREST, toggling this forces lightmaps to synch to gl_texturemode

cvar_t	gl_externaltextures_world	= {"gl_externaltextures_world"	, "1",true};
cvar_t	gl_externaltextures_bmodels = {"gl_externaltextures_bmodels", "1",true};
cvar_t	gl_externaltextures_models	= {"gl_externaltextures_models"	, "1",true};

qboolean OnChange_gl_crosshairimage (cvar_t *var, char *string);
cvar_t	gl_crosshairimage = {"crosshairimage", "crosshair1", true, false, OnChange_gl_crosshairimage};

qboolean OnChange_gl_consolefont (cvar_t *var, char *string);
cvar_t	gl_consolefont = {"gl_consolefont", "charset-3", true, false, OnChange_gl_consolefont};

cvar_t	gl_crosshairalpha = {"crosshairalpha", "1", true};
cvar_t	crosshair_static = {"crosshair_static", "1", true};

qboolean OnChange_gl_smoothfont (cvar_t *var, char *string);
cvar_t gl_smoothfont			= {"gl_smoothfont", "0", true, false, OnChange_gl_smoothfont};

cvar_t	gl_free_world_textures	= {"gl_free_world_textures","1",true};//R00k

cvar_t	gl_greyscale = {"gl_greyscale","0", false, false};//internal for mods...

static qboolean no24bit, no24bitWad;

extern char last_mapname[MAX_QPATH];
extern mpic_t *last_lvlshot;

#define	NUMCROSSHAIRS	5

byte	*draw_chars;			// 8*8 graphic characters
mpic_t	*draw_disc;
mpic_t	*draw_backtile;

mpic_t	conback_data;
mpic_t	*conback = &conback_data;
mpic_t	crosshairpic;

int		translate_texture;
int		char_texture;
int		crosshairtextures[NUMCROSSHAIRS];
int		sceneblur_texture;//Spike

extern	cvar_t	crosshair, cl_crossx, cl_crossy, crosshaircolor, crosshairsize;

static qboolean	crosshairimage_loaded = false;

int GL_LoadPicTexture (char *name, mpic_t *pic, byte *data);

int		gl_max_size_default;		
#ifndef USEFAKEGL
int		gl_lightmap_format = GL_RGBA8, gl_solid_format = GL_RGBA8, gl_alpha_format = GL_RGBA8;
#else
int		gl_lightmap_format = GL_RGBA, gl_solid_format = GL_RGBA, gl_alpha_format = GL_RGBA;
#endif

int	gl_filter_min = GL_LINEAR_MIPMAP_NEAREST;
int	gl_filter_max = GL_LINEAR;

extern	byte	vid_gamma_table[256];
extern	float	vid_gamma;

static byte crosshairdata[NUMCROSSHAIRS][64] = 
{
	0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
	0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff,
	0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xfe, 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, 0xfe, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xfe, 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, 0xff, 0xfe, 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,

	0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
	0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff,
	0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff,
	0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff,
	0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,

	0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 
	0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 
	0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

gltexture_t	gltextures[MAX_GLTEXTURES];
int		numgltextures;

int		currenttexture = -1;		// to avoid unnecessary texture sets

extern qboolean gl_filtering_anisotropic;

void GL_Bind (int texnum)
{
	if (currenttexture == texnum)
		return;

	if (gl_filtering_anisotropic)
	{
		 if (gl_anisotropic.value > 1)
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropic.value); // R00k
		 else
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
	}

	currenttexture = texnum;
	glBindTexture (GL_TEXTURE_2D, texnum);
}

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

  scrap allocation

  Allocate all the little status bar objects into a single texture
  to crutch up stupid hardware / drivers

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

// some cards have low quality of alpha pics, so load the pics
// without transparent pixels into a different scrap block.
// scrap 0 is solid pics, 1 is transparent

#define	MAX_SCRAPS			2
#define	SCRAP_BLOCK_WIDTH	256
#define	SCRAP_BLOCK_HEIGHT	256

int		scrap_allocated[MAX_SCRAPS][SCRAP_BLOCK_WIDTH];
byte	scrap_texels[MAX_SCRAPS][SCRAP_BLOCK_WIDTH*SCRAP_BLOCK_HEIGHT];
int		scrap_dirty = 0;	// bit mask
int		scrap_texnum;

// returns false if allocation failed
qboolean Scrap_AllocBlock (int scrapnum, int w, int h, int *x, int *y)
{
	int	i, j, best, best2;

	best = SCRAP_BLOCK_HEIGHT;

	for (i=0 ; i < SCRAP_BLOCK_WIDTH - w ; i++)
	{
		best2 = 0;
		
		for (j=0 ; j<w ; j++)
		{
			if (scrap_allocated[scrapnum][i+j] >= best)
				break;
			if (scrap_allocated[scrapnum][i+j] > best2)
				best2 = scrap_allocated[scrapnum][i+j];
		}
		if (j == w)
		{	// this is a valid spot
			*x = i;
			*y = best = best2;
		}
	}

	if (best + h > SCRAP_BLOCK_HEIGHT)
		return false;

	for (i=0 ; i<w ; i++)
		scrap_allocated[scrapnum][*x+i] = best + h;

	scrap_dirty |= (1 << scrapnum);

	return true;
}

int	scrap_uploads;

void Scrap_Upload (void)
{
	int	i;

	scrap_uploads++;
	for (i=0 ; i<MAX_SCRAPS ; i++)
	{
		if (!(scrap_dirty & (1 << i)))
			continue;
		scrap_dirty &= ~(1 << i);
		GL_Bind (scrap_texnum + i);
		GL_Upload8 (scrap_texels[i], SCRAP_BLOCK_WIDTH, SCRAP_BLOCK_HEIGHT, TEX_ALPHA);
	}
}

//=============================================================================
/* Support Routines */

typedef struct cachepic_s
{
	char	name[MAX_QPATH];
	mpic_t	pic;
} cachepic_t;

#define	MAX_CACHED_PICS		128
cachepic_t	cachepics[MAX_CACHED_PICS];
int		numcachepics;

byte	menuplyr_pixels[4096];

int		pic_texels;
int		pic_count;

mpic_t *Draw_PicFromWad (char *name)
{
	qpic_t	*p;
	mpic_t	*pic, *pic_24bit;
	char vname[MAX_QPATH]= "";

	p = W_GetLumpName (name);
	pic = (mpic_t *)p;

	//Check in gamedir first, then /id1 ...
	if ((no24bitWad == false) && (no24bit == false))
	{			
		Q_snprintfz (vname, sizeof(vname),"%s/textures/wad/%s",com_gamedir, name);
		if (pic_24bit = GL_LoadPicImage(vname, name, 0, 0, TEX_ALPHA|TEX_MIPMAP))		
		{
			memcpy (&pic->texnum, &pic_24bit->texnum, sizeof(mpic_t) - 8);
			return pic;
		}

		Q_snprintfz (vname, sizeof(vname),"%s/gfx/%s",com_gamedir, name);
		if (pic_24bit = GL_LoadPicImage(vname, name, 0, 0, TEX_ALPHA|TEX_MIPMAP))
		{
			memcpy (&pic->texnum, &pic_24bit->texnum, sizeof(mpic_t) - 8);
			return pic;
		}

		if ((pic_24bit = GL_LoadPicImage(va("textures/wad/%s", name), name, 0, 0, TEX_ALPHA|TEX_MIPMAP)) || (pic_24bit = GL_LoadPicImage(va("gfx/%s",name), name, 0, 0, TEX_ALPHA|TEX_MIPMAP)))		
		{
			memcpy (&pic->texnum, &pic_24bit->texnum, sizeof(mpic_t) - 8);
			return pic;
		}
	}
	// load little ones into the scrap
	if (p->width < 64 && p->height < 64)
	{
		int	x, y, i, j, k, texnum;

		texnum = memchr (p->data, 255, p->width * p->height) != NULL;
		if (!Scrap_AllocBlock (texnum, p->width, p->height, &x, &y))
		{
			GL_LoadPicTexture (name, pic, p->data);
			return pic;
		}
		k = 0;
		for (i=0 ; i<p->height ; i++)
			for (j=0 ; j<p->width ; j++, k++)
				scrap_texels[texnum][(y+i)*SCRAP_BLOCK_WIDTH+x+j] = p->data[k];
		texnum += scrap_texnum;
		pic->texnum = texnum;
		pic->sl = (x + 0.25) / (float)SCRAP_BLOCK_WIDTH;
		pic->sh = (x + p->width - 0.25) / (float)SCRAP_BLOCK_WIDTH;
		pic->tl = (y + 0.25) / (float)SCRAP_BLOCK_WIDTH;
		pic->th = (y + p->height - 0.25) / (float)SCRAP_BLOCK_WIDTH;

		pic_count++;
		pic_texels += p->width * p->height;
	}
	else
	{
		GL_LoadPicTexture (name, pic, p->data);
	}

	return pic;
}

/*
================
Draw_CachePic
================
*/
mpic_t *Draw_CachePic (char *path)
{
	int		i;
	cachepic_t	*pic;
	qpic_t		*dat;
	mpic_t		*pic_24bit;
	
	for (pic = cachepics, i = 0 ; i < numcachepics ; pic++, i++)
		if (!strcmp(path, pic->name))
			return &pic->pic;

	if (numcachepics == MAX_CACHED_PICS)
		Sys_Error ("numcachepics == MAX_CACHED_PICS");
	numcachepics++;
	Q_strncpyz (pic->name, path, sizeof(pic->name));

	// load the pic from disk
	if (!(dat = (qpic_t *)COM_LoadTempFile(path)))
		Sys_Error ("Draw_CachePic: failed to load %s", path);
	SwapPic (dat);

	// HACK HACK HACK --- we need to keep the bytes for
	// the translatable player picture just for the menu
	// configuration dialog
	if (!strcmp(path, "gfx/menuplyr.lmp"))
		memcpy (menuplyr_pixels, dat->data, dat->width * dat->height);

	pic->pic.width = dat->width;
	pic->pic.height = dat->height;

	if ((pic_24bit = GL_LoadPicImage(path, NULL, 0, 0, TEX_ALPHA|TEX_MIPMAP)))
		memcpy (&pic->pic.texnum, &pic_24bit->texnum, sizeof(mpic_t) - 8);
	else
		GL_LoadPicTexture (path, &pic->pic, dat->data);

	return &pic->pic;
}

void Draw_CharToConback (int num, byte *dest)
{
	int	row, col, drawline, x;
	byte	*source;

	row = num >> 4;
	col = num & 15;
	source = draw_chars + (row<<10) + (col<<3);

	drawline = 8;

	while (drawline--)
	{
		for (x=0 ; x<8 ; x++)
			if (source[x] != 255)
				dest[x] = 0x60 + source[x];
		source += 128;
		dest += 320;
	}
}

qboolean OnChange_Conback_Image (cvar_t *var, char *string)
{
	mpic_t	*pic_24bit;

	if (strlen(string) < 1)
		return false;

	if (pic_24bit = GL_LoadPicImage(string, "conback", 0, 0, TEX_MIPMAP))
	{
		memcpy (&conback->texnum, &pic_24bit->texnum, sizeof(mpic_t) - 8);
	}
	else
	{
		Con_Printf ("Couldn't load conback image \"%s\".\n", string);
		return false;
	}

	conback->width = vid.conwidth;
	conback->height = vid.conheight;
	return true;
}

void Draw_InitConback (void)
{
	qpic_t	*cb;
	int		start;
	mpic_t	*pic_24bit;

	start = Hunk_LowMark ();

	if (!(cb = (qpic_t *)COM_LoadHunkFile("gfx/conback.lmp")))
		Sys_Error ("Couldn't load gfx/conback.lmp");

	SwapPic (cb);

//	if (cb->width != 320 || cb->height != 200)
//		Sys_Error ("Draw_InitConback: conback.lmp size is not 320x200");

	if (pic_24bit = GL_LoadPicImage(gl_conback_image.string, "conback", 0, 0, TEX_MIPMAP))
	{
		memcpy (&conback->texnum, &pic_24bit->texnum, sizeof(mpic_t) - 8);
	}
	else
	{
		conback->width = cb->width;
		conback->height = cb->height;
		GL_LoadPicTexture ("conback", conback, cb->data);
	}

	conback->width = vid.conwidth;
	conback->height = vid.conheight;

	// free loaded console
	Hunk_FreeToLowMark (start);
}

qboolean OnChange_gl_max_size (cvar_t *var, char *string)
{
	int	i;
	float	newval = Q_atof(string);

	if (newval > gl_max_size_default)
	{
		Con_Printf ("Your hardware doesn't support texture sizes bigger than %dx%d\n", gl_max_size_default, gl_max_size_default);
		return true;
	}

	Q_ROUND_POWER2(newval, i);

	if (i != newval)
	{
		Con_Printf ("Valid values for %s are powers of 2 only\n", var->name);
		return true;
	}

	return false;
}

typedef struct
{
	char	*name;
	int	minimize, maximize;
} glmode_t;

glmode_t modes[] = 
{
	{"GL_NEAREST", GL_NEAREST, GL_NEAREST},
	{"GL_LINEAR", GL_LINEAR, GL_LINEAR},
	{"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
	{"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
	{"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
	{"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR},
	{"GL_LINEAR_MIPMAP_LINEAR_MAG_NEAREST", GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST},
	{"GL_NEAREST_MIPMAP_LINEAR_MAG_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR}
};

#define GLMODE_NUMODES	(sizeof(modes) / sizeof(glmode_t))

qboolean OnChange_gl_texturemode (cvar_t *var, char *string)
{
	int		i;
	gltexture_t	*glt;
	extern void GL_ReBuildLightmaps (void);

	for (i=0 ; i<GLMODE_NUMODES ; i++)
	{
		if (!Q_strcasecmp (modes[i].name, string))
			break;
	}

	if (i == GLMODE_NUMODES)
	{
		Con_Printf ("bad filter name: %s\n", string);
		return true;
	}

	gl_filter_min = modes[i].minimize;
	gl_filter_max = modes[i].maximize;

	// change all the existing mipmap texture objects
	for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
	{
		if (glt->texmode & TEX_MIPMAP)
		{
			GL_Bind (glt->texnum);
			glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
			glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
		}
	}

	if (gl_mipmap_lightmaps.value == 1)
		GL_ReBuildLightmaps ();//R00k: (v2.011.3492|fixed) added so lightmaps can be mipmapped!

	return false;
}

static int Draw_LoadCharset (char *name)
{
	int	texnum;

	if ((!Q_strcasecmp(name, "original"))||(!Q_strcasecmp(name, "")))
	{
		int	i;
		char	buf[128*256], *src, *dest;

		// Convert the 128*128 conchars texture to 128*256 leaving empty space between 
		// rows so that chars don't stumble on each other because of texture smoothing.
		// This hack costs us 64K of GL texture memory
		memset (buf, 255, sizeof(buf));
		src = draw_chars;
		dest = buf;
		for (i=0 ; i<16 ; i++)
		{
			memcpy (dest, src, 128*8);
			src += 128*8;
			dest += 128*8*2;
		}

		char_texture = GL_LoadTexture ("pic:charset", 128, 256, buf, TEX_ALPHA, 1);
		goto done;
	}

	if ((texnum = GL_LoadCharsetImage(va("textures/charsets/%s", name), "pic:charset")))
	{
		char_texture = texnum;
		goto done;
	}

	Con_DPrintf (1,"Couldn't load charset \"%s\"\n", name);

	return 1;

done:
	if (!gl_smoothfont.value)
	{
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	}
	else
	{
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	}
	

	return 0;
}

qboolean OnChange_gl_consolefont (cvar_t *var, char *string)
{
	return Draw_LoadCharset (string);
}

void Draw_LoadCharset_f (void)
{
	switch (Cmd_Argc())
	{
	case 1:
		Con_Printf ("Current charset is \"%s\"\n", gl_consolefont.string);
		break;

	case 2:
		Cvar_Set ("gl_consolefont", Cmd_Argv(1));
		break;

	default:
		Con_Printf ("Usage: %s <loadcharset>\n", Cmd_Argv(0));
	}
}

void Draw_InitCharset (void)
{
	int	i;

	draw_chars = W_GetLumpName ("conchars");

	for (i=0 ; i<256*64 ; i++)
		if (draw_chars[i] == 0)
			draw_chars[i] = 255;	// proper transparent color

	Draw_LoadCharset (gl_consolefont.string);

	if (!char_texture)
		Cvar_Set ("gl_consolefont", "original");

	if (!char_texture)	
		Sys_Error ("Draw_InitCharset: Couldn't load charset");
}

qboolean OnChange_gl_smoothfont (cvar_t *var, char *string)
{
	float	newval;

	newval = Q_atof (string);
	if (!newval == !gl_smoothfont.value || !char_texture)
		return false;

	GL_Bind (char_texture);

	if (newval)
	{
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	}
	else
	{
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	}

	return false;
}

qboolean OnChange_gl_crosshairimage (cvar_t *var, char *string)
{
	mpic_t	*pic;

	if (!string[0])
	{
		crosshairimage_loaded = false;
		return false;
	}

	if (!(pic = GL_LoadPicImage(va("crosshairs/%s", string), "crosshair", 0, 0, TEX_ALPHA|TEX_MIPMAP)))
	{
		crosshairimage_loaded = false;
		Con_Printf ("Couldn't load crosshairimage %s\n", string);

		return false;
	}

	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//Cvar this filter... gl_mipmap_2dgfx
	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	crosshairpic = *pic;
	crosshairimage_loaded = true;

	return false;
}

qboolean OnChange_con_textsize (cvar_t *var, char *string)
{
	float	f;

	f = bound(2, (atoi(string)), 16);
	Cvar_SetValue("con_textsize", f);
	return true;
}
/*
===============
Draw_Init
===============
*/
extern void R_InitCoronas ();
void Draw_Init (void)
{
	int	i;

	if (!host_initialized) 
	{
		Cmd_AddCommand ("loadcharset", Draw_LoadCharset_f);

		Cvar_RegisterVariable (&gl_max_size);
		Cvar_RegisterVariable (&gl_picmip);
		Cvar_RegisterVariable (&gl_conalpha);
		Cvar_RegisterVariable (&gl_smoothfont);
		Cvar_RegisterVariable (&gl_consolefont);	
		Cvar_RegisterVariable (&gl_crosshairalpha);
		Cvar_RegisterVariable (&gl_crosshairimage);
		Cvar_RegisterVariable (&crosshair_static);//Entar
		Cvar_RegisterVariable (&gl_greyscale);
		Cvar_RegisterVariable (&gl_texturemode);
		Cvar_RegisterVariable (&gl_mipmap_lightmaps);
		Cvar_RegisterVariable (&gl_externaltextures_world);
		Cvar_RegisterVariable (&gl_externaltextures_bmodels);
		Cvar_RegisterVariable (&gl_externaltextures_models);
		Cvar_RegisterVariable (&gl_texCompression); //rww
		Cvar_RegisterVariable (&gl_free_world_textures);
		Cvar_RegisterVariable (&gl_conback_image);//R00k
	}

	glGetIntegerv (GL_MAX_TEXTURE_SIZE, &gl_max_size_default);
	Cvar_SetValue ("gl_max_size", gl_max_size_default);

	no24bit = COM_CheckParm("-no24bit") ? true : false;
	no24bitWad = COM_CheckParm("-no24bitwad") ? true : false;

	// 3dfx can only handle 256 wide textures
	if (!Q_strncasecmp((char *)gl_renderer, "3dfx", 4) || strstr((char *)gl_renderer, "Glide"))
		Cvar_Set ("gl_max_size", "256");

	// load the console background and the charset by hand, because we need to write the version
	// string into the background before turning it into a texture
	Draw_InitCharset ();
	Draw_InitConback ();

	// save a texture slot for translated picture
	translate_texture = texture_extension_number++;

	// save slots for scraps
	scrap_texnum = texture_extension_number;
	texture_extension_number += MAX_SCRAPS;

	sceneblur_texture = texture_extension_number++; //Spike
		
	// Load the crosshair pics
	for (i=0 ; i<NUMCROSSHAIRS ; i++)
	{
		crosshairtextures[i] = GL_LoadTexture ("", 8, 8, crosshairdata[i], TEX_ALPHA, 1);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	}

	// get the other pics we need
	draw_disc = Draw_PicFromWad ("disc");//This is used for the PENT icon on the sbar.
	draw_backtile = Draw_PicFromWad ("backtile");
	R_InitCoronas(); 
}


void Draw_Alpha_Start (float alpha)
{
	if (alpha < 1.0f)
	{
		glEnable (GL_BLEND);
		glColor4f (1,1,1, alpha);
		glDisable (GL_ALPHA_TEST);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	}
}

void Draw_Alpha_End (void)
{
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glEnable (GL_ALPHA_TEST);
	glDisable (GL_BLEND);
	glColor4f (1,1,1,1);
}

/*
================
Draw_Character

Draws one 8*8 graphics character with 0 being transparent.
It can be clipped to the top of the screen to allow the console to be
smoothly scrolled off.
================
*/
void Draw_Character (int x, int y, int num, qboolean scale)
{
	int		row, col;
	float	frow, fcol,size;

	if (y <= -8)
		return;			// totally off screen

	if (num == 32)
		return;		// space

	num &= 255;
	
	row = num >> 4;
	col = num & 15;

	frow = row * 0.0625;
	fcol = col * 0.0625;

	GL_Bind (char_texture);

	if (scale)
	{
		x *= (con_textsize.value/8);
	//	y *= (con_textsize.value/8);//not used, precalc'ed in console.c
		size = con_textsize.value;
	}
	else
		size = 8;

	glBegin (GL_QUADS);

	glTexCoord2f (fcol,				frow);						
	glVertex2f (x,	y);
	
	glTexCoord2f (fcol + 0.0625,	frow);						
	glVertex2f (x+size,y);
	
	glTexCoord2f (fcol + 0.0625,	frow + 0.03125);			
	glVertex2f (x+size,y+size);
	
	glTexCoord2f (fcol,				frow + 0.03125);			
	glVertex2f (x,y+size);
	 
	glEnd ();
}

/*
================
Draw_String
================
*/

void Draw_String (int x, int y, char *str, int scale)
{
	float	frow, fcol, size;
	int		num;

	if (y <= -8)
		return;			// totally off screen

	if (!*str)
		return;


	if (scale > 1)
	{
		x *= (con_textsize.value/scale);
//		y *= (con_textsize.value/8);
		size = con_textsize.value;
	}
	else
		size = 8;

	GL_Bind (char_texture);

	glBegin (GL_QUADS);

	while (*str)		// stop rendering when out of characters
	{
		if ((num = *str++) != (32))	// skip spaces
		{
			frow = (float)(num >> 4)*0.0625;
			fcol = (float)(num & 15)*0.0625;
			glTexCoord2f (fcol, frow);						glVertex2f (x, y);
			glTexCoord2f (fcol + 0.0625, frow);				glVertex2f (x+size, y);
			glTexCoord2f (fcol + 0.0625, frow + 0.03125);	glVertex2f (x+size, y+size);
			glTexCoord2f (fcol, frow + 0.03125);			glVertex2f (x, y+size);		
		}
		x += size;
	}
	 
	glEnd ();
}

void Draw_String_Scaled (int x, int y, char *str, float scale)
{
	float	frow, fcol;
	int		num;
	float scale8 = scale * 8;
	float scale8_2 = scale8 * 2;
	
	if (!*str)
		return;

	glDepthMask(GL_FALSE);
	glEnable(GL_BLEND);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glShadeModel(GL_SMOOTH);

	GL_Bind (char_texture);

	glBegin (GL_QUADS);

	while (*str)		// stop rendering when out of characters
	{
		if ((num = *str++) != (32))	// skip spaces
		{
			frow = (float)(num >> 4)*0.0625;
			fcol = (float)(num & 15)*0.0625;

			// Top left.
			glTexCoord2f (fcol, frow);
			glVertex2f (x, y);

			// Top right.
			glTexCoord2f(fcol + 0.0625, frow);
			glVertex2f(x + scale8, y);

			// Bottom right.
			glTexCoord2f(fcol + 0.0625, frow + 0.0625);
			glVertex2f(x + scale8, y + scale8_2);

			// Bottom left.
			glTexCoord2f(fcol, frow + 0.0625);
			glVertex2f(x, y + scale8_2);

		}
		x += scale8;
	}	 

	glEnd ();

	glEnable(GL_TEXTURE_2D);
	glDepthMask(GL_TRUE);
	glDisable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glShadeModel(GL_FLAT);
}

/*
================
Draw_Alt_String
================
*/
void Draw_Alt_String (int x, int y, char *str, qboolean scale)
{
	float	frow, fcol, size;
	int		num;

	if (y <= -8)
		return;			// totally off screen

	if (!*str)
		return;

	if (scale)
	{
		x *= (con_textsize.value/8);
//		y *= (con_textsize.value/8);//Still need??
		size = con_textsize.value;
	}
	else
		size = 8;

	GL_Bind (char_texture);

	glBegin (GL_QUADS);

	while (*str)		// stop rendering when out of characters
	{
		if ((num = *str++|0x80) != (32|0x80))	// skip spaces
		{
			frow = (float)(num >> 4)*0.0625;
			fcol = (float)(num & 15)*0.0625;
			glTexCoord2f (fcol, frow);
			glVertex2f (x, y);
			glTexCoord2f (fcol + 0.0625, frow);
			glVertex2f (x+size, y);
			glTexCoord2f (fcol + 0.0625, frow + 0.03125);
			glVertex2f (x+size, y+size);
			glTexCoord2f (fcol, frow + 0.03125);
			glVertex2f (x, y+size);
		}
		x += size;
	}
	 
	glEnd ();
}

byte *StringToRGB (char *s)
{
	byte		*col;
	static	byte	rgb[4];

	Cmd_TokenizeString (s);
	if (Cmd_Argc() == 3)
	{
		rgb[0] = (byte)Q_atoi(Cmd_Argv(0));
		rgb[1] = (byte)Q_atoi(Cmd_Argv(1));
		rgb[2] = (byte)Q_atoi(Cmd_Argv(2));
	}
	else
	{
		col = (byte *)&d_8to24table[(byte)Q_atoi(s)];
		rgb[0] = col[0];
		rgb[1] = col[1];
		rgb[2] = col[2];
	}
	rgb[3] = 255;

	return rgb;
}

/*
================
Draw_Crosshair		-- joe, from FuhQuake
================
*/
extern entity_t *CL_NewTempEntity (void);
#ifndef USEFAKEGL
extern cvar_t r_bloom;
#endif
extern void ML_Project (vec3_t in, vec3_t out, vec3_t viewangles, vec3_t vieworg, float wdivh, float fovy);
void Draw_Crosshair (void)
{	
	float			ofs1, ofs2, sh, th, sl, tl, x, y;
	byte			*col;
	extern vrect_t	scr_vrect;
	extern cvar_t	v_viewheight;

	trace_t tr;
	vec3_t end, start, fwds;

	if (!(crosshair.value))	
		return;


	if (!crosshair_static.value && scr_viewsize.value >= 100 && cl.worldmodel)//dynamic xhair by Entar/LordHavoc
	{
		AngleVectors (r_refdef.viewangles,  fwds, NULL, NULL);	
		VectorCopy(cl_entities[cl.viewentity].origin, start);
		start[2] += 16;//QuakeC uses + '0 0 16' for gun aim.

		VectorMA(start, 4096, fwds, end);
	
		memset(&tr, 0, sizeof(tr));
		tr.fraction = 1;
		SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &tr);

		if (tr.fraction != 1)
		{
			start[2] += 16;//REQUIRED: Methinks the start gets reset after SV_RecursiveHullCheck....so this is required.
		
			ML_Project(tr.endpos, end, cl.viewangles, start, (float)scr_vrect.width/scr_vrect.height, r_refdef.fov_y);//Entar
			x = scr_vrect.x + scr_vrect.width/2;
			
			y = (scr_vrect.y+scr_vrect.height*(end[1]));
			y = scr_vrect.height - y; // Entar : HACKYNESS yes, but it works pretty well
			y -= (y - (scr_vrect.height / 2)) / 2;//
		}
		else
		{	
			x = scr_vrect.x + scr_vrect.width / 2 + cl_crossx.value; 
			y = scr_vrect.y + scr_vrect.height / 2 + cl_crossy.value;
		}
	}
	else
	{
			x = (scr_vrect.x + scr_vrect.width / 2 + cl_crossx.value); 
			y = (scr_vrect.y + scr_vrect.height / 2 + cl_crossy.value);
	}

	if ((crosshair.value >= 2 && crosshair.value <= NUMCROSSHAIRS + 1) || crosshairimage_loaded)
	{
		if (!gl_crosshairalpha.value)
		{
			return;
		}


		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		col = StringToRGB (crosshaircolor.string);

		if (gl_crosshairalpha.value)
		{
			glDisable (GL_ALPHA_TEST);
			glEnable (GL_BLEND);
#ifndef USEFAKEGL
			if (r_bloom.value)
				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#endif
			col[3] = bound(0, gl_crosshairalpha.value, 1) * 255;
			glColor4ubv (col);			
		}
		else
		{
			glColor3ubv (col);			
		}

		if (crosshairimage_loaded)
		{
			GL_Bind (crosshairpic.texnum);
			ofs1 = 4 - 4.0 / crosshairpic.width;
			ofs2 = 4 + 4.0 / crosshairpic.width;
			sh = crosshairpic.sh;
			sl = crosshairpic.sl;
			th = crosshairpic.th;
			tl = crosshairpic.tl;
		}
		else
		{
			GL_Bind (crosshairtextures[(int)crosshair.value-2]);
			ofs1 = 3.5;
			ofs2 = 4.5;
			tl = sl = 0;
			sh = th = 1;
		}

		ofs1 *= bound(1, crosshairsize.value, 20);
		ofs2 *= bound(1, crosshairsize.value, 20);

		glBegin (GL_QUADS);
		glTexCoord2f (sl, tl);
		glVertex2f (x - ofs1, y - ofs1);
		glTexCoord2f (sh, tl);
		glVertex2f (x + ofs2, y - ofs1);
		glTexCoord2f (sh, th);
		glVertex2f (x + ofs2, y + ofs2);
		glTexCoord2f (sl, th);
		glVertex2f (x - ofs1, y + ofs2);
		 
		glEnd ();

		if (gl_crosshairalpha.value)
		{
			glDisable (GL_BLEND);
			glEnable (GL_ALPHA_TEST);
		}

		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glColor3f (1, 1, 1);
	}
	else if (crosshair.value)
	{		
		if (gl_crosshairalpha.value)
			Draw_Alpha_Start (gl_crosshairalpha.value);
		Draw_Character (x - 4 + cl_crossx.value, y - 4 + cl_crossy.value, '+',false);		
		if (gl_crosshairalpha.value)
			Draw_Alpha_End();
	}
}


/*
================
Draw_TextBox
================
*/
void Draw_TextBox (int x, int y, int width, int lines)
{
	mpic_t	*p;
	int	cx, cy, n;

	// draw left side
	cx = x;
	cy = y;
	p = Draw_CachePic ("gfx/box_tl.lmp");
	Draw_TransPic (cx, cy, p);
	p = Draw_CachePic ("gfx/box_ml.lmp");
	for (n = 0; n < lines; n++)
	{
		cy += 8;
		Draw_TransPic (cx, cy, p);
	}
	p = Draw_CachePic ("gfx/box_bl.lmp");
	Draw_TransPic (cx, cy+8, p);

	// draw middle
	cx += 8;
	while (width > 0)
	{
		cy = y;
		p = Draw_CachePic ("gfx/box_tm.lmp");
		Draw_TransPic (cx, cy, p);
		p = Draw_CachePic ("gfx/box_mm.lmp");
		for (n = 0; n < lines; n++)
		{
			cy += 8;
			if (n == 1)
				p = Draw_CachePic ("gfx/box_mm2.lmp");
			Draw_TransPic (cx, cy, p);
		}
		p = Draw_CachePic ("gfx/box_bm.lmp");
		Draw_TransPic (cx, cy+8, p);
		width -= 2;
		cx += 16;
	}

	// draw right side
	cy = y;
	p = Draw_CachePic ("gfx/box_tr.lmp");
	Draw_TransPic (cx, cy, p);
	p = Draw_CachePic ("gfx/box_mr.lmp");
	for (n = 0; n < lines; n++)
	{
		cy += 8;
		Draw_TransPic (cx, cy, p);
	}
	p = Draw_CachePic ("gfx/box_br.lmp");
	Draw_TransPic (cx, cy+8, p);
}

/*
================
Draw_DebugChar

Draws a single character directly to the upper right corner of the screen.
This is for debugging lockups by drawing different chars in different parts
of the code.
================
*/
void Draw_DebugChar (char num)
{
}

/*
=============
Draw_AlphaPic
=============
*/
void Draw_AlphaPic (int x, int y, mpic_t *pic, float alpha)
{
	if (scrap_dirty)
		Scrap_Upload ();

	glDisable(GL_ALPHA_TEST);
	glEnable (GL_BLEND);
	// fix alpha
	glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glCullFace (GL_FRONT);
	glColor4f (1, 1, 1, alpha);
	GL_Bind (pic->texnum);
	
	glBegin (GL_QUADS);
	glTexCoord2f (pic->sl, pic->tl);
	glVertex2f (x, y);
	glTexCoord2f (pic->sh, pic->tl);
	glVertex2f (x+pic->width, y);
	glTexCoord2f (pic->sh, pic->th);
	glVertex2f (x+pic->width, y+pic->height);
	glTexCoord2f (pic->sl, pic->th);
	glVertex2f (x, y+pic->height);
	 
	glEnd ();

	glColor4f (1, 1, 1, 1);
	glEnable(GL_ALPHA_TEST);
	glDisable (GL_BLEND);
	//MHGLQFIX: fix alpha
	glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}

/*
=============
Draw_Pic
=============
*/
void Draw_Pic (int x, int y, mpic_t *pic)
{
	if (scrap_dirty)
		Scrap_Upload ();

	GL_Bind (pic->texnum);
	glBegin (GL_QUADS);
	glTexCoord2f (pic->sl, pic->tl);
	glVertex2f (x, y);
	glTexCoord2f (pic->sh, pic->tl);
	glVertex2f (x+pic->width, y);
	glTexCoord2f (pic->sh, pic->th);
	glVertex2f (x+pic->width, y+pic->height);
	glTexCoord2f (pic->sl, pic->th);
	glVertex2f (x, y+pic->height);
	 
	glEnd ();
}

void Draw_CrossHairPic (int x, int y)
{
		float	ofs1, ofs2, sh, th, sl, tl;
		byte	*col;

	if ((crosshair.value >= 2 && crosshair.value <= NUMCROSSHAIRS + 1) || crosshairimage_loaded)
	{
		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		col = StringToRGB (crosshaircolor.string);

		if (gl_crosshairalpha.value)
		{
			glDisable (GL_ALPHA_TEST);
			glEnable (GL_BLEND);
#ifndef USEFAKEGL
			if (r_bloom.value)
				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#endif
			col[3] = bound(0, gl_crosshairalpha.value, 1) * 255;
			glColor4ubv (col);
		}
		else
		{
			glColor3ubv (col);
		}

		if (crosshairimage_loaded)
		{
			GL_Bind (crosshairpic.texnum);
			ofs1 = 4 - 4.0 / crosshairpic.width;
			ofs2 = 4 + 4.0 / crosshairpic.width;
			sh = crosshairpic.sh;
			sl = crosshairpic.sl;
			th = crosshairpic.th;
			tl = crosshairpic.tl;
		}
		else
		{
			GL_Bind (crosshairtextures[(int)crosshair.value-2]);
			ofs1 = 3.5;
			ofs2 = 4.5;
			tl = sl = 0;
			sh = th = 1;
		}

		ofs1 *= (vid.width / vid.conwidth) * bound(0, crosshairsize.value, 20);
		ofs2 *= (vid.width / vid.conwidth) * bound(0, crosshairsize.value, 20);

		glBegin (GL_QUADS);
		glTexCoord2f (sl, tl);
		glVertex2f (x - ofs1, y - ofs1);
		glTexCoord2f (sh, tl);
		glVertex2f (x + ofs2, y - ofs1);
		glTexCoord2f (sh, th);
		glVertex2f (x + ofs2, y + ofs2);
		glTexCoord2f (sl, th);
		glVertex2f (x - ofs1, y + ofs2);
		 
		glEnd ();

		if (gl_crosshairalpha.value)
		{
			glDisable (GL_BLEND);
			glEnable (GL_ALPHA_TEST);
		}

		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glColor3f (1, 1, 1);
	}
	else if (crosshair.value)
	{
		Draw_Character (x - 4 + cl_crossx.value, y - 4 + cl_crossy.value, '+',false);
	}
}

void Draw_SubPic (int x, int y, mpic_t *pic, int srcx, int srcy, int width, int height)
{
	float	newsl, newtl, newsh, newth, oldglwidth, oldglheight;

	if (scrap_dirty)
		Scrap_Upload ();

	oldglwidth = pic->sh - pic->sl;
	oldglheight = pic->th - pic->tl;

	newsl = pic->sl + (srcx * oldglwidth) / pic->width;
	newsh = newsl + (width * oldglwidth) / pic->width;

	newtl = pic->tl + (srcy * oldglheight) / pic->height;
	newth = newtl + (height * oldglheight) / pic->height;

	GL_Bind (pic->texnum);
	glBegin (GL_QUADS);
	glTexCoord2f (newsl, newtl);
	glVertex2f (x, y);
	glTexCoord2f (newsh, newtl);
	glVertex2f (x+width, y);
	glTexCoord2f (newsh, newth);
	glVertex2f (x+width, y+height);
	glTexCoord2f (newsl, newth);
	glVertex2f (x, y+height);
	 
	glEnd ();
}

/*
=============
Draw_TransPic
=============
*/
void Draw_TransPic (int x, int y, mpic_t *pic)
{
	if ((x < 0 || y < 0) || ((unsigned)(x + pic->width) > vid.width || (unsigned)(y + pic->height) > vid.height))
	{
		return;
	}		
	Draw_Pic (x, y, pic);
}

/*
=============
Draw_TransPicTranslate

Only used for the player color selection menu
=============
*/
void Draw_TransPicTranslate (int x, int y, mpic_t *pic, byte *translation)
{
	int		v, u, c, p;
	unsigned	trans[64*64], *dest;
	byte		*src;

	GL_Bind (translate_texture);

	c = pic->width * pic->height;

	dest = trans;
	for (v=0 ; v<64 ; v++, dest += 64)
	{
		src = &menuplyr_pixels[((v*pic->height)>>6)*pic->width];
		for (u=0 ; u<64 ; u++)
		{
			p = src[(u*pic->width)>>6];
			dest[u] = (p == 255) ? p : d_8to24table[translation[p]];
		}
	}

	glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans);

	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//R00k: Let's let pixel art look crisp!
	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	glBegin (GL_QUADS);
	glTexCoord2f (0, 0);
	glVertex2f (x, y);
	glTexCoord2f (1, 0);
	glVertex2f (x+pic->width, y);
	glTexCoord2f (1, 1);
	glVertex2f (x+pic->width, y+pic->height);
	glTexCoord2f (0, 1);
	glVertex2f (x, y+pic->height);
	 
	glEnd ();
}

extern int logoTex;
void doLogo (int left, int top, float alpha)
{
	if (no24bit)
		return;

	glDisable(GL_ALPHA_TEST);
	glEnable (GL_BLEND);
	glCullFace (GL_FRONT);
	glColor4f (1, 1, 1, alpha);

	GL_Bind (logoTex);

	glBegin (GL_QUADS);

	glTexCoord2f (0, 0);
	glVertex2f (left, top);

	glTexCoord2f (0, 1);
	glVertex2f (left, top + 64);

	glTexCoord2f (1, 1);
	glVertex2f (left + 256, top + 64);

	glTexCoord2f (1, 0);
	glVertex2f (left + 256, top);

	 
	glEnd ();
	glColor3f (1, 1, 1);
	glEnable(GL_ALPHA_TEST);
	glDisable (GL_BLEND);
}

void Draw_MapShotBackground (char *lvlname, float alpha)
{
	if (key_dest == key_console)
			return;

	if (no24bit)
		return;

	if (no24bitWad)
		return;

	if (strncmp(lvlname, last_mapname, sizeof(last_mapname)))
	{
		char name[MAX_QPATH];

		Q_snprintfz(name, sizeof(name), "textures/levelshots/%s.jpg", lvlname);//ack! hardcoded jpg extension
			
		if (COM_FindFile(name))
		{
			if (last_lvlshot = Draw_CachePic(name)) 
			{
				last_lvlshot->width = (int)vid_conwidth.value;
				last_lvlshot->height =(int)vid_conheight.value;
			}			
		}
		Q_strncpyz(last_mapname, lvlname, sizeof(last_mapname));
	}

	if (last_lvlshot != NULL)
	{
		if (last_lvlshot->width != (int)vid_conwidth.value)//R00k re-check incase we changed resolutions
			last_lvlshot->width = (int)vid_conwidth.value;
		if (last_lvlshot->height != (int)vid_conheight.value)
			last_lvlshot->height =(int)vid_conheight.value;

		Draw_AlphaPic (0, 0, last_lvlshot, alpha);
	}
}

/*
================
Draw_ConsoleBackground
================
*/

void Draw_ConsoleBackground (int lines)
{
	char	ver[80];

	if (!gl_conalpha.value && lines < vid.height)
		goto end;

	if (lines == vid.height)
		Draw_Pic (0, 0, conback);
	else
	{
		Draw_AlphaPic (0, lines - vid.height, conback, bound(0, (gl_conalpha.value), 1));		
	}
end:
	sprintf (ver, "Qrack %s", VersionString());
	Draw_Alt_String (vid.conwidth - strlen(ver) * 8 - 8, lines - 10, ver, false);	
}

/*
=============
Draw_TileClear

This repeats a 64*64 tile graphic to fill the screen around a sized down
refresh window.
=============
*/
void Draw_TileClear (int x, int y, int w, int h)
{
	GL_Bind (draw_backtile->texnum);
	glBegin (GL_QUADS);
	glTexCoord2f (x/64.0, y/64.0);
	glVertex2f (x, y);
	glTexCoord2f ((x+w)/64.0, y/64.0);
	glVertex2f (x+w, y);
	glTexCoord2f ((x+w)/64.0, (y+h)/64.0);
	glVertex2f (x+w, y+h);
	glTexCoord2f (x/64.0, (y+h)/64.0);
	glVertex2f (x, y+h);
	 
	glEnd ();
}

void Draw_TileClearAlpha (int x, int y, int w, int h, float alpha)
{
	glDisable(GL_ALPHA_TEST);
	glEnable (GL_BLEND);
	glCullFace (GL_FRONT);
	glColor4f (1, 1, 1, alpha);
	GL_Bind (draw_backtile->texnum);
	glBegin (GL_QUADS);
	glTexCoord2f (x/64.0, y/64.0);
	glVertex2f (x, y);
	glTexCoord2f ((x+w)/64.0, y/64.0);
	glVertex2f (x+w, y);
	glTexCoord2f ((x+w)/64.0, (y+h)/64.0);
	glVertex2f (x+w, y+h);
	glTexCoord2f (x/64.0, (y+h)/64.0);
	glVertex2f (x, y+h);
	 
	glEnd ();
	glColor3f (1, 1, 1);
	glEnable(GL_ALPHA_TEST);
	glDisable (GL_BLEND);
}


/*
=============
Draw_Fill

Fills a box of pixels with a single color
=============
*/
void Draw_AlphaFill(int x, int y, int w, int h, int c, float alpha) 
{
	alpha = bound(0, alpha, 1);

	if (!alpha)
		return;

	glDisable (GL_TEXTURE_2D);
	if (alpha < 1) 
	{
		glEnable (GL_BLEND);
		glDisable(GL_ALPHA_TEST);
		glColor4f (host_basepal[c * 3] / 255.0,  host_basepal[c * 3 + 1] / 255.0, host_basepal[c * 3 + 2] / 255.0, alpha);
	} 
	else 
	{
		glColor3f (host_basepal[c * 3] / 255.0, host_basepal[c * 3 + 1] / 255.0, host_basepal[c * 3 + 2]  /255.0);
	}

	glBegin (GL_QUADS);
	glVertex2f (x, y);
	glVertex2f (x + w, y);
	glVertex2f (x + w, y + h);
	glVertex2f (x, y + h);
	 
	glEnd ();

	glEnable (GL_TEXTURE_2D);
	if (alpha < 1) 
	{
		glEnable(GL_ALPHA_TEST);
		glDisable (GL_BLEND);
	}
	glColor3f (1, 1, 1);
}

void Draw_Fill (int x, int y, int w, int h, int c)
{
	glDisable (GL_TEXTURE_2D);
	glColor3f (host_basepal[c*3] / 255.0, host_basepal[c*3+1] / 255.0, host_basepal[c*3+2] / 255.0);

	glBegin (GL_QUADS);

	glVertex2f (x, y);
	glVertex2f (x+w, y);
	glVertex2f (x+w, y+h);
	glVertex2f (x, y+h);

	
	 
	glEnd ();
	glColor3f (1, 1, 1);
	glEnable (GL_TEXTURE_2D);
}

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

/*
================
Draw_FadeScreen
================
*/
void Draw_FadeScreen (void)
{
	glEnable (GL_BLEND);
	glDisable (GL_TEXTURE_2D);
	glColor4f (0.0, 0.0, 0.0, 0.8);

	glBegin (GL_QUADS);

	glVertex2f (0,0);
	glVertex2f (vid.width, 0);
	glVertex2f (vid.width, vid.height);
	glVertex2f (0, vid.height);
	 
	glEnd ();
	glColor3f (1, 1, 1);
	glEnable (GL_TEXTURE_2D);
	glDisable (GL_BLEND);

	Sbar_Changed ();
}

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

/*
================
Draw_BeginDisc

Draws the little blue disc in the corner of the screen.
Call before beginning any disc IO.
================
*/
void Draw_BeginDisc (void)
{
	if (!draw_disc)
		return;
if (developer.value)	
	//Draw_Pic (vid.width - 24, 0, draw_disc);
	Draw_AlphaFill	(8, 8, 8, 8, 255, 0.8);
}


/*
================
Draw_EndDisc

Erases the disc icon.
Call after completing any disc IO
================
*/
void Draw_EndDisc (void)
{
	if (!draw_disc)
		return;
if (developer.value)	
	Draw_AlphaFill	(8, 8, 8, 8, 95, 0.25);
}

/*
================
GL_Set2D

Setup as if the screen was 320*200
================
*/
void GL_Set2D (void)
{
	glViewport (glx, gly, glwidth, glheight);

	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();
	glOrtho (0, vid.width, vid.height, 0, -99999, 99999);

	glMatrixMode (GL_MODELVIEW);
	glLoadIdentity ();

	glDisable (GL_DEPTH_TEST);
	glDisable (GL_CULL_FACE);

	glDisable (GL_BLEND);
	glEnable (GL_ALPHA_TEST);

	glColor4f (1, 1, 1, 1);
}

void ResampleTextureLerpLine (byte *in, byte *out, int inwidth, int outwidth)
{
	int	j, xi, oldx = 0, f, fstep, endx;

	fstep = (int) (inwidth*65536.0f/outwidth);
	endx = (inwidth - 1);
	for (j = 0, f = 0 ; j < outwidth ; j++, f += fstep)
	{
		xi = f >> 16;
		if (xi != oldx)
		{
			in += (xi - oldx) * 4;
			oldx = xi;
		}
		if (xi < endx)
		{
			int	lerp = f & 0xFFFF;

			*out++ = (byte)((((in[4] - in[0]) * lerp) >> 16) + in[0]);
			*out++ = (byte)((((in[5] - in[1]) * lerp) >> 16) + in[1]);
			*out++ = (byte)((((in[6] - in[2]) * lerp) >> 16) + in[2]);
			*out++ = (byte)((((in[7] - in[3]) * lerp) >> 16) + in[3]);
		}
		else	// last pixel of the line has no pixel to lerp to
		{
			*out++ = in[0];
			*out++ = in[1];
			*out++ = in[2];
			*out++ = in[3];
		}
	}
}

/*
================
ResampleTexture
================
*/
void ResampleTexture (unsigned *indata, int inwidth, int inheight, unsigned *outdata, int outwidth, int outheight)
{
	int	i, j, yi, oldy, f, fstep, endy = (inheight-1);
	byte	*inrow, *out, *row1, *row2, *memalloc;

	out = (byte *)outdata;
	fstep = (inheight << 16) / outheight;

	memalloc = Q_malloc (2 * (outwidth * 4));
	row1 = memalloc;
	row2 = memalloc + (outwidth * 4);
	inrow = (byte *)indata;
	oldy = 0;
	ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
	ResampleTextureLerpLine (inrow + inwidth * 4, row2, inwidth, outwidth);
	for (i = 0, f = 0 ; i < outheight ; i++, f += fstep)
	{
		yi = f >> 16;
		if (yi < endy)
		{
			int	lerp = f & 0xFFFF;

			if (yi != oldy)
			{
				inrow = (byte *)indata + inwidth * 4 * yi;
				if (yi == oldy+1)
					memcpy (row1, row2, outwidth * 4);
				else
					ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
				ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
				oldy = yi;
			}
			for (j = outwidth ; j ; j--)
			{
				out[0] = (byte)((((row2[0] - row1[0]) * lerp) >> 16) + row1[0]);
				out[1] = (byte)((((row2[1] - row1[1]) * lerp) >> 16) + row1[1]);
				out[2] = (byte)((((row2[2] - row1[2]) * lerp) >> 16) + row1[2]);
				out[3] = (byte)((((row2[3] - row1[3]) * lerp) >> 16) + row1[3]);
				out += 4;
				row1 += 4;
				row2 += 4;
			}
			row1 -= outwidth * 4;
			row2 -= outwidth * 4;
		}
		else
		{
			if (yi != oldy)
			{
				inrow = (byte *)indata + inwidth * 4 * yi;
				if (yi == oldy+1)
					memcpy (row1, row2, outwidth * 4);
				else
					ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
				oldy = yi;
			}
			memcpy (out, row1, outwidth*4);
		}
	}
	Q_free (memalloc);
}

/*
================
MipMap

Operates in place, quartering the size of the texture
================
*/
__inline void MipMap (byte *in, int *width, int *height)
{
	int	i, j, nextrow;
	byte	*out;

	nextrow = *width << 2;

	out = in;
	if (*width > 1)
	{
		*width >>= 1;
		if (*height > 1)
		{
			*height >>= 1;
			for (i = 0 ; i < *height ; i++, in += nextrow)
			{
				for (j = 0 ; j < *width ; j++, out += 4, in += 8)
				{
					out[0] = (in[0] + in[4] + in[nextrow+0] + in[nextrow+4]) >> 2;
					out[1] = (in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2;
					out[2] = (in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2;
					out[3] = (in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2;
				}
			}
		}
		else
		{
			for (i = 0 ; i < *height ; i++)
			{
				for (j = 0 ; j < *width ; j++, out += 4, in += 8)
				{
					out[0] = (in[0] + in[4]) >> 1;
					out[1] = (in[1] + in[5]) >> 1;
					out[2] = (in[2] + in[6]) >> 1;
					out[3] = (in[3] + in[7]) >> 1;
				}
			}
		}
	}
	else if (*height > 1)
	{
		*height >>= 1;
		for (i = 0 ; i < *height ; i++, in += nextrow)
		{
			for (j = 0 ; j < *width ; j++, out += 4, in += 4)
			{
				out[0] = (in[0] + in[nextrow+0]) >> 1;
				out[1] = (in[1] + in[nextrow+1]) >> 1;
				out[2] = (in[2] + in[nextrow+2]) >> 1;
				out[3] = (in[3] + in[nextrow+3]) >> 1;
			}
		}
	}
}

static void ScaleDimensions(int width, int height, int *scaled_width, int *scaled_height, int mode) 
{
	int	maxsize;

	Q_ROUND_POWER2(width, *scaled_width);
	Q_ROUND_POWER2(height, *scaled_height);

	if (mode & TEX_MIPMAP)
	{
		*scaled_width >>= (int)gl_picmip.value;
		*scaled_height >>= (int)gl_picmip.value;
	}

	maxsize = (mode & TEX_MIPMAP) ? gl_max_size.value : gl_max_size_default;

	*scaled_width = bound(1, *scaled_width, maxsize);
	*scaled_height = bound(1, *scaled_height, maxsize);
}

/*
__inline unsigned RGBAtoGammascale(unsigned rgba)
{
	unsigned char *rgb;
	unsigned output;
	
	int value;

	value = bound(1, gl_gammascale.value, 3);

	output = rgba;
	rgb = ((unsigned char *)&output);

	rgb[0] = CLAMP(1, (rgb[0] * value), 255);
	rgb[1] = CLAMP(1, (rgb[1] * value), 255);
	rgb[2] = CLAMP(1, (rgb[2] * value), 255);

	return output;
}
*/

__inline unsigned RGBAtoGrayscale(unsigned rgba)//QMB
{
	unsigned char *rgb, value;
	unsigned output;
	
	output = rgba;
	rgb = ((unsigned char *)&output);

	value = min(255,rgb[0] * 0.738 + rgb[1] * 1.063 + rgb[2] * 0.563);

	//Changed to sepia brown

	rgb[0] = value * 0.74;
	rgb[1] = value * 0.53;
	rgb[2] = value * 0.38;

	return output;
}

extern qboolean g_canUseTexComp;

//rww
static __inline char *GL_LegalizeTexName(char *texname)
{
	static char buf[MAX_OSPATH];
	int i;
	for (i = 0; texname[i] && i < (MAX_OSPATH-1); i++)
	{
		if (texname[i] == '*' ||
			texname[i] == '?' ||
			texname[i] == '\\' ||
			texname[i] == '/')
		{
			buf[i] = '_';
		}
		else
		{
			buf[i] = texname[i];
		}
	}
	buf[i] = 0;

	return buf;
}


/*
===============
GL_Upload32
===============
*/

void GL_Upload32 (unsigned *data, int width, int height, int mode)
{
	unsigned int	*scaled;
	int				scaled_width, scaled_height, miplevel;
	GLenum			internal_format;

	Q_ROUND_POWER2(width, scaled_width);
	Q_ROUND_POWER2(height, scaled_height);

	scaled = Q_malloc (scaled_width * scaled_height * 4);

	if (width < scaled_width || height < scaled_height)
	{
		ResampleTexture (data, width, height, scaled, scaled_width, scaled_height);
		width = scaled_width;
		height = scaled_height;
	}
	else
	{
		memcpy (scaled, data, width * height * 4);
	}

	ScaleDimensions (width, height, &scaled_width, &scaled_height, mode);

	while (width > scaled_width || height > scaled_height)
		MipMap ((byte *)scaled, &width, &height);

	if ((mode & TEX_WORLD) && (gl_greyscale.value))
	{
		int i, size;

		size = scaled_width * scaled_height;
		for (i=0;i<size;i++)
		{
			scaled[i] = RGBAtoGrayscale(scaled[i]);//QMB
		}	
	}

#ifdef USEFAKEGL 
	internal_format = (mode & TEX_ALPHA) ? gl_alpha_format : gl_solid_format;
#else
	if (g_canUseTexComp && gl_texCompression.value)
	{
		glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST);
		if (mode & TEX_ALPHA)
		{
			if (mode & TEX_LUMA)
				internal_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
			else
				internal_format = GL_COMPRESSED_RGBA;//alpha_compressed_format
		}
		else 
		{
			if (mode & TEX_LUMA)
				internal_format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
			else
				internal_format = GL_COMPRESSED_RGB;//solid_compressed_format
		}
	}
	else
	{
		if (mode & TEX_ALPHA)
		{
			internal_format = gl_alpha_format;
		}
		else
		{
			internal_format = gl_solid_format;
		}
	}
#endif
	miplevel = 0;

	glTexImage2D (GL_TEXTURE_2D, 0, internal_format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);

	if (mode & TEX_MIPMAP)
	{

		while (width > 1 || height > 1)
		{
			MipMap ((byte *)scaled, &width, &height);			
			miplevel++;
			glTexImage2D (GL_TEXTURE_2D, miplevel, internal_format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);		

		}	
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);

	}
	else
	{
		
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
	}
	Q_free (scaled);
}

/*
===============
GL_Upload8
===============
*/
void GL_Upload8 (byte *data, int width, int height, int mode)
{
	static unsigned	trans[1024*1024];//R00k upped...
	int		i, size, p;
	unsigned	*table;

	table = d_8to24table;
	size = width * height;

	if (size * 4 > sizeof(trans))
		Sys_Error ("GL_Upload8: image too big");

	if (mode & TEX_FULLBRIGHT)
	{
	// this is a fullbright mask, so make all non-fullbright
	// colors transparent
		mode |= TEX_ALPHA;
		for (i=0 ; i<size ; i++)
		{
			p = data[i];
			if (p < 224)
				trans[i] = table[p] & 0x00FFFFFF;// transparent 
			else
				trans[i] = table[p];// fullbright
		}
	}
	else if (mode & TEX_ALPHA)
	{
	// if there are no transparent pixels, make it a 3 component
	// texture even if it was specified as otherwise
		mode &= ~TEX_ALPHA;
		for (i=0 ; i<size ; i++)
		{
			p = data[i];
			if (p == 255)
				mode |= TEX_ALPHA;
			trans[i] = table[p];
		}
	}
	else
	{
		if (size & 3)
			Sys_Error ("GL_Upload8: bad size (%d)", size);

		for (i = 0 ; i < size ; i += 4)
		{
			trans[i]	= table[data[i]];
			trans[i+1]	= table[data[i+1]];
			trans[i+2]	= table[data[i+2]];
			trans[i+3]	= table[data[i+3]];
		}
	}
	
	GL_Upload32 (trans, width, height, mode);	
}

/*
================
GL_FreeTextures -- BPJ
================
*/
void GL_FreeTextures (void)
{
	int i, j;

	if (gl_free_world_textures.value == 0)
	{
		Con_DPrintf (1,"GL_FreeTextures: Not clearing loaded map textures.\n");
		return;
	}

	for (i = j = 0; i < numgltextures; ++i, ++j)
	{
		if (gltextures[i].texmode & TEX_WORLD)
		{
			Con_DPrintf (1,"GL_FreeTextures: Clearing texture: %s.\n", gltextures[i].identifier);
			glDeleteTextures(1, &gltextures[i].texnum);
			--j;
		}
		else if (j < i)
			gltextures[j] = gltextures[i];
	}

	numgltextures = j;
}
/*
================
GL_LoadTexture
================
*/
int GL_LoadTexture (char *identifier, int width, int height, byte *data, int mode, int bpp)
{
	int			i, scaled_width, scaled_height, crc = 0;
	gltexture_t	*glt;
	
	ScaleDimensions (width, height, &scaled_width, &scaled_height, mode);

	// see if the texture is already present
	if (identifier[0])
	{
		crc = CRC_Block (data, width * height * bpp);
		for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
		{
			if (!strncmp(identifier, glt->identifier, sizeof(glt->identifier)-1))
			{
				if (width == glt->width && height == glt->height && scaled_width == glt->scaled_width && scaled_height == glt->scaled_height && crc == glt->crc && bpp == glt->bpp && (mode & ~TEX_COMPLAIN) == (glt->texmode & ~TEX_COMPLAIN))
				{
					GL_Bind (gltextures[i].texnum);
					return gltextures[i].texnum;	// texture cached
				}
				else
				{
					goto GL_LoadTexture_setup;	// reload the texture into the same slot
				}
			}
		}
	}

	if (numgltextures == MAX_GLTEXTURES)
		Sys_Error ("GL_LoadTexture: numgltextures == MAX_GLTEXTURES");

	glt = &gltextures[numgltextures];
	numgltextures++;

	Q_strncpyz (glt->identifier, identifier, sizeof(glt->identifier));
	glt->texnum = texture_extension_number;
	texture_extension_number++;

GL_LoadTexture_setup:
	glt->width = width;
	glt->height = height;
	glt->scaled_width = scaled_width;
	glt->scaled_height = scaled_height;
	glt->texmode = mode;
	glt->crc = crc;
	glt->bpp = bpp;

	if (glt->pathname)
	{
		Z_Free (glt->pathname);
		glt->pathname = NULL;
	}

	if (bpp == 4 && com_netpath[0])
		glt->pathname = CopyString (com_netpath);

	if (cls.state != ca_dedicated)
	{
		GL_Bind (glt->texnum);	

		switch (bpp)
		{
		case 1:
			GL_Upload8 (data, width, height, mode);
			break;

		case 4:
			GL_Upload32 ((void *)data, width, height, mode);
			break;

		default:
			Sys_Error ("GL_LoadTexture: unknown bpp");
			break;
		}
	}
	return glt->texnum;
}

/*
================
GL_LoadPicTexture
================
*/
int GL_LoadPicTexture (char *name, mpic_t *pic, byte *data)
{
	int		i, glwidth, glheight;
	char	fullname[MAX_QPATH] = "pic:";

	for (glwidth = 1 ; glwidth < pic->width ; glwidth <<= 1)
		;
	for (glheight = 1 ; glheight < pic->height ; glheight <<= 1)
		;

	Q_strncpyz (fullname + 4, name, sizeof(fullname) - 4);
	if (glwidth == pic->width && glheight == pic->height)
	{
		pic->texnum = GL_LoadTexture (fullname, glwidth, glheight, data, TEX_ALPHA|TEX_MIPMAP, 1);
		pic->sl = 0;
		pic->sh = 1;
		pic->tl = 0;
		pic->th = 1;
	}
	else
	{
		byte	*src, *dest, *buf;

		buf = Q_calloc (glwidth * glheight, 1);

		src = data;
		dest = buf;
		for (i = 0 ; i < pic->height ; i++)
		{
			memcpy (dest, src, pic->width);
			src += pic->width;
			dest += glwidth;
		}

		pic->texnum = GL_LoadTexture ("", glwidth, glheight, buf, TEX_ALPHA|TEX_MIPMAP, 1);
		pic->sl = 0;
		pic->sh = (float)pic->width / glwidth;
		pic->tl = 0;
		pic->th = (float)pic->height / glheight;

		Q_free (buf);
	}                                 	

	return pic->texnum;
}

/*
================
GL_FindTexture
================
*/
gltexture_t *GL_FindTexture (char *identifier)
{
	int	i;

	for (i=0 ; i<numgltextures ; i++)
	{
		if (!strcmp(identifier, gltextures[i].identifier))
			return &gltextures[i];
	}
	return NULL;
}

static	gltexture_t	*current_texture = NULL;

#define CHECK_TEXTURE_ALREADY_LOADED		\
	if (CheckTextureLoaded(mode))		\
	{					\
		current_texture = NULL;		\
		fclose (file);			\
		return NULL;			\
	}


static qboolean CheckTextureLoaded (int mode)
{
	int	scaled_width, scaled_height;

	if (current_texture && current_texture->pathname && !strcmp(com_netpath, current_texture->pathname))
	{
		ScaleDimensions (current_texture->width, current_texture->height, &scaled_width, &scaled_height, mode);
		if (current_texture->scaled_width == scaled_width && current_texture->scaled_height == scaled_height)
			return true;
	}

	return false;
}

static struct
{
	char *name;
} t_ext[] =
	{
		{"tga"},	
		{"jpg"},	
		{"png"},	
		{""}	
	};

//R00k: Redone so that we search for filename.tga, filename.png, filename.jpg in each pak, before proceeding to the next pak.
//increases the load time! :(
byte *GL_LoadImagePixels (char *filename, int matchwidth, int matchheight, int mode)
{
	char	basename[MAX_QPATH];
	byte	*c, *data;
	FILE	*file;

	searchpath_t	*search;
	pack_t			*pak;
	int				i, j;
	char			fname[MAX_QPATH];

	com_filesize = -1;
	com_netpath[0] = 0;

	COM_StripExtension (filename, basename);
	for (c = basename ; *c ; c++)
		if (*c == '*')
			*c = '#';

// search through the path, one element at a time
	for (search = com_searchpaths ; search ; search = search->next)
	{
		// is the element a pak file?
		if (search->pack)
		{
			// look through all the pak file elements
			pak = search->pack;
			for (j = 0 ; j < 3 ; j++)
			{
				Q_snprintfz(fname, sizeof(fname), "%s.%s", basename, t_ext[j].name);
				for (i=0 ; i<pak->numfiles ; i++)
				{				
					if (!strcmp(pak->files[i].name, fname))	// found it!
					{
						if (!(file = fopen(pak->filename, "rb")))
							Sys_Error ("Couldn't reopen %s", pak->filename);
	
						if (developer.value)
							Con_Printf ("PackFile: %s : %s\n", pak->filename, filename);
						
						fseek (file, pak->files[i].filepos, SEEK_SET);
						com_filesize = pak->files[i].filelen;
						Q_snprintfz (com_netpath, sizeof(com_netpath), "%s#%i", pak->filename, i);

						switch (j)
						{
							case 0:
							CHECK_TEXTURE_ALREADY_LOADED;
							if ((data = Image_LoadTGA(file, fname, matchwidth, matchheight)))
								return data;
							break;
						
							case 1:
							CHECK_TEXTURE_ALREADY_LOADED;
							if ((data = Image_LoadJPEG(file, fname, matchwidth, matchheight)))
								return data;
							break;

							case 2:
							CHECK_TEXTURE_ALREADY_LOADED;
							if ((data = Image_LoadPNG(file, fname, matchwidth, matchheight)))
								return data;
							break;
						}
					}
				}					
			}
		}
		else
		{               
			// check a file in the directory tree
			memset(fname,0, sizeof(fname));

			for (j = 0 ; j < 3 ; j++)
			{
				Q_snprintfz(fname, sizeof(fname), "%s.%s", basename, t_ext[j].name);
				Q_snprintfz (com_netpath, sizeof(com_netpath), "%s/%s", search->filename, fname);

				if (!(file = fopen(com_netpath, "rb")))
					continue;

				switch (j)
				{
					case 0:
					CHECK_TEXTURE_ALREADY_LOADED;
					if ((data = Image_LoadTGA(file, fname, matchwidth, matchheight)))
						return data;
					break;
				
					case 1:
					CHECK_TEXTURE_ALREADY_LOADED;
					if ((data = Image_LoadJPEG(file, fname, matchwidth, matchheight)))
						return data;
					break;

					case 2:
					CHECK_TEXTURE_ALREADY_LOADED;
					if ((data = Image_LoadPNG(file, fname, matchwidth, matchheight)))
						return data;
					break;
				}
			}
		}		
	}
	
	if (developer.value)
		Sys_Printf ("COM_FOpenFile: can't find %s\n", filename);
	
	file = NULL;
	com_filesize = -1;

	if (mode & TEX_COMPLAIN)
	{
		if (!no24bit)
			Con_Printf ("Couldn't load %s image\n", COM_SkipPath(filename));
	}

	return NULL;
}

int GL_LoadTexturePixels (byte *data, char *identifier, int width, int height, int mode)
{
	int			i, j, image_size;
	qboolean	gamma;

	image_size = width * height;
	gamma = (vid_gamma != 1);

	if (mode & TEX_LUMA)
	{
		gamma = false;
	}
	
	if (mode & TEX_ALPHA)
	{
		mode &= ~TEX_ALPHA;
		for (j=0 ; j<image_size ; j++)
		{
			if (((((unsigned *)data)[j] >> 24) & 0xFF) < 255)
			{
				mode |= TEX_ALPHA;
				break;
			}
		}
	}

	if (gamma)
	{
		for (i=0 ; i<image_size ; i++)
		{
			data[4*i+0] = vid_gamma_table[data[4*i+0]];
			data[4*i+1] = vid_gamma_table[data[4*i+1]];
			data[4*i+2] = vid_gamma_table[data[4*i+2]];
		}
	}

	return GL_LoadTexture (identifier, width, height, data, mode, 4);
}

int GL_LoadTextureImage (char *filename, char *identifier, int matchwidth, int matchheight, int mode)
{
	int		texnum;
	byte		*data;
	gltexture_t	*gltexture;

	if (no24bit)
		return 0;

	if (!identifier)
		identifier = filename;

	gltexture = current_texture = GL_FindTexture (identifier);

	if (!(data = GL_LoadImagePixels(filename, matchwidth, matchheight, mode)))
	{
		texnum = (gltexture && !current_texture) ? gltexture->texnum : 0;
	}
	else
	{
		texnum = GL_LoadTexturePixels (data, identifier, image_width, image_height, mode);
		Q_free (data);
	}

	current_texture = NULL;
	return texnum;
}

mpic_t *GL_LoadPicImage (char *filename, char *id, int matchwidth, int matchheight, int mode)
{
	int		glwidth, glheight, i;
	char	identifier[MAX_QPATH] = "pic:";
	byte	*data, *src, *dest, *buf;
	static	mpic_t	pic;

	if (no24bit)
		return NULL;
	
	if (no24bitWad)
		return NULL;

	if (!(data = GL_LoadImagePixels(filename, matchwidth, matchheight, mode)))
			return NULL;

	pic.width = image_width;
	pic.height = image_height;

	if (mode & TEX_ALPHA)
	{
		mode &= ~TEX_ALPHA;
		for (i=0 ; i < image_width * image_height ; i++)
		{
			if (((((unsigned *)data)[i] >> 24) & 0xFF) < 255)
			{
				mode |= TEX_ALPHA;
				break;
			}
		}
	}

	for (glwidth = 1 ; glwidth < pic.width ; glwidth <<= 1)
		;
	for (glheight = 1 ; glheight < pic.height ; glheight <<= 1)
		;

	Q_strncpyz (identifier + 4, id ? id : filename, sizeof(identifier) - 4);
	if (glwidth == pic.width && glheight == pic.height)
	{
		pic.texnum = GL_LoadTexture (identifier, pic.width, pic.height, data, mode, 4);
		pic.sl = 0;
		pic.sh = 1;
		pic.tl = 0;
		pic.th = 1;
	}
	else
	{
		buf = Q_calloc (glwidth * glheight, 4);

		src = data;
		dest = buf;
		for (i=0 ; i<pic.height ; i++)
		{
			memcpy (dest, src, pic.width * 4);
			src += pic.width * 4;
			dest += glwidth * 4;
		}
		pic.texnum = GL_LoadTexture (identifier, glwidth, glheight, buf, mode & ~TEX_MIPMAP, 4);
		pic.sl = 0;
		pic.sh = (float)pic.width / glwidth;
		pic.tl = 0;
		pic.th = (float)pic.height / glheight;
		Q_free (buf);
	}

	Q_free (data);
	return &pic;
}

int GL_LoadCharsetImage (char *filename, char *identifier)
{
	int		i, j, texnum, image_size;
	byte		*data, *buf, *dest, *src;
	qboolean	transparent = false;

	if ((no24bit)||(no24bitWad))
		return 0;

	if (!(data = GL_LoadImagePixels(filename, 0, 0, 0)))
		return 0;

	if (!identifier)
		identifier = filename;

	image_size = image_width * image_height;

	for (j=0 ; j<image_size ; j++)
	{
		if (((((unsigned *)data)[j] >> 24) & 0xFF) < 255)
		{
			transparent = true;
			break;
		}
	}
	if (!transparent)
	{
		for (i=0 ; i < image_width * image_height ; i++)
		{
			if (data[i*4] == data[0] && data[i*4+1] == data[1] && data[i*4+2] == data[2])
				data[i*4+3] = 0;
		}
	}

	
	buf = dest = Q_calloc (image_size * 2, 4);
	src = data;
	for (i=0 ; i<16 ; i++)
	{
		memcpy (dest, src, image_size >> 2);	
		src += image_size >> 2;
		dest += image_size >> 1;				
	}

	texnum = GL_LoadTexture (identifier, image_width, image_height * 2, buf, TEX_ALPHA, 4);

	Q_free (buf);
	Q_free (data);

	return texnum;
}

static	GLenum	oldtarget = GL_TEXTURE0;
static	int	cnttextures[4] = {-1, -1, -1, -1};     // cached
static qboolean	mtexenabled = false;

void GL_SelectTexture (GLenum target) 
{
	if (target == oldtarget)
		return;

	qglActiveTexture (target);

	cnttextures[oldtarget-GL_TEXTURE0] = currenttexture;
	currenttexture = cnttextures[target-GL_TEXTURE0];
	oldtarget = target;
}

void GL_BindTexture (GLenum tmu, gltexture_t *texture)
{
	int tmunum = 0;

	// allow specifying if we just want to bind on the current TMU
	if (tmu == GL_DONT_CARE)
		tmu = oldtarget + 1;

	tmunum = tmu - GL_TEXTURE0;

	if (!texture)
	{
		// allow clearing down of a current bind
		cnttextures[tmunum] = -1;
	}
	else if (texture->texnum != cnttextures[tmunum])
	{
		// activate the correct tmu
		if (oldtarget != tmu)
		{
			GL_SelectTexture(tmu);
		}

		cnttextures[tmunum] = texture->texnum;
		glBindTexture (GL_TEXTURE_2D, texture->texnum);
	}
}

void GL_DisableMultitexture (void) 
{
	if (mtexenabled)
	{
		glDisable (GL_TEXTURE_2D);
		GL_SelectTexture (GL_TEXTURE0);
		mtexenabled = false;
	}
}

void GL_EnableMultitexture (void) 
{
	if (gl_mtexable)
	{
		GL_SelectTexture (GL_TEXTURE1);
		glEnable (GL_TEXTURE_2D);
		mtexenabled = true;
	}
}

void GL_EnableTMU (GLenum target)
{
	GL_SelectTexture (target);
	glEnable (GL_TEXTURE_2D);
}

void GL_DisableTMU (GLenum target)
{
	GL_SelectTexture (target);
	glDisable (GL_TEXTURE_2D);
}

