#include "quakedef.h"

#ifndef SERVERONLY
#include "shader.h"

#ifdef _WIN32
#include <windows.h>
#endif
#include <ctype.h>

void Font_Init(void);
void Font_Shutdown(void);
struct font_s *Font_LoadFont(int height, char *fontfilename);
void Font_Free(struct font_s *f);
void Font_BeginString(struct font_s *font, int vx, int vy, int *px, int *py);
void Font_BeginScaledString(struct font_s *font, float vx, float vy, float szx, float szy, float *px, float *py); /*avoid using*/
void Font_Transform(int vx, int vy, int *px, int *py);
int Font_CharHeight(void);
float Font_CharScaleHeight(void);
int Font_CharWidth(unsigned int charcode);
float Font_CharScaleWidth(unsigned int charcode);
int Font_CharEndCoord(struct font_s *font, int x, unsigned int charcode);
int Font_DrawChar(int px, int py, unsigned int charcode);
float Font_DrawScaleChar(float px, float py, unsigned int charcode); /*avoid using*/
void Font_EndString(struct font_s *font);
int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int maxlines, conchar_t **starts, conchar_t **ends);
struct font_s *font_conchar;
struct font_s *font_tiny;

#ifdef AVAIL_FREETYPE
#include <ft2build.h>
#include FT_FREETYPE_H 

static FT_Library fontlib;


qboolean triedtoloadfreetype;
dllhandle_t *fontmodule;
FT_Error (VARGS *pFT_Init_FreeType)		(FT_Library  *alibrary);
FT_Error (VARGS *pFT_Load_Char)			(FT_Face face, FT_ULong char_code, FT_Int32 load_flags);
FT_Error (VARGS *pFT_Set_Pixel_Sizes)	(FT_Face face, FT_UInt pixel_width, FT_UInt pixel_height);
FT_Error (VARGS *pFT_New_Face)			(FT_Library library, const char *pathname, FT_Long face_index, FT_Face *aface);
FT_Error (VARGS *pFT_New_Memory_Face)	(FT_Library library, const FT_Byte* file_base, FT_Long file_size, FT_Long face_index, FT_Face *aface);
FT_Error (VARGS *pFT_Done_Face)			(FT_Face face);
#endif

static const char *imgs[] =
{
	//0xe10X
	"e100","e101",
	"inv_shotgun",
	"inv_sshotgun",
	"inv_nailgun",
	"inv_snailgun",
	"inv_rlaunch",
	"inv_srlaunch",
	"inv_lightng",	//8
	"e109","e10a","e10b","e10c","e10d","e10e","e10f",

	//0xe11X
	"e110","e111",
	"inv2_shotgun",
	"inv2_sshotgun",
	"inv2_nailgun",
	"inv2_snailgun",
	"inv2_rlaunch",
	"inv2_srlaunch",
	"inv2_lightng",
	"e119","e11a","e11b","e11c","e11d","e11e","e11f",

	//0xe12X
	"sb_shells",
	"sb_nails",
	"sb_rocket",
	"sb_cells",

	"sb_armor1",
	"sb_armor2",
	"sb_armor3",
	"e127","e128","e129","e12a","e12b","e12c","e12d","e12e","e12f",

	//0xe13X
	"sb_key1",
	"sb_key2",
	"sb_invis",
	"sb_invuln",
	"sb_suit",
	"sb_quad",

	"sb_sigil1",
	"sb_sigil2",
	"sb_sigil3",
	"sb_sigil4",
	"e13a","e13b","e13c","e13d","e13e","e13f",

	//0xe14X
	"face1",
	"face_p1",
	"face2",
	"face_p2",
	"face3",
	"face_p3",
	"face4",
	"face_p4",
	"face5",
	"face_p5",

	"face_invis",
	"face_invul2",
	"face_inv2",
	"face_quad",
	"e14e",
	"e14f",

	//0xe15X
	"e150",
	"e151",
	"e152",
	"e153",
	"e154",
	"e155",
	"e156",
	"e157",
	"e158",
	"e159",
	"e15a",
	"e15b",
	"e15c",
	"e15d",
	"e15e",
	"e15f",

	//0xe16X
	"e160",
	"e161",
	"e162",
	"e163",
	"e164",
	"e165",
	"e166",
	"e167",
	"e168",
	"e169",
	"e16a",
	"e16b",
	"e16c",
	"e16d",
	"e16e",
	"e16f"
};

#define FONTCHARS (1<<16)
#define FONTPLANES (1<<2)	//this is total, not per font.
#define PLANEIDXTYPE unsigned short
#define CHARIDXTYPE unsigned short

#define INVALIDPLANE ((1<<(8*sizeof(PLANEIDXTYPE)))-1)
#define BITMAPPLANE ((1<<(8*sizeof(PLANEIDXTYPE)))-2)
#define DEFAULTPLANE ((1<<(8*sizeof(PLANEIDXTYPE)))-3)
#define SINGLEPLANE ((1<<(8*sizeof(PLANEIDXTYPE)))-4)
#define PLANEWIDTH (1<<8)
#define PLANEHEIGHT PLANEWIDTH



#define GEN_CONCHAR_GLYPHS 0	//set to 0 or 1 to define whether to generate glyphs from conchars too, or if it should just draw them as glquake always used to
extern cvar_t cl_noblink;
extern cvar_t con_ocranaleds;

typedef struct font_s
{
	struct charcache_s
	{
		struct charcache_s *nextchar;

		PLANEIDXTYPE texplane;
		unsigned char advance;	//how wide this char is, when drawn
		char pad;

		unsigned char bmx;
		unsigned char bmy;
		unsigned char bmw;
		unsigned char bmh;

		short top;
		short left;
	} chars[FONTCHARS];
	char name[64];

	short charheight;
	texid_t singletexture;
#ifdef AVAIL_FREETYPE
	FT_Face face;
	void *membuf;
#endif
	struct font_s *alt;
	vec3_t alttint;
} font_t;

typedef struct {
	texid_t texnum[FONTPLANES];
	texid_t defaultfont;

	unsigned char plane[PLANEWIDTH*PLANEHEIGHT][4];	//tracks the current plane
	PLANEIDXTYPE activeplane;
	unsigned char planerowx;
	unsigned char planerowy;
	unsigned char planerowh;
	qboolean planechanged;

	struct charcache_s *oldestchar;
	struct charcache_s *newestchar;
	shader_t *shader;
	shader_t *backshader;
} fontplanes_t;

static fontplanes_t fontplanes;
	
#define FONT_CHAR_BUFFER 512
static index_t font_indicies[FONT_CHAR_BUFFER*6];
static vecV_t font_coord[FONT_CHAR_BUFFER*4];
static vec2_t font_texcoord[FONT_CHAR_BUFFER*4];
static byte_vec4_t font_forecoloura[FONT_CHAR_BUFFER*4];
static byte_vec4_t font_backcoloura[FONT_CHAR_BUFFER*4];
static mesh_t font_foremesh;
static mesh_t font_backmesh;
static texid_t font_texture;
static int font_colourmask;
static byte_vec4_t font_forecolour;
static byte_vec4_t font_backcolour;

static struct font_s *curfont;
static float curfont_scale[2];
static qboolean curfont_scaled;

//called at load time - initalises font buffers
void Font_Init(void)
{
	int i;
	TEXASSIGN(fontplanes.defaultfont, r_nulltex);

	font_foremesh.indexes = font_indicies;
	font_foremesh.xyz_array = font_coord;
	font_foremesh.st_array = font_texcoord;
	font_foremesh.colors4b_array = font_forecoloura;

	font_backmesh.indexes = font_indicies;
	font_backmesh.xyz_array = font_coord;
	font_backmesh.st_array = font_texcoord;
	font_backmesh.colors4b_array = font_backcoloura;

	for (i = 0; i < FONT_CHAR_BUFFER; i++)
	{
		font_indicies[i*6+0] = i*4+0;
		font_indicies[i*6+1] = i*4+1;
		font_indicies[i*6+2] = i*4+2;
		font_indicies[i*6+3] = i*4+0;
		font_indicies[i*6+4] = i*4+2;
		font_indicies[i*6+5] = i*4+3;
	}

	for (i = 0; i < FONTPLANES; i++)
	{
		TEXASSIGN(fontplanes.texnum[i], R_AllocNewTexture("***fontplane***", PLANEWIDTH, PLANEHEIGHT, IF_2D|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA));
	}

	fontplanes.shader = R_RegisterShader("ftefont",
		"{\n"
			"if $nofixed\n"
				"program default2d\n"
			"endif\n"
			"nomipmaps\n"
			"{\n"
				"map $nearest:$diffuse\n"
				"rgbgen vertex\n"
				"alphagen vertex\n"
				"blendfunc blend\n"
			"}\n"
		"}\n"
		);

	fontplanes.backshader = R_RegisterShader("ftefontback",
		"{\n"
			"nomipmaps\n"
			"{\n"
				"map $whiteimage\n"
				"rgbgen vertex\n"
				"alphagen vertex\n"
				"blendfunc blend\n"
			"}\n"
		"}\n"
		);

	font_colourmask = ~0;
}

//flush the font buffer, by drawing it to the screen
static void Font_Flush(void)
{
	if (!font_foremesh.numindexes)
		return;
	if (fontplanes.planechanged)
	{
		R_Upload(fontplanes.texnum[fontplanes.activeplane], NULL, TF_RGBA32, (void*)fontplanes.plane, NULL, PLANEWIDTH, PLANEHEIGHT, IF_2D|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA);

		fontplanes.planechanged = false;
	}
	font_foremesh.istrifan = (font_foremesh.numvertexes == 4);
	if (font_colourmask & CON_NONCLEARBG)
	{
		font_backmesh.numindexes = font_foremesh.numindexes;
		font_backmesh.numvertexes = font_foremesh.numvertexes;
		font_backmesh.istrifan = font_foremesh.istrifan;

		BE_DrawMesh_Single(fontplanes.backshader, &font_backmesh, NULL, &fontplanes.backshader->defaulttextures, 0);
	}
	TEXASSIGN(fontplanes.shader->defaulttextures.base, font_texture);
	BE_DrawMesh_Single(fontplanes.shader, &font_foremesh, NULL, &fontplanes.shader->defaulttextures, 0);
	font_foremesh.numindexes = 0;
	font_foremesh.numvertexes = 0;
}

static int Font_BeginChar(texid_t tex)
{
	int fvert;

	if (font_foremesh.numindexes == FONT_CHAR_BUFFER*6 || font_texture.ref != tex.ref)
	{
		Font_Flush();
		TEXASSIGNF(font_texture, tex);
	}

	fvert = font_foremesh.numvertexes;
	
	font_foremesh.numindexes += 6;
	font_foremesh.numvertexes += 4;

	return fvert;
}

//clear the shared planes and free memory etc
void Font_Shutdown(void)
{
	int i;

	for (i = 0; i < FONTPLANES; i++)
		TEXASSIGN(fontplanes.texnum[i], r_nulltex);
	fontplanes.activeplane = 0;
	fontplanes.oldestchar = NULL;
	fontplanes.newestchar = NULL;
	fontplanes.planechanged = 0;
	fontplanes.planerowx = 0;
	fontplanes.planerowy = 0;
	fontplanes.planerowh = 0;
}

//we got too many chars and switched to a new plane - purge the chars in that plane
void Font_FlushPlane(font_t *f)
{
	/*
	assumption:
	oldest chars must be of the oldest plane
	*/

	//we've not broken anything yet, flush while we can
	Font_Flush();

	fontplanes.activeplane++;
	fontplanes.activeplane = fontplanes.activeplane % FONTPLANES;
	fontplanes.planerowh = 0;
	fontplanes.planerowx = 0;
	fontplanes.planerowy = 0;

	while (fontplanes.oldestchar)
	{
		if (fontplanes.oldestchar->texplane != fontplanes.activeplane)
			break;

		//remove it from the list of active chars, and invalidate it
		fontplanes.oldestchar->texplane = INVALIDPLANE;
		fontplanes.oldestchar = fontplanes.oldestchar->nextchar;
	}
	if (!fontplanes.oldestchar)
		fontplanes.newestchar = NULL;
}

//loads a new image into a given character slot for the given font.
//note: make sure it doesn't already exist or things will get cyclic
//alphaonly says if its a greyscale image. false means rgba.
static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, int alphaonly, void *data, unsigned int bmw, unsigned int bmh, unsigned int pitch)
{
	int x, y;
	unsigned char *out;
	struct charcache_s *c = &f->chars[charidx];

	if (fontplanes.planerowx + (int)bmw >= PLANEWIDTH)
	{
		fontplanes.planerowx = 0;
		fontplanes.planerowy += fontplanes.planerowh;
		fontplanes.planerowh = 0;
	}

	if (fontplanes.planerowy+(int)bmh >= PLANEHEIGHT)
		Font_FlushPlane(f);

	if (fontplanes.newestchar)
		fontplanes.newestchar->nextchar = c;
	else
		fontplanes.oldestchar = c;
	fontplanes.newestchar = c;
	c->nextchar = NULL;

	c->texplane = fontplanes.activeplane;
	c->bmx = fontplanes.planerowx;
	c->bmy = fontplanes.planerowy;
	c->bmw = bmw;
	c->bmh = bmh;

	if (fontplanes.planerowh < (int)bmh)
		fontplanes.planerowh = bmh;
	fontplanes.planerowx += bmw;

	out = (unsigned char *)&fontplanes.plane[c->bmx+(int)c->bmy*PLANEHEIGHT];
	if (alphaonly)
	{
		for (y = 0; y < bmh; y++)
		{
			for (x = 0; x < bmw; x++)
			{
				*(unsigned int *)&out[x*4] = 0xffffffff;
				out[x*4+3] = ((unsigned char*)data)[x];
			}
			data = (char*)data + pitch;
			out += PLANEWIDTH*4;
		}
	}
	else
	{
		pitch*=4;
		for (y = 0; y < bmh; y++)
		{
			for (x = 0; x < bmw; x++)
			{
				((unsigned int*)out)[x] = ((unsigned int*)data)[x];
			}
			data = (char*)data + pitch;
			out += PLANEWIDTH*4;
		}
	}
	fontplanes.planechanged = true;
	return c;
}

//loads the given charidx for the given font, importing from elsewhere if needed.
static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx)
{
	struct charcache_s *c;

#if GEN_CONCHAR_GLYPHS != 0
	if (charidx >= 0xe000 && charidx <= 0xe0ff)
	{
		int cpos = charidx & 0xff;
		unsigned int img[64*64], *d;
		unsigned char *s;
		int scale;
		int x,y, ys;
		qbyte *draw_chars = W_GetLumpName("conchars");
		if (draw_chars)
		{
			d = img;
			s = draw_chars + 8*(cpos&15)+128*8*(cpos/16);

			scale = f->charheight/8;
			if (scale < 1)
				scale = 1;
			if (scale > 64/8)
				scale = 64/8;
			
			for (y = 0; y < 8; y++)
			{
				for (ys = 0; ys < scale; ys++)
				{
					for (x = 0; x < 8*scale; x++)
						d[x] = d_8to24rgbtable[s[x/scale]];
					d+=8*scale;
				}
				s+=128;
			}
			c = Font_LoadGlyphData(f, charidx, false, img, 8*scale, 8*scale, 8*scale);
			if (c)
			{
				c->advance = 8*scale;
				c->left = 0;
				c->top = 7*scale;
			}
			return c;
		}
		charidx &= 0x7f;
	}
#endif

	if (charidx >= 0xe100 && charidx <= 0xe1ff)
	{
		qpic_t *wadimg;
		unsigned char *src;
		unsigned int img[64*64];
		int nw, nh;
		int x, y;
		unsigned int stepx, stepy;
		unsigned int srcx, srcy;

		if (charidx-0xe100 >= sizeof(imgs)/sizeof(imgs[0]))
			wadimg = NULL;
		else
			wadimg = W_SafeGetLumpName(imgs[charidx-0xe100]);
		if (wadimg)
		{
			nh = wadimg->height;
			nw = wadimg->width;
			while (nh < f->charheight)
			{
				nh *= 2;
				nw *= 2;
			}
			if (nh > f->charheight)
			{
				nw = (nw * f->charheight)/nh;
				nh = f->charheight;
			}
			stepy = 0x10000*((float)wadimg->height/nh);
			stepx = 0x10000*((float)wadimg->width/nw);
			if (nh > 64)
				nh = 64;
			if (nw > 64)
				nw = 64;
			srcy = 0;
			for (y = 0; y < nh; y++)
			{
				src = (unsigned char *)(wadimg->data);
				src += wadimg->width * (srcy>>16);
				srcy += stepy;
				srcx = 0;
				for (x = 0; x < nw; x++)
				{
					img[x+y*64] = d_8to24rgbtable[src[srcx>>16]];
					srcx += stepx;
				}
			}

			c = Font_LoadGlyphData(f, charidx, false, img, nw, nh, 64);
			if (c)
			{
				c->left = 0;
				c->top = f->charheight - nh;
				c->advance = nw;
				return c;
			}
		}
	}
	/*make tab invisible*/
	if (charidx == '\t' || charidx == '\n')
	{
		c = &f->chars[charidx];
		c->left = 0;
		c->advance = f->charheight;
		c->top = 0;

		c->texplane = 0;
		c->bmx = 0;
		c->bmy = 0;
		c->bmw = 0;
		c->bmh = 0;
		return c;
	}

#ifdef AVAIL_FREETYPE
	if (f->face)
	{
		if (pFT_Load_Char(f->face, charidx, FT_LOAD_RENDER) == 0)
		{
			FT_GlyphSlot slot;
			FT_Bitmap *bm;

			slot = f->face->glyph;
			bm = &slot->bitmap;
			c = Font_LoadGlyphData(f, charidx, true, bm->buffer, bm->width, bm->rows, bm->pitch); 

			if (c)
			{
				c->advance = slot->advance.x >> 6;
				c->left = slot->bitmap_left;
				c->top = f->charheight*3/4 - slot->bitmap_top;
				return c;
			}
		}
	}
#endif
	return NULL;
}

//obtains a cached char, null if not cached
static struct charcache_s *Font_GetChar(font_t *f, CHARIDXTYPE charidx)
{
	struct charcache_s *c = &f->chars[charidx];
	if (c->texplane == INVALIDPLANE)
	{
		//not cached, can't get.
		c = Font_TryLoadGlyph(f, charidx);

		if (!c)
		{
			charidx = 0xfffd;	//unicode's replacement char
			c = &f->chars[charidx];
			if (c->texplane == INVALIDPLANE)
				c = Font_TryLoadGlyph(f, charidx);
		}
		if (!c)
		{
			charidx = '?';	//meh
			c = &f->chars[charidx];
			if (c->texplane == INVALIDPLANE)
				c = Font_TryLoadGlyph(f, charidx);
		}
	}
	return c;
}

qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, char *fontfilename)
{
#ifdef AVAIL_FREETYPE
	FT_Face face = NULL;
	FT_Error error;
	flocation_t loc;
	void *fbase = NULL;
	if (!*fontfilename)
		return false;
	if (!fontlib)
	{
		dllfunction_t ft2funcs[] =
		{
			{(void**)&pFT_Init_FreeType, "FT_Init_FreeType"},
			{(void**)&pFT_Load_Char, "FT_Load_Char"},
			{(void**)&pFT_Set_Pixel_Sizes, "FT_Set_Pixel_Sizes"},
			{(void**)&pFT_New_Face, "FT_New_Face"},
			{(void**)&pFT_New_Memory_Face, "FT_New_Memory_Face"},
			{(void**)&pFT_Init_FreeType, "FT_Init_FreeType"},
			{(void**)&pFT_Done_Face, "FT_Done_Face"},
			{NULL, NULL}
		};
		if (triedtoloadfreetype)
			return false;
		triedtoloadfreetype = true;

		fontmodule = Sys_LoadLibrary("freetype6", ft2funcs);
		if (!fontmodule)
			return false;
		error = pFT_Init_FreeType(&fontlib);
		if (error)
		{
			Sys_CloseLibrary(fontmodule);
			return false;
		}
		/*any other errors leave freetype open*/
	}

	error = FT_Err_Cannot_Open_Resource;
	if (FS_FLocateFile(fontfilename, FSLFRT_LENGTH, &loc)>0)
	{
		if (*loc.rawname && !loc.offset)
		{
			fbase = NULL;
			/*File is directly fopenable with no bias (not in a pk3/pak). Use the system-path form, so we don't have to eat the memory cost*/
			error = pFT_New_Face(fontlib, loc.rawname, 0, &face);
		}
		else
		{
			/*File is inside an archive, we need to read it and pass it as memory (and keep it available)*/
			vfsfile_t *f;
			f = FS_OpenReadLocation(&loc);
			if (f && loc.len > 0)
			{
				fbase = BZ_Malloc(loc.len);
				VFS_READ(f, fbase, loc.len);
				VFS_CLOSE(f);

				error = pFT_New_Memory_Face(fontlib, fbase, loc.len, 0, &face);
			}
		}
	}

#ifdef _WIN32
	if (error)
	{
		static qboolean firsttime = true;
		static char fontdir[MAX_OSPATH];

		if (firsttime)
		{
			HMODULE shfolder = LoadLibrary("shfolder.dll");
			firsttime = false;
			if (shfolder)
			{
				HRESULT (WINAPI *dSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
				dSHGetFolderPath = (void *)GetProcAddress(shfolder, "SHGetFolderPathA");
				if (dSHGetFolderPath)
				{
					// 0x14 == CSIDL_FONTS
					if (dSHGetFolderPath(NULL, 0x14, NULL, 0, fontdir) != S_OK)
						*fontdir = 0;
				}
				FreeLibrary(shfolder);
			}
		}
		if (*fontdir)
		{
			error = pFT_New_Face(fontlib, va("%s/%s", fontdir, fontfilename), 0, &face);
			if (error)
				error = pFT_New_Face(fontlib, va("%s/%s.ttf", fontdir, fontfilename), 0, &face);
		}
	}
#endif
	if (!error)
	{
		error = pFT_Set_Pixel_Sizes(face, 0, height);
		if (!error)
		{
			/*success!*/
			f->membuf = fbase;
			f->face = face;
			return true;
		}
	}
	if (fbase)
		BZ_Free(fbase);
#endif

	return false;
}

static texid_t Font_LoadReplacementConchars(void)
{
	texid_t tex;
	//q1 replacement
	tex = R_LoadReplacementTexture("gfx/conchars.lmp", NULL, IF_2D|IF_NOMIPMAP|IF_NOGAMMA);
	if (TEXVALID(tex))
		return tex;
	//q2
	tex = R_LoadHiResTexture("pics/conchars.pcx", NULL, IF_2D|IF_NOMIPMAP|IF_NOGAMMA);
	if (TEXVALID(tex))
		return tex;
	//q3
	tex = R_LoadHiResTexture("gfx/2d/bigchars.tga", NULL, IF_2D|IF_NOMIPMAP|IF_NOGAMMA);
	if (TEXVALID(tex))
		return tex;
	return r_nulltex;
}
static texid_t Font_LoadQuakeConchars(void)
{
	unsigned int i;
	qbyte *lump;
	lump = W_SafeGetLumpName ("conchars");
	if (lump)
	{
		// add ocrana leds
		if (con_ocranaleds.ival)
		{
			if (con_ocranaleds.ival != 2 || QCRC_Block(lump, 128*128) == 798)
				AddOcranaLEDsIndexed (lump, 128, 128);
		}

		for (i=0 ; i<128*128 ; i++)
			if (lump[i] == 0)
				lump[i] = 255;	// proper transparent color

		return R_LoadTexture8("charset", 128, 128, (void*)lump, IF_2D|IF_NOMIPMAP|IF_NOGAMMA, 1);
	}
	return r_nulltex;
}

static texid_t Font_LoadHexen2Conchars(qboolean iso88591)
{
	//gulp... so it's come to this has it? rework the hexen2 conchars into the q1 system.
	texid_t tex;
	unsigned int i, x;
	unsigned char *tempchars;
	unsigned char *in, *out, *outbuf;
	FS_LoadFile("gfx/menu/conchars.lmp", (void**)&tempchars);

	/*hexen2's conchars are arranged 32-wide, 16 high.
	the upper 8 rows are 256 8859-1 chars
	the lower 8 rows are a separate set of recoloured 8859-1 chars.

	if we're loading for the fallback then we're loading this data for quake compatibility, 
	so we grab only the first 4 rows of each set of chars (128 low chars, 128 high chars).

	if we're loading a proper charset, then we load only the first set of chars, we can recolour the rest anyway (com_parseutf8 will do so anyway).
	as a final note, parsing iso8859-1 french/german/etc as utf8 will generally result in decoding errors which can gracefully revert to 8859-1 safely. If this premise fails too much, we can always change the parser for different charsets - the engine always uses unicode and thus 8859-1 internally.
	*/
	if (tempchars)
	{
		outbuf = BZ_Malloc(8*8*256*8);

		out = outbuf;
		i = 0;
		/*read the low chars*/
		for (; i < 8*8*1; i+=1)
		{
			if (i&(1<<3))
				in = tempchars + (i>>3)*16*8*8+(i&7)*32*8 - 256*4+128;
			else
				in = tempchars + (i>>3)*16*8*8+(i&7)*32*8;
			for (x = 0; x < 16*8; x++)
				*out++ = *in++;
		}
		if (iso88591)
		{
			/*read the non 8859-1 quake-compat control chars*/
			for (; i < 8*8*1 + 16; i+=1)
			{
				if (i&(1<<3))
					in = tempchars+128*128 + ((i>>3)&7)*16*8*8+(i&7)*32*8 - 256*4+128;
				else
					in = tempchars+128*128 + ((i>>3)&7)*16*8*8+(i&7)*32*8;
				for (x = 0; x < 16*8; x++)
					*out++ = *in++;
			}

			/*read the final low chars (final if 8859-1 anyway)*/
			for (; i < 8*8*2; i+=1)
			{
				if (i&(1<<3))
					in = tempchars + (i>>3)*16*8*8+(i&7)*32*8 - 256*4+128;
				else
					in = tempchars + (i>>3)*16*8*8+(i&7)*32*8;
				for (x = 0; x < 16*8; x++)
					*out++ = *in++;
			}
		}
		else
		{
			/*read the high chars*/
			for (; i < 8*8*2; i+=1)
			{
				if (i&(1<<3))
					in = tempchars+128*128 + ((i>>3)&15)*16*8*8+(i&7)*32*8 - 256*4+128;
				else
					in = tempchars+128*128 + ((i>>3)&15)*16*8*8+(i&7)*32*8;
				for (x = 0; x < 16*8; x++)
					*out++ = *in++;
			}
		}
		FS_FreeFile(tempchars);

		// add ocrana leds
		if (!iso88591 && con_ocranaleds.value && con_ocranaleds.value != 2)
			AddOcranaLEDsIndexed (outbuf, 128, 128);

		for (i=0 ; i<128*128 ; i++)
			if (outbuf[i] == 0)
				outbuf[i] = 255;	// proper transparent color
		tex = R_LoadTexture8 (iso88591?"gfx/menu/8859-1.lmp":"charset", 128, 128, outbuf, IF_2D|IF_NOMIPMAP|IF_NOGAMMA, 1);
		Z_Free(outbuf);
		return tex;
	}
	return r_nulltex;
}

static texid_t Font_LoadFallbackConchars(void)
{
	texid_t tex;
	extern qbyte default_conchar[11356];
	int width, height;
	unsigned int i;
	qbyte *lump;
	qboolean hasalpha;
	lump = ReadTargaFile(default_conchar, sizeof(default_conchar), &width, &height, &hasalpha, false);
	if (!lump)
		Sys_Error("Corrupt internal drawchars");
	/*convert greyscale to alpha*/
	for (i = 0; i < width*height; i++)
	{
		lump[i*4+3] = lump[i*4];
		lump[i*4+0] = 255;
		lump[i*4+1] = 255;
		lump[i*4+2] = 255;
	}
	tex = R_LoadTexture32("charset", width, height, (void*)lump, IF_2D|IF_NOMIPMAP|IF_NOGAMMA);
	BZ_Free(lump);
	return tex;
}

/*loads a fallback image. not allowed to fail (use syserror if needed)*/
static texid_t Font_LoadDefaultConchars(void)
{
	texid_t tex;
	tex = Font_LoadReplacementConchars();
	if (TEXVALID(tex))
		return tex;
	tex = Font_LoadQuakeConchars();
	if (TEXVALID(tex))
		return tex;
	tex = Font_LoadHexen2Conchars(true);
	if (TEXVALID(tex))
		return tex;
	tex = Font_LoadFallbackConchars();
	if (TEXVALID(tex))
		return tex;
	Sys_Error("Unable to load any conchars\n");
}

typedef struct 
{ 
    short		width;
    short		height; 
    short		leftoffset;	// pixels to the left of origin 
    short		topoffset;	// pixels below the origin 
    int			columnofs[1];
} doompatch_t;
typedef struct
{
    unsigned char		topdelta;	// -1 is the last post in a column
    unsigned char		length; 	// length data bytes follows
} doomcolumn_t;
void Doom_ExpandPatch(doompatch_t *p, unsigned char *b, int stride)
{
	doomcolumn_t *col;
	unsigned char *src, *dst;
	int x, y;
	for (x = 0; x < p->width; x++)
	{
		col = (doomcolumn_t *)((unsigned char *)p + p->columnofs[x]);
		while(col->topdelta != 0xff)
		{
			//exploit protection
			if (col->length + col->topdelta > p->height)
				break;

			src = (unsigned char *)col + 3; /*why 3? why not, I suppose*/
			dst = b + stride*col->topdelta;
			for (y = 0; y < col->length; y++)
			{
				*dst = *src++;
				dst += stride;
			}
			col = (doomcolumn_t *)((unsigned char*)col + col->length + 4);
		}
		b++;
	}
}

//creates a new font object from the given file, with each text row with the given height.
//width is implicit and scales with height and choice of font.
struct font_s *Font_LoadFont(int vheight, char *fontfilename)
{
	struct font_s *f;
	int i = 0;
	int defaultplane;
	char *aname;
	int height = (vheight * vid.rotpixelheight)/vid.height;

	f = Z_Malloc(sizeof(*f));
	f->charheight = height;
	Q_strncpyz(f->name, fontfilename, sizeof(f->name));

	VectorSet(f->alttint, 1.16, 0.54, 0.41);

#ifdef DOOMWADS
	if (!*fontfilename)
	{
		unsigned char buf[PLANEWIDTH*PLANEHEIGHT];
		int i;
		int x=0,y=0,h=0;
		doompatch_t *dp;

		memset(buf, 0, sizeof(buf));

		for (i = '!'; i <= '_'; i++)
		{
			dp = NULL;
			FS_LoadFile(va("wad/stcfn%.3d", i), (void**)&dp);
			if (!dp)
				break;

			/*make sure it can fit*/
			if (x + dp->width > PLANEWIDTH)
			{
				x = 0;
				y += h;
				h = 0;
			}

			f->chars[i].advance = dp->width;	/*this is how much line space the char takes*/
			f->chars[i].left = -dp->leftoffset;
			f->chars[i].top = -dp->topoffset;
			f->chars[i].nextchar = 0;
			f->chars[i].pad = 0;
			f->chars[i].texplane = SINGLEPLANE;

			f->chars[i].bmx = x;
			f->chars[i].bmy = y;
			f->chars[i].bmh = dp->height;
			f->chars[i].bmw = dp->width;

			Doom_ExpandPatch(dp, &buf[y*PLANEWIDTH + x], PLANEWIDTH);

			x += dp->width;
			if (dp->height > h)
			{
				h = dp->height;
				if (h > f->charheight)
					f->charheight = h;
			}


			FS_FreeFile(dp);
		}

		/*if all loaded okay, replicate the chars to the quake-compat range (both white+red chars)*/
		if (i == '_'+1)
		{
			f->chars[' '].advance = 8;
			f->singletexture = R_LoadTexture8("doomfont", PLANEWIDTH, PLANEHEIGHT, buf, 0, true);
			for (i = 0xe000; i <= 0xe0ff; i++)
			{
				f->chars[i] = f->chars[toupper(i&0x7f)];
			}
			return f;
		}
	}
#endif
	if (!strcmp(fontfilename, "gfx/tinyfont"))
	{
		unsigned int *img;
		int x, y;
		unsigned char *w = W_SafeGetLumpName(fontfilename+4);
		if (!w)
		{
			Z_Free(f);
			return NULL;
		}
		img = Z_Malloc(PLANEWIDTH*PLANEWIDTH*4);
		for (y = 0; y < 32; y++)
			for (x = 0; x < 128; x++)
				img[x + y*PLANEWIDTH] = w[x + y*128]?d_8to24rgbtable[w[x + y*128]]:0;

		f->singletexture = R_LoadTexture(fontfilename,PLANEWIDTH,PLANEWIDTH,TF_RGBA32,img,IF_2D|IF_NOPICMIP|IF_NOMIPMAP);
		Z_Free(img);

		for (i = 0x00; i <= 0xff; i++)
		{
			f->chars[i].advance = height;
			f->chars[i].left = 0;
			f->chars[i].top = 0;
			f->chars[i].nextchar = 0;	//these chars are not linked in
			f->chars[i].pad = 0;
			f->chars[i].texplane = BITMAPPLANE;	/*if its a 'raster' font, don't use the default chars, always use the raster images*/


			if (i >= 'a' && i <= 'z')
			{
				f->chars[i].bmx = ((i - 64)&15)*8;
				f->chars[i].bmy = ((i - 64)/16)*8;
				f->chars[i].bmh = 8;
				f->chars[i].bmw = 8;
			}
			else if (i >= 32 && i < 96)
			{
				f->chars[i].bmx = ((i - 32)&15)*8;
				f->chars[i].bmy = ((i - 32)/16)*8;
				f->chars[i].bmh = 8;
				f->chars[i].bmw = 8;
			}
			else
			{
				f->chars[i].bmh = 0;
				f->chars[i].bmw = 0;
				f->chars[i].bmx = 0;
				f->chars[i].bmy = 0;
			}
		}
		for (i = 0xe000; i <= 0xe0ff; i++)
		{
			f->chars[i] = f->chars[i&0xff];
		}
		return f;
	}

	aname = strstr(fontfilename, ":");
	if (aname)
	{
		*aname = 0;
		if (!strncmp(aname+1, "?col=", 5))
		{
			char *t = aname+6;
			f->alttint[0] = strtod(t, &t);
			if (*t == ' ') t++;
			f->alttint[1] = strtod(t, &t);
			if (*t == ' ') t++;
			f->alttint[2] = strtod(t, &t);
		}
		else
			f->alt = Font_LoadFont(vheight, aname+1);
	}
	if (!Font_LoadFreeTypeFont(f, height, fontfilename))
	{
		//default to only map the ascii-compatible chars from the quake font.
		if (*fontfilename)
			f->singletexture = R_LoadHiResTexture(fontfilename, "fonts", IF_2D|IF_NOMIPMAP);

		for ( ; i < 32; i++)
		{
			f->chars[i].texplane = INVALIDPLANE;
		}
		/*force it to load, even if there's nothing there*/
		for ( ; i < 128; i++)
		{
			f->chars[i].advance = f->charheight;
			f->chars[i].bmh = PLANEWIDTH/16;
			f->chars[i].bmw = PLANEWIDTH/16;
			f->chars[i].bmx = (i&15)*(PLANEWIDTH/16);
			f->chars[i].bmy = (i/16)*(PLANEWIDTH/16);
			f->chars[i].left = 0;
			f->chars[i].top = 0;
			f->chars[i].nextchar = 0;	//these chars are not linked in
			f->chars[i].pad = 0;
			f->chars[i].texplane = BITMAPPLANE;
		}
	}
	if (aname)
		*aname = ':';

	defaultplane = BITMAPPLANE;/*assume the bitmap plane - don't use the fallback as people don't think to use com_parseutf8*/
	if (!TEXVALID(f->singletexture))
	{
		if (!TEXVALID(f->singletexture) && !TEXVALID(fontplanes.defaultfont))
			fontplanes.defaultfont = Font_LoadDefaultConchars();

		if (!strcmp(fontfilename, "gfx/hexen2"))
		{
			f->singletexture = Font_LoadHexen2Conchars(false);
			defaultplane = DEFAULTPLANE;
		}
		if (!TEXVALID(f->singletexture))
			f->singletexture = fontplanes.defaultfont;
	}

	for (; i < FONTCHARS; i++)
	{
		f->chars[i].texplane = INVALIDPLANE;
	}

	/*pack the default chars into it*/
	for (i = 0xe000; i <= 0xe0ff; i++)
	{
		f->chars[i].advance = f->charheight;
		f->chars[i].bmh = PLANEWIDTH/16;
		f->chars[i].bmw = PLANEWIDTH/16;
		f->chars[i].bmx = (i&15)*(PLANEWIDTH/16);
		f->chars[i].bmy = ((i/16)*(PLANEWIDTH/16)) & 0xff;
		f->chars[i].left = 0;
		f->chars[i].top = 0;
		f->chars[i].nextchar = 0;	//these chars are not linked in
		f->chars[i].pad = 0;
		f->chars[i].texplane = defaultplane;
	}
	return f;
}

//removes a font from memory.
void Font_Free(struct font_s *f)
{
	struct charcache_s **link, *c, *valid;

	//kill the alt font first.
	if (f->alt)
	{
		Font_Free(f->alt);
		f->alt = NULL;
	}
	valid = NULL;
	//walk all chars, unlinking any that appear to be within this font's char cache
	for (link = &fontplanes.oldestchar; *link; )
	{
		c = *link;
		if (c >= f->chars && c <= f->chars + FONTCHARS)
		{
			c = c->nextchar;
			if (!c)
				fontplanes.newestchar = valid;
			*link = c;
		}
		else
		{
			valid = c;
			link = &c->nextchar;
		}
	}

#ifdef AVAIL_FREETYPE
	if (f->face)
		pFT_Done_Face(f->face);
	if (f->membuf)
		BZ_Free(f->membuf);
#endif
	Z_Free(f);
}

//maps a given virtual screen coord to a pixel coord, which matches the font's height/width values
void Font_BeginString(struct font_s *font, int vx, int vy, int *px, int *py)
{
	curfont = font;
	*px = (vx*(int)vid.rotpixelwidth) / (float)vid.width;
	*py = (vy*(int)vid.rotpixelheight) / (float)vid.height;

	curfont_scale[0] = curfont->charheight;
	curfont_scale[1] = curfont->charheight;
	curfont_scaled = false;
}
void Font_Transform(int vx, int vy, int *px, int *py)
{
	if (px)
		*px = (vx*(int)vid.rotpixelwidth) / (float)vid.width;
	if (py)
		*py = (vy*(int)vid.rotpixelheight) / (float)vid.height;
}
void Font_BeginScaledString(struct font_s *font, float vx, float vy, float szx, float szy, float *px, float *py)
{
	curfont = font;
	*px = (vx*(float)vid.rotpixelwidth) / (float)vid.width;
	*py = (vy*(float)vid.rotpixelheight) / (float)vid.height;

	//now that its in pixels, clamp it so the text is at least consistant with its position.
	//an individual char may end straddling a pixel boundary, but at least the pixels won't jiggle around as the text moves.
	*px = (int)*px;
	*py = (int)*py;

	if ((int)(szx * vid.rotpixelheight/vid.height) == curfont->charheight && (int)(szy * vid.rotpixelheight/vid.height) == curfont->charheight)
		curfont_scaled = false;
	else
		curfont_scaled = true;

	curfont_scale[0] = (szx * (float)vid.rotpixelheight) / (curfont->charheight * (float)vid.height);
	curfont_scale[1] = (szy * (float)vid.rotpixelheight) / (curfont->charheight * (float)vid.height);
}

void Font_EndString(struct font_s *font)
{
	Font_Flush();
	curfont = NULL;
}

//obtains the font's row height (each row of chars should be drawn using this increment)
int Font_CharHeight(void)
{
	return curfont->charheight;
}

//obtains the font's row height (each row of chars should be drawn using this increment)
float Font_CharScaleHeight(void)
{
	return curfont->charheight * curfont_scale[1];
}

/*
This is where the character ends.
Note: this function supports tabs - x must always be based off 0, with Font_LineDraw actually used to draw the line.
*/
int Font_CharEndCoord(struct font_s *font, int x, unsigned int charcode)
{
	struct charcache_s *c;
#define TABWIDTH (8*20)
	if (charcode&CON_HIDDEN)
		return x;
	if ((charcode&CON_CHARMASK) == '\t')
		return x + ((TABWIDTH - (x % TABWIDTH)) % TABWIDTH);

	if ((charcode & CON_2NDCHARSETTEXT) && font->alt)
		font = font->alt;

	c = Font_GetChar(font, (CHARIDXTYPE)(charcode&CON_CHARMASK));
	if (!c)
	{
		return x+0;
	}

	return x+c->advance;
}

//obtains the width of a character from a given font. This is how wide it is. The next char should be drawn at x + result.
int Font_CharWidth(unsigned int charcode)
{
	struct charcache_s *c;
	struct font_s *font = curfont;
	if (charcode&CON_HIDDEN)
		return 0;
	if ((charcode & CON_2NDCHARSETTEXT) && font->alt)
		font = font->alt;

	c = Font_GetChar(curfont, (CHARIDXTYPE)(charcode&CON_CHARMASK));
	if (!c)
	{
		return 0;
	}

	return c->advance;
}

//obtains the width of a character from a given font. This is how wide it is. The next char should be drawn at x + result.
float Font_CharScaleWidth(unsigned int charcode)
{
	struct charcache_s *c;
	struct font_s *font = curfont;
	if (charcode&CON_HIDDEN)
		return 0;
	if ((charcode & CON_2NDCHARSETTEXT) && font->alt)
		font = font->alt;

	c = Font_GetChar(curfont, (CHARIDXTYPE)(charcode&CON_CHARMASK));
	if (!c)
	{
		return 0;
	}

	return c->advance * curfont_scale[0];
}

//for a given font, calculate the line breaks and word wrapping for a block of text
//start+end are the input string
//starts+ends are an array of line start and end points, which have maxlines elements.
//(end is the terminator, null or otherwise)
//maxpixelwidth is the width of the display area in pixels
int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int maxlines, conchar_t **starts, conchar_t **ends)
{
	int l, bt;
	int px;
	int foundlines = 0;
	struct font_s *font = curfont;

	while (start < end)
	{
	// scan the width of the line
		for (px=0, l=0 ; px <= maxpixelwidth; )
		{
			l++;
			if (start+l >= end || (start[l-1]&CON_CHARMASK) == '\n')
				break;
			px = Font_CharEndCoord(font, px, start[l]);
		}
		//if we did get to the end
		if (px > maxpixelwidth)
		{
			bt = l;
			//backtrack until we find a space
			while(l > 0 && (start[l-1]&CON_CHARMASK)>' ')
			{
				l--;
			}
			if (l == 0 && bt>0)
				l = bt-1;
		}

		starts[foundlines] = start;
		ends[foundlines] = start+l;
		foundlines++;
		if (foundlines == maxlines)
			break;

		start+=l;
		if (start == end)
			break;

		if ((*start&CON_CHARMASK) == '\n'||!l)
			start++;                // skip the \n
	}

	return foundlines;
}

int Font_LineWidth(conchar_t *start, conchar_t *end)
{
	int x = 0;
	struct font_s *font = curfont;
	for (; start < end; start++)
	{
		x = Font_CharEndCoord(font, x, *start);
	}
	return x;
}
void Font_LineDraw(int x, int y, conchar_t *start, conchar_t *end)
{
	int lx = 0;
	struct font_s *font = curfont;
	for (; start < end; start++)
	{
		Font_DrawChar(x+lx, y, *start);
		lx = Font_CharEndCoord(font, lx, *start);
	}
}

/*Note: *all* strings after the current one will inherit the same colour, until one changes it explicitly
correct usage of this function thus requires calling this with 1111 before Font_EndString*/
void Font_ForceColour(float r, float g, float b, float a)
{
	if (font_colourmask & CON_NONCLEARBG)
		Font_Flush();
	font_colourmask = CON_WHITEMASK;

	font_forecolour[0] = r*255;
	font_forecolour[1] = g*255;
	font_forecolour[2] = b*255;
	font_forecolour[3] = a*255;

	font_backcolour[3] = 0;

	/*Any drawchars that are now drawn will get the forced colour*/
}
void Font_InvalidateColour(void)
{
	Font_Flush();
	font_colourmask = ~0;
}

//draw a character from the current font at a pixel location.
int Font_DrawChar(int px, int py, unsigned int charcode)
{
	struct charcache_s *c;
	float s0, s1;
	float t0, t1;
	float nextx;
	float sx, sy, sw, sh;
	int col;
	int v;
	struct font_s *font = curfont;
#ifdef D3D11QUAKE
	float dxbias = (qrenderer == QR_DIRECT3D11)?0.5:0;
#else
#define dxbias 0
#endif
	if (charcode & CON_HIDDEN)
		return px;

	if (charcode & CON_2NDCHARSETTEXT)
	{
		if (font->alt)
		{
			font = font->alt;
			charcode &= ~CON_2NDCHARSETTEXT;
		}
		else if ((charcode&CON_CHARMASK) >= 0xe000 && (charcode&CON_CHARMASK) <= 0xe0ff)
			charcode &= ~CON_2NDCHARSETTEXT;	//don't double-dip
	}

	//crash if there is no current font.
	c = Font_GetChar(font, (CHARIDXTYPE)(charcode&CON_CHARMASK));
	if (!c)
		return px;

	nextx = px + c->advance;

	if ((charcode & CON_CHARMASK) == '\t')
		return px + ((TABWIDTH - (px % TABWIDTH)) % TABWIDTH);
	if ((charcode & CON_CHARMASK) == ' ')
		return nextx;

	if (charcode & CON_BLINKTEXT)
	{
		if (!cl_noblink.ival)
			if ((int)(realtime*3) & 1)
				return nextx;
	}

	if (charcode & CON_RICHFORECOLOUR)
	{
		col = charcode & (CON_2NDCHARSETTEXT|CON_RICHFORECOLOUR|(0xfff<<CON_RICHBSHIFT));
		if (col != font_colourmask)
		{
			if (font_colourmask & CON_NONCLEARBG)
				Font_Flush();
			font_colourmask = col;

			font_forecolour[0] = ((col>>CON_RICHRSHIFT)&0xf)*0x11;
			font_forecolour[1] = ((col>>CON_RICHGSHIFT)&0xf)*0x11;
			font_forecolour[2] = ((col>>CON_RICHBSHIFT)&0xf)*0x11;
			font_forecolour[3] = 255;

			font_backcolour[0] = 0;
			font_backcolour[1] = 0;
			font_backcolour[2] = 0;
			font_backcolour[3] = 0;

			if (charcode & CON_2NDCHARSETTEXT)
			{
				font_forecolour[0] = min(font_forecolour[0]*1.16, 255);
				font_forecolour[1] *= 0.54;
				font_forecolour[2] *= 0.41;
			}
		}
	}
	else
	{
		col = charcode & (CON_2NDCHARSETTEXT|CON_NONCLEARBG|CON_BGMASK|CON_FGMASK|CON_HALFALPHA);
		if (col != font_colourmask)
		{
			if ((col ^ font_colourmask) & CON_NONCLEARBG)
				Font_Flush();
			font_colourmask = col;

			col = (charcode&CON_FGMASK)>>CON_FGSHIFT;
			font_forecolour[0] = consolecolours[col].fr*255;
			font_forecolour[1] = consolecolours[col].fg*255;
			font_forecolour[2] = consolecolours[col].fb*255;
			font_forecolour[3] = (charcode & CON_HALFALPHA)?127:255;

			col = (charcode&CON_BGMASK)>>CON_BGSHIFT;
			font_backcolour[0] = consolecolours[col].fr*255;
			font_backcolour[1] = consolecolours[col].fg*255;
			font_backcolour[2] = consolecolours[col].fb*255;
			font_backcolour[3] = (charcode & CON_NONCLEARBG)?127:0;

			if (charcode & CON_2NDCHARSETTEXT)
			{
				font_forecolour[0] = min(font_forecolour[0]*font->alttint[0], 255);
				font_forecolour[1] = min(font_forecolour[1]*font->alttint[1], 255);
				font_forecolour[2] = min(font_forecolour[2]*font->alttint[2], 255);
			}
		}
	}

	s0 = (float)c->bmx/PLANEWIDTH;
	t0 = (float)c->bmy/PLANEWIDTH;
	s1 = (float)(c->bmx+c->bmw)/PLANEWIDTH;
	t1 = (float)(c->bmy+c->bmh)/PLANEWIDTH;

	switch(c->texplane)
	{
	case DEFAULTPLANE:
		sx = ((px+c->left + dxbias)*(int)vid.width) / (float)vid.rotpixelwidth;
		sy = ((py+c->top + dxbias)*(int)vid.height) / (float)vid.rotpixelheight;
		sw = ((font->charheight)*vid.width) / (float)vid.rotpixelwidth;
		sh = ((font->charheight)*vid.height) / (float)vid.rotpixelheight;
		v = Font_BeginChar(fontplanes.defaultfont);
		break;
	case BITMAPPLANE:
		sx = ((px+c->left + dxbias)*(int)vid.width) / (float)vid.rotpixelwidth;
		sy = ((py+c->top + dxbias)*(int)vid.height) / (float)vid.rotpixelheight;
		sw = ((font->charheight)*vid.width) / (float)vid.rotpixelwidth;
		sh = ((font->charheight)*vid.height) / (float)vid.rotpixelheight;
		v = Font_BeginChar(font->singletexture);
		break;
	case SINGLEPLANE:
		sx = ((px+c->left + dxbias)*(int)vid.width) / (float)vid.rotpixelwidth;
		sy = ((py+c->top + dxbias)*(int)vid.height) / (float)vid.rotpixelheight;
		sw = ((c->bmw)*vid.width) / (float)vid.rotpixelwidth;
		sh = ((c->bmh)*vid.height) / (float)vid.rotpixelheight;
		v = Font_BeginChar(font->singletexture);
		break;
	default:
		sx = ((px+c->left + dxbias)*(int)vid.width) / (float)vid.rotpixelwidth;
		sy = ((py+c->top + dxbias)*(int)vid.height) / (float)vid.rotpixelheight;
		sw = ((c->bmw)*vid.width) / (float)vid.rotpixelwidth;
		sh = ((c->bmh)*vid.height) / (float)vid.rotpixelheight;
		v = Font_BeginChar(fontplanes.texnum[c->texplane]);
		break;
	}

	font_texcoord[v+0][0] = s0;
	font_texcoord[v+0][1] = t0;
	font_texcoord[v+1][0] = s1;
	font_texcoord[v+1][1] = t0;
	font_texcoord[v+2][0] = s1;
	font_texcoord[v+2][1] = t1;
	font_texcoord[v+3][0] = s0;
	font_texcoord[v+3][1] = t1;

	font_coord[v+0][0] = sx;
	font_coord[v+0][1] = sy;
	font_coord[v+1][0] = sx+sw;
	font_coord[v+1][1] = sy;
	font_coord[v+2][0] = sx+sw;
	font_coord[v+2][1] = sy+sh;
	font_coord[v+3][0] = sx;
	font_coord[v+3][1] = sy+sh;

	*(int*)font_forecoloura[v+0] = *(int*)font_forecolour;
	*(int*)font_forecoloura[v+1] = *(int*)font_forecolour;
	*(int*)font_forecoloura[v+2] = *(int*)font_forecolour;
	*(int*)font_forecoloura[v+3] = *(int*)font_forecolour;
	*(int*)font_backcoloura[v+0] = *(int*)font_backcolour;
	*(int*)font_backcoloura[v+1] = *(int*)font_backcolour;
	*(int*)font_backcoloura[v+2] = *(int*)font_backcolour;
	*(int*)font_backcoloura[v+3] = *(int*)font_backcolour;

	return nextx;
}

/*there is no sane way to make this pixel-correct*/
float Font_DrawScaleChar(float px, float py, unsigned int charcode)
{
	struct charcache_s *c;
	float s0, s1;
	float t0, t1;
	float nextx;
	float sx, sy, sw, sh;
	int col;
	int v;
	struct font_s *font = curfont;
	float cw, ch;

//	if (!curfont_scaled)
//		return Font_DrawChar(px, py, charcode);

	if (charcode & CON_2NDCHARSETTEXT)
	{
		if (font->alt)
		{
			font = font->alt;
			charcode &= ~CON_2NDCHARSETTEXT;
		}
		else if ((charcode&CON_CHARMASK) >= 0xe000 && (charcode&CON_CHARMASK) <= 0xe0ff)
			charcode &= ~CON_2NDCHARSETTEXT;	//don't double-dip
	}

	cw = curfont_scale[0];
	ch = curfont_scale[1];

	//crash if there is no current font.
	c = Font_GetChar(font, (CHARIDXTYPE)(charcode&CON_CHARMASK));
	if (!c)
		return px;

	nextx = px + c->advance*cw;

	if ((charcode & CON_CHARMASK) == ' ')
		return nextx;

	if (charcode & CON_BLINKTEXT)
	{
		if (!cl_noblink.ival)
			if ((int)(realtime*3) & 1)
				return nextx;
	}

	if (charcode & CON_RICHFORECOLOUR)
	{
		col = charcode & (CON_2NDCHARSETTEXT|CON_RICHFORECOLOUR|(0xfff<<CON_RICHBSHIFT));
		if (col != font_colourmask)
		{
			if (font_backcolour[3])
				Font_Flush();
			font_colourmask = col;

			font_forecolour[0] = ((col>>CON_RICHRSHIFT)&0xf)*0x11;
			font_forecolour[1] = ((col>>CON_RICHGSHIFT)&0xf)*0x11;
			font_forecolour[2] = ((col>>CON_RICHBSHIFT)&0xf)*0x11;
			font_forecolour[3] = 255;

			font_backcolour[0] = 0;
			font_backcolour[1] = 0;
			font_backcolour[2] = 0;
			font_backcolour[3] = 0;

			if (charcode & CON_2NDCHARSETTEXT)
			{
				font_forecolour[0] = min(font_forecolour[0]*1.16, 255);
				font_forecolour[1] *= 0.54;
				font_forecolour[2] *= 0.41;
			}
		}
	}
	else
	{
		col = charcode & (CON_2NDCHARSETTEXT|CON_NONCLEARBG|CON_BGMASK|CON_FGMASK|CON_HALFALPHA);
		if (col != font_colourmask)
		{
			if (font_backcolour[3] != ((charcode & CON_NONCLEARBG)?127:0))
				Font_Flush();
			font_colourmask = col;

			col = (charcode&CON_FGMASK)>>CON_FGSHIFT;
			font_forecolour[0] = consolecolours[col].fr*255;
			font_forecolour[1] = consolecolours[col].fg*255;
			font_forecolour[2] = consolecolours[col].fb*255;
			font_forecolour[3] = (charcode & CON_HALFALPHA)?127:255;

			col = (charcode&CON_BGMASK)>>CON_BGSHIFT;
			font_backcolour[0] = consolecolours[col].fr*255;
			font_backcolour[1] = consolecolours[col].fg*255;
			font_backcolour[2] = consolecolours[col].fb*255;
			font_backcolour[3] = (charcode & CON_NONCLEARBG)?127:0;

			if (charcode & CON_2NDCHARSETTEXT)
			{
				font_forecolour[0] = min(font_forecolour[0]*font->alttint[0], 255);
				font_forecolour[1] = min(font_forecolour[1]*font->alttint[1], 255);
				font_forecolour[2] = min(font_forecolour[2]*font->alttint[2], 255);
			}
		}
	}

	s0 = (float)c->bmx/PLANEWIDTH;
	t0 = (float)c->bmy/PLANEWIDTH;
	s1 = (float)(c->bmx+c->bmw)/PLANEWIDTH;
	t1 = (float)(c->bmy+c->bmh)/PLANEWIDTH;

	if (c->texplane >= DEFAULTPLANE)
	{
		sx = ((px+c->left*cw));
		sy = ((py+c->top*ch));
		sw = ((font->charheight*cw));
		sh = ((font->charheight*ch));

		if (c->texplane == DEFAULTPLANE)
			v = Font_BeginChar(fontplanes.defaultfont);
		else
			v = Font_BeginChar(font->singletexture);
	}
	else
	{
		sx = (px+c->left*cw);
		sy = (py+c->top*ch);
		sw = ((c->bmw*cw));
		sh = ((c->bmh*ch));
		v = Font_BeginChar(fontplanes.texnum[c->texplane]);
	}

	sx *= (int)vid.width / (float)vid.rotpixelwidth;
	sy *= (int)vid.height / (float)vid.rotpixelheight;
	sw *= (int)vid.width / (float)vid.rotpixelwidth;
	sh *= (int)vid.height / (float)vid.rotpixelheight;

	font_texcoord[v+0][0] = s0;
	font_texcoord[v+0][1] = t0;
	font_texcoord[v+1][0] = s1;
	font_texcoord[v+1][1] = t0;
	font_texcoord[v+2][0] = s1;
	font_texcoord[v+2][1] = t1;
	font_texcoord[v+3][0] = s0;
	font_texcoord[v+3][1] = t1;

	font_coord[v+0][0] = sx;
	font_coord[v+0][1] = sy;
	font_coord[v+1][0] = sx+sw;
	font_coord[v+1][1] = sy;
	font_coord[v+2][0] = sx+sw;
	font_coord[v+2][1] = sy+sh;
	font_coord[v+3][0] = sx;
	font_coord[v+3][1] = sy+sh;

	*(int*)font_forecoloura[v+0] = *(int*)font_forecolour;
	*(int*)font_forecoloura[v+1] = *(int*)font_forecolour;
	*(int*)font_forecoloura[v+2] = *(int*)font_forecolour;
	*(int*)font_forecoloura[v+3] = *(int*)font_forecolour;
	*(int*)font_backcoloura[v+0] = *(int*)font_backcolour;
	*(int*)font_backcoloura[v+1] = *(int*)font_backcolour;
	*(int*)font_backcoloura[v+2] = *(int*)font_backcolour;
	*(int*)font_backcoloura[v+3] = *(int*)font_backcolour;

	return nextx;
}

#endif	//!SERVERONLY
