#include "quakedef.h"

#ifdef MENU_DAT

#ifdef RGLQUAKE
#include "glquake.h"
#ifdef Q3SHADERS
#include "shader.h"
#endif
#endif

#include "pr_common.h"


typedef struct menuedict_s
{
	qboolean	isfree;
	float		freetime; // sv.time when the object was freed
	int			entnum;
	qboolean	readonly;	//world
	void		*fields;
} menuedict_t;



int menuentsize;

// cvars
#define MENUPROGSGROUP "Menu progs control"
cvar_t forceqmenu = SCVAR("forceqmenu", "0");
cvar_t pr_menuqc_coreonerror = SCVAR("pr_menuqc_coreonerror", "1");


//new generic functions.

//float	isfunction(string function_name) = #607;
void PF_isfunction (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char	*name = PR_GetStringOfs(prinst, OFS_PARM0);
	G_FLOAT(OFS_RETURN) = !!PR_FindFunction(prinst, name, PR_CURRENT);
}

//void	callfunction(...) = #605;
void PF_callfunction (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char	*name;
	func_t f;
	if (*prinst->callargc < 1)
		PR_BIError(prinst, "callfunction needs at least one argument\n");
	name = PR_GetStringOfs(prinst, OFS_PARM0+(*prinst->callargc-1)*3);
	f = PR_FindFunction(prinst, name, PR_CURRENT);
	if (f)
		PR_ExecuteProgram(prinst, f);
}

//void	loadfromfile(string file) = #69;
void PF_loadfromfile (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char	*filename = PR_GetStringOfs(prinst, OFS_PARM0);
	char *file = COM_LoadTempFile(filename);

	int size;

	if (!file)
	{
		G_FLOAT(OFS_RETURN) = -1;
		return;
	}

	while(prinst->restoreent(prinst, file, &size, NULL))
	{
		file += size;
	}

	G_FLOAT(OFS_RETURN) = 0;
}

void PF_loadfromdata (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char	*file = PR_GetStringOfs(prinst, OFS_PARM0);

	int size;

	if (!*file)
	{
		G_FLOAT(OFS_RETURN) = -1;
		return;
	}

	while(prinst->restoreent(prinst, file, &size, NULL))
	{
		file += size;
	}

	G_FLOAT(OFS_RETURN) = 0;
}

void PF_parseentitydata(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	void	*ed = G_EDICT(prinst, OFS_PARM0);
	char	*file = PR_GetStringOfs(prinst, OFS_PARM1);

	int size;

	if (!*file)
	{
		G_FLOAT(OFS_RETURN) = -1;
		return;
	}

	if (!prinst->restoreent(prinst, file, &size, ed))
		Con_Printf("parseentitydata: missing opening data\n");
	else
	{
		file += size;
		while(*file < ' ' && *file)
			file++;
		if (*file)
			Con_Printf("parseentitydata: too much data\n");
	}
	
	G_FLOAT(OFS_RETURN) = 0;
}

void PF_mod (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	G_FLOAT(OFS_RETURN) = (float)(((int)G_FLOAT(OFS_PARM0))%((int)G_FLOAT(OFS_PARM1)));
}

char *RemapCvarNameFromDPToFTE(char *name)
{
	if (!stricmp(name, "vid_bitsperpixel"))
		return "vid_bpp";
	if (!stricmp(name, "_cl_playermodel"))
		return "model";
	if (!stricmp(name, "_cl_playerskin"))
		return "skin";
	if (!stricmp(name, "_cl_color"))
		return "topcolor";
	if (!stricmp(name, "_cl_name"))
		return "name";

	if (!stricmp(name, "v_contrast"))
		return "v_contrast";
	if (!stricmp(name, "v_hwgamma"))
		return "vid_hardwaregamma";
	if (!stricmp(name, "showfps"))
		return "show_fps";
	if (!stricmp(name, "sv_progs"))
		return "progs";

	return name;
}

static void PF_menu_cvar (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	cvar_t	*var;
	char	*str;
	
	str = PR_GetStringOfs(prinst, OFS_PARM0);
	str = RemapCvarNameFromDPToFTE(str);
	{
		var = Cvar_Get(str, "", 0, "menu cvars");
		if (var)
		{
			if (var->latched_string)
				G_FLOAT(OFS_RETURN) = atof(var->latched_string);			else
				G_FLOAT(OFS_RETURN) = var->value;
		}
		else
			G_FLOAT(OFS_RETURN) = 0;
	}
}
static void PF_menu_cvar_set (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char	*var_name, *val;
	cvar_t *var;

	var_name = PR_GetStringOfs(prinst, OFS_PARM0);
	var_name = RemapCvarNameFromDPToFTE(var_name);
	val = PR_GetStringOfs(prinst, OFS_PARM1);

	var = Cvar_Get(var_name, val, 0, "QC variables");
	Cvar_Set (var, val);
}
static void PF_menu_cvar_string (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char	*str = PR_GetStringOfs(prinst, OFS_PARM0);
	cvar_t *cv = Cvar_Get(RemapCvarNameFromDPToFTE(str), "", 0, "QC variables");
	G_INT( OFS_RETURN ) = (int)PR_SetString( prinst, cv->string );
}

qboolean M_Vid_GetMove(int num, int *w, int *h);
//a bit pointless really
void PF_cl_getresolution (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	float mode = G_FLOAT(OFS_PARM0);
	float *ret = G_VECTOR(OFS_RETURN);
	int w, h;

	w=h=0;
	M_Vid_GetMove(mode, &w, &h);

	ret[0] = w;
	ret[1] = h;
	ret[2] = 0;
}




void PF_nonfatalobjerror (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char	*s;
	struct edict_s	*ed;
	eval_t *selfp;
	
	s = PF_VarString(prinst, 0, pr_globals);

	PR_StackTrace(prinst);

	selfp = PR_FindGlobal(prinst, "self", PR_CURRENT);
	if (selfp && selfp->_int)
	{
		ed = PROG_TO_EDICT(prinst, selfp->_int);

		PR_PrintEdict(prinst, ed);


		if (developer.value)
			*prinst->pr_trace = 2;
		else
		{
			ED_Free (prinst, ed);
		}
	}

	Con_Printf ("======OBJECT ERROR======\n%s\n", s);
}






//float	isserver(void)  = #60;
void PF_isserver (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	G_FLOAT(OFS_RETURN) = sv.state != ss_dead;
}

//float	clientstate(void)  = #62;
void PF_clientstate (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	G_FLOAT(OFS_RETURN) = cls.state >= ca_connected ? 2 : 1;	//fit in with netquake	 (we never run a menu.dat dedicated)
}

//too specific to the prinst's builtins.
static void PF_Fixme (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	Con_Printf("\n");

	prinst->RunError(prinst, "\nBuiltin %i not implemented.\nMenu is not compatable.", prinst->lastcalledbuiltinnumber);
	PR_BIError (prinst, "bulitin not implemented");
}



void PF_CL_precache_sound (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char	*str;
	
	str = PR_GetStringOfs(prinst, OFS_PARM0);

	if (S_PrecacheSound(str))
		G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
	else
		G_INT(OFS_RETURN) = 0;
}


void PF_CL_is_cached_pic (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char	*str;
	
	str = PR_GetStringOfs(prinst, OFS_PARM0);

//	if (Draw_IsCached)
//		G_FLOAT(OFS_RETURN) = !!Draw_IsCached(str);
//	else
		G_FLOAT(OFS_RETURN) = 1;
}

void PF_CL_precache_pic (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char	*str;
	mpic_t	*pic;

	str = PR_GetStringOfs(prinst, OFS_PARM0);

	if (cls.state && !sv.active)
		CL_CheckOrEnqueDownloadFile(str, str);

	pic = Draw_SafeCachePic(str);

	if (pic)
		G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
	else
		G_INT(OFS_RETURN) = 0;
}

void PF_CL_free_pic (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char	*str;
	
	str = PR_GetStringOfs(prinst, OFS_PARM0);

	//we don't support this.
}


//float	drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag) = #454;
void PF_CL_drawcharacter (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	float *pos = G_VECTOR(OFS_PARM0);
	int chara = G_FLOAT(OFS_PARM1);
	float *size = G_VECTOR(OFS_PARM2);
	float *rgb = G_VECTOR(OFS_PARM3);
	float alpha = G_FLOAT(OFS_PARM4);
//	float flag = G_FLOAT(OFS_PARM5);

	const float fsize = 0.0625;
	float frow, fcol;

	if (!chara)
	{
		G_FLOAT(OFS_RETURN) = -1;	//was null..
		return;
	}

	chara &= 255;
	frow = (chara>>4)*fsize;
	fcol = (chara&15)*fsize;

	if (Draw_ImageColours)
		Draw_ImageColours(rgb[0], rgb[1], rgb[2], alpha);
	if (Draw_Image)
	{
		if (qrenderer == QR_SOFTWARE)
		{
			extern cvar_t vid_conwidth, vid_conheight;
			float xratio, yratio;
			// this has to be done this way because CSQC drawing needs
			// to respect these cvars
			xratio = vid.width / vid_conwidth.value;
			yratio = vid.height / vid_conheight.value;
			Draw_Image(pos[0]*xratio, pos[1]*yratio, size[0]*xratio, size[1]*yratio, fcol, frow, fcol+fsize, frow+fsize, Draw_CachePic("conchars"));
		}
		else
			Draw_Image(pos[0], pos[1], size[0], size[1], fcol, frow, fcol+fsize, frow+fsize, Draw_CachePic("conchars"));
	}

	G_FLOAT(OFS_RETURN) = 1;
}
//float	drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) = #455;
void PF_CL_drawstring (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	float *pos = G_VECTOR(OFS_PARM0);
	char *text = PR_GetStringOfs(prinst, OFS_PARM1);
	float *size = G_VECTOR(OFS_PARM2);
//	float *rgb = G_VECTOR(OFS_PARM3);
//	float alpha = G_FLOAT(OFS_PARM4);
//	float flag = G_FLOAT(OFS_PARM5);

	if (!text)
	{
		G_FLOAT(OFS_RETURN) = -1;	//was null..
		return;
	}

	while(*text)
	{
		G_FLOAT(OFS_PARM1) = *text++;
		PF_CL_drawcharacter(prinst, pr_globals);
		pos[0] += size[0];
	}
}
#ifdef Q3SHADERS
void GLDraw_ShaderPic (int x, int y, int width, int height, shader_t *pic, float r, float g, float b, float a);
#endif
//float	drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag) = #456;
void PF_CL_drawpic (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	float *pos = G_VECTOR(OFS_PARM0);
	char *picname = PR_GetStringOfs(prinst, OFS_PARM1);
	float *size = G_VECTOR(OFS_PARM2);
	float *rgb = G_VECTOR(OFS_PARM3);
	float alpha = G_FLOAT(OFS_PARM4);
	float flag = G_FLOAT(OFS_PARM5);

	mpic_t *p;

#ifdef RGLQUAKE
	if (qrenderer == QR_OPENGL)
	{
#ifdef Q3SHADERS
		shader_t *s;

		s = R_RegisterCustom(picname, NULL);
		if (s)
		{
			GLDraw_ShaderPic(pos[0], pos[1], size[0], size[1], s, rgb[0], rgb[1], rgb[2], alpha);
			return;
		}
#endif

		if (flag == 1)	//add
			qglBlendFunc(GL_SRC_ALPHA, GL_ONE);
		else if(flag == 2)	//modulate
			qglBlendFunc(GL_DST_COLOR, GL_ZERO);
		else if(flag == 3)	//modulate*2
			qglBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
		else	//blend
			qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}
#endif

	p = Draw_SafeCachePic(picname);

	if (Draw_ImageColours)
		Draw_ImageColours(rgb[0], rgb[1], rgb[2], alpha);
	if (Draw_Image)
		Draw_Image(pos[0], pos[1], size[0], size[1], 0, 0, 1, 1, p);

#ifdef RGLQUAKE
	if (qrenderer == QR_OPENGL)
		qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#endif

	G_FLOAT(OFS_RETURN) = 1;
}
//float	drawfill(vector position, vector size, vector rgb, float alpha, float flag) = #457;
void PF_CL_drawfill (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	float *pos = G_VECTOR(OFS_PARM0);
	float *size = G_VECTOR(OFS_PARM1);
	float *rgb = G_VECTOR(OFS_PARM2);
	float alpha = G_FLOAT(OFS_PARM3);
#ifdef RGLQUAKE
	if (qrenderer == QR_OPENGL)
	{
		qglColor4f(rgb[0], rgb[1], rgb[2], alpha);

		qglDisable(GL_TEXTURE_2D);

		qglBegin(GL_QUADS);
		qglVertex2f(pos[0],			pos[1]);
		qglVertex2f(pos[0]+size[0],	pos[1]);
		qglVertex2f(pos[0]+size[0],	pos[1]+size[1]);
		qglVertex2f(pos[0],			pos[1]+size[1]);
		qglEnd();

		qglEnable(GL_TEXTURE_2D);
	}
#endif
	G_FLOAT(OFS_RETURN) = 1;
}
//void	drawsetcliparea(float x, float y, float width, float height) = #458;
void PF_CL_drawsetcliparea (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	float x = G_FLOAT(OFS_PARM0), y = G_FLOAT(OFS_PARM1), w = G_FLOAT(OFS_PARM2), h = G_FLOAT(OFS_PARM3);

#ifdef RGLQUAKE
	if (qrenderer == QR_OPENGL && qglScissor)
	{

		x *= (float)glwidth/vid.width;
		y *= (float)glheight/vid.height;

		w *= (float)glwidth/vid.width;
		h *= (float)glheight/vid.height;

		//add a pixel because this makes DP's menus come out right.
		x-=1;
		y-=1;
		w+=2;
		h+=2;


		qglScissor (x, glheight-(y+h), w, h);
		qglEnable(GL_SCISSOR_TEST);
		G_FLOAT(OFS_RETURN) = 1;
		return;
	}
#endif
	G_FLOAT(OFS_RETURN) = 0;
}
//void	drawresetcliparea(void) = #459;
void PF_CL_drawresetcliparea (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
#ifdef RGLQUAKE
	if (qrenderer == QR_OPENGL)
	{
		qglDisable(GL_SCISSOR_TEST);
		G_FLOAT(OFS_RETURN) = 1;
		return;
	}
#endif
	G_FLOAT(OFS_RETURN) = 0;
}

//void (float width, vector rgb, float alpha, float flags, vector pos1, ...) drawline;
void PF_CL_drawline (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	float width = G_FLOAT(OFS_PARM0);
	float *rgb = G_VECTOR(OFS_PARM1);
	float alpha = G_FLOAT(OFS_PARM2);
	float flags = G_FLOAT(OFS_PARM3);
	float *pos = G_VECTOR(OFS_PARM4);
	int numpoints = *prinst->callargc-4;

#ifdef RGLQUAKE	// :(

	if (qrenderer == QR_OPENGL)
	{
		qglColor4f(rgb[0], rgb[1], rgb[2], alpha);
		qglBegin(GL_LINES);
		while (numpoints-->0)
		{
			qglVertex3fv(pos);
			pos += 3;
		}

		qglEnd();
	}
#endif
}

//vector  drawgetimagesize(string pic) = #460;
void PF_CL_drawgetimagesize (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char *picname = PR_GetStringOfs(prinst, OFS_PARM0);
	mpic_t *p = Draw_SafeCachePic(picname);

	float *ret = G_VECTOR(OFS_RETURN);

	if (!p)
		p = Draw_SafeCachePic(va("%s.tga", picname));

	if (p)
	{
		ret[0] = p->width;
		ret[1] = p->height;
		ret[2] = 0;
	}
	else
	{
		ret[0] = 0;
		ret[1] = 0;
		ret[2] = 0;
	}
}

//void	setkeydest(float dest) 	= #601;
void PF_cl_setkeydest (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	switch((int)G_FLOAT(OFS_PARM0))
	{
	case 0:
		// key_game
		key_dest = key_game;
		break;
	case 2:
		// key_menu
		key_dest = key_menu;
		m_state = m_menu_dat;
		break;
	case 1:
		// key_message
		// key_dest = key_message
		// break;
	default:
		PR_BIError (prinst, "PF_setkeydest: wrong destination %i !\n",(int)G_FLOAT(OFS_PARM0));
	}
}
//float	getkeydest(void)	= #602;
void PF_cl_getkeydest (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	switch(key_dest)
	{
	case key_game:
		G_FLOAT(OFS_RETURN) = 0;
		break;
	case key_menu:
		if (m_state == m_menu_dat)
			G_FLOAT(OFS_RETURN) = 2;
		else
			G_FLOAT(OFS_RETURN) = 3;
		break;
	case 1:
		// key_message
		// key_dest = key_message
		// break;
	default:
		G_FLOAT(OFS_RETURN) = 3;
	}
}

//void	setmousetarget(float trg) = #603;
void PF_cl_setmousetarget (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	extern int mouseusedforgui;
	switch ((int)G_FLOAT(OFS_PARM0))
	{
	case 1:
		mouseusedforgui = true;
		break;
	case 2:
		mouseusedforgui = false;
		break;
	default:
		PR_BIError(prinst, "PF_setmousetarget: not a valid destination\n");
	}
}

//float	getmousetarget(void)	  = #604;
void PF_cl_getmousetarget (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
}

int MP_TranslateDPtoFTECodes(int code);
//string	keynumtostring(float keynum) = #609;
void PF_cl_keynumtostring (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	int code = G_FLOAT(OFS_PARM0);
	char *keyname = PF_TempStr(prinst);

	code = MP_TranslateDPtoFTECodes (code);

	strcpy(keyname, Key_KeynumToString(code));
	RETURN_SSTRING(keyname);
}

void PF_cl_getkeybind (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char *binding = Key_GetBinding(G_FLOAT(OFS_PARM0));
	char *result = PF_TempStr(prinst);
	if (!binding)
		binding = "";
	Q_strncpyz(result, binding, MAXTEMPBUFFERLEN);
	RETURN_SSTRING(result);
}

int MP_TranslateDPtoFTECodes(int code);
//string	findkeysforcommand(string command) = #610;
void PF_cl_findkeysforcommand (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char *cmdname = PR_GetStringOfs(prinst, OFS_PARM0);
	int keynums[2];
	char *keyname = PF_TempStr(prinst);

	M_FindKeysForCommand(cmdname, keynums);

	keyname[0] = '\0';

	Q_strncatz (keyname, va(" \'%i\'", MP_TranslateFTEtoDPCodes(keynums[0])), MAXTEMPBUFFERLEN);
	Q_strncatz (keyname, va(" \'%i\'", MP_TranslateFTEtoDPCodes(keynums[1])), MAXTEMPBUFFERLEN);

	RETURN_SSTRING(keyname);
}

//vector	getmousepos(void)  	= #66;
void PF_cl_getmousepos (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	float *ret = G_VECTOR(OFS_RETURN);
	extern int mousemove_x, mousemove_y;

	ret[0] = mousemove_x;
	ret[1] = mousemove_y;

	mousemove_x=0;
	mousemove_y=0;

/*	ret[0] = mousecursor_x;
	ret[1] = mousecursor_y;*/
	ret[2] = 0;
}


static void PF_Remove_ (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	menuedict_t *ed;
	
	ed = (void*)G_EDICT(prinst, OFS_PARM0);

	if (ed->isfree)
	{
		Con_DPrintf("Tried removing free entity\n");
		return;
	}

	ED_Free (prinst, (void*)ed);
}

static void PF_CopyEntity (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	menuedict_t *in, *out;

	in = (menuedict_t*)G_EDICT(prinst, OFS_PARM0);
	out = (menuedict_t*)G_EDICT(prinst, OFS_PARM1);

	memcpy(out->fields, in->fields, menuentsize);
}

#ifdef CL_MASTER
#include "cl_master.h"

typedef enum{
	SLIST_HOSTCACHEVIEWCOUNT,
	SLIST_HOSTCACHETOTALCOUNT,
	SLIST_MASTERQUERYCOUNT,
	SLIST_MASTERREPLYCOUNT,
	SLIST_SERVERQUERYCOUNT,
	SLIST_SERVERREPLYCOUNT,
	SLIST_SORTFIELD,
	SLIST_SORTDESCENDING
} hostcacheglobal_t;

void PF_M_gethostcachevalue (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	hostcacheglobal_t hcg = G_FLOAT(OFS_PARM0);
	G_FLOAT(OFS_RETURN) = 0;
	switch(hcg)
	{
	case SLIST_HOSTCACHEVIEWCOUNT:
		G_FLOAT(OFS_RETURN) = Master_NumSorted();
		return;
	case SLIST_HOSTCACHETOTALCOUNT:
		CL_QueryServers();
		NET_CheckPollSockets();
		G_FLOAT(OFS_RETURN) = Master_TotalCount();
		return;

	case SLIST_MASTERQUERYCOUNT:
	case SLIST_MASTERREPLYCOUNT:
	case SLIST_SERVERQUERYCOUNT:
	case SLIST_SERVERREPLYCOUNT:
		G_FLOAT(OFS_RETURN) = 0;
		return;

	case SLIST_SORTFIELD:
		G_FLOAT(OFS_RETURN) = Master_GetSortField();
		return;
	case SLIST_SORTDESCENDING:
		G_FLOAT(OFS_RETURN) = Master_GetSortDescending();
		return;
	default:
		return;
	}
}

//void 	resethostcachemasks(void) = #615;
void PF_M_resethostcachemasks(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	Master_ClearMasks();
}
//void 	sethostcachemaskstring(float mask, float fld, string str, float op) = #616;
void PF_M_sethostcachemaskstring(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	int mask = G_FLOAT(OFS_PARM0);
	int field = G_FLOAT(OFS_PARM1);
	char *str = PR_GetStringOfs(prinst, OFS_PARM2);
	int op = G_FLOAT(OFS_PARM3);

	Master_SetMaskString(mask, field, str, op);
}
//void	sethostcachemasknumber(float mask, float fld, float num, float op) = #617;
void PF_M_sethostcachemasknumber(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	int mask = G_FLOAT(OFS_PARM0);
	int field = G_FLOAT(OFS_PARM1);
	int str = G_FLOAT(OFS_PARM2);
	int op = G_FLOAT(OFS_PARM3);

	Master_SetMaskInteger(mask, field, str, op);
}
//void 	resorthostcache(void) = #618;
void PF_M_resorthostcache(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
}
//void	sethostcachesort(float fld, float descending) = #619;
void PF_M_sethostcachesort(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	Master_SetSortField(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));
}
//void	refreshhostcache(void) = #620;
void PF_M_refreshhostcache(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	MasterInfo_Begin();
}
//float	gethostcachenumber(float fld, float hostnr) = #621;
void PF_M_gethostcachenumber(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	float ret = 0;
	int keynum = G_FLOAT(OFS_PARM0);
	int svnum = G_FLOAT(OFS_PARM1);
	serverinfo_t *sv;
	sv = Master_SortedServer(svnum);

	ret = Master_ReadKeyFloat(sv, keynum);

	G_FLOAT(OFS_RETURN) = ret;
}
void PF_M_gethostcachestring (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char *keyname = PF_TempStr(prinst);
	char *ret = "";
	int keynum = G_FLOAT(OFS_PARM0);
	int svnum = G_FLOAT(OFS_PARM1);
	serverinfo_t *sv;
	sv = Master_SortedServer(svnum);

	ret = Master_ReadKeyString(sv, keynum);

	Q_strncpyz(keyname, ret, MAXTEMPBUFFERLEN);
	RETURN_SSTRING(keyname);
}

//float	gethostcacheindexforkey(string key) = #622;
void PF_M_gethostcacheindexforkey(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char *keyname = PR_GetStringOfs(prinst, OFS_PARM0);

	G_FLOAT(OFS_RETURN) = Master_KeyForName(keyname);
}
//void	addwantedhostcachekey(string key) = #623;
void PF_M_addwantedhostcachekey(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	PF_M_gethostcacheindexforkey(prinst, pr_globals);
}
#else

void PF_gethostcachevalue (progfuncs_t *prinst, struct globalvars_s *pr_globals){G_FLOAT(OFS_RETURN) = 0;}
void PF_gethostcachestring (progfuncs_t *prinst, struct globalvars_s *pr_globals) {G_INT(OFS_RETURN) = 0;}
//void 	resethostcachemasks(void) = #615;
void PF_M_resethostcachemasks(progfuncs_t *prinst, struct globalvars_s *pr_globals){}
//void 	sethostcachemaskstring(float mask, float fld, string str, float op) = #616;
void PF_M_sethostcachemaskstring(progfuncs_t *prinst, struct globalvars_s *pr_globals){}
//void	sethostcachemasknumber(float mask, float fld, float num, float op) = #617;
void PF_M_sethostcachemasknumber(progfuncs_t *prinst, struct globalvars_s *pr_globals){}
//void 	resorthostcache(void) = #618;
void PF_M_resorthostcache(progfuncs_t *prinst, struct globalvars_s *pr_globals){}
//void	sethostcachesort(float fld, float descending) = #619;
void PF_M_sethostcachesort(progfuncs_t *prinst, struct globalvars_s *pr_globals){}
//void	refreshhostcache(void) = #620;
void PF_M_refreshhostcache(progfuncs_t *prinst, struct globalvars_s *pr_globals) {}
//float	gethostcachenumber(float fld, float hostnr) = #621;
void PF_M_gethostcachenumber(progfuncs_t *prinst, struct globalvars_s *pr_globals){G_FLOAT(OFS_RETURN) = 0;}
//float	gethostcacheindexforkey(string key) = #622;
void PF_M_gethostcacheindexforkey(progfuncs_t *prinst, struct globalvars_s *pr_globals){G_FLOAT(OFS_RETURN) = 0;}
//void	addwantedhostcachekey(string key) = #623;
void PF_M_addwantedhostcachekey(progfuncs_t *prinst, struct globalvars_s *pr_globals){}
#endif


void PF_localsound (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char *soundname = PR_GetStringOfs(prinst, OFS_PARM0);
	S_LocalSound (soundname);
}

#define skip1 PF_Fixme,
#define skip5 skip1 skip1 skip1 skip1 skip1
#define skip10 skip5 skip5
#define skip50 skip10 skip10 skip10 skip10 skip10
#define skip100 skip50 skip50

void PF_menu_checkextension (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	//yeah, this is a stub... not sure what form extex
	G_FLOAT(OFS_RETURN) = 0;
}

void PF_gettime (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	G_FLOAT(OFS_RETURN) = *prinst->parms->gametime;
}

void PF_CL_precache_file (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
}

//entity	findchainstring(.string _field, string match) = #26;
void PF_menu_findchain (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	int i, f;
	char *s, *t;
	menuedict_t *ent, *chain;	//note, all edicts share the common header, but don't use it's fields!
	eval_t *val;

	chain = (menuedict_t *) *prinst->parms->sv_edicts;

	f = G_INT(OFS_PARM0)+prinst->fieldadjust;
	s = PR_GetStringOfs(prinst, OFS_PARM1);

	for (i = 1; i < *prinst->parms->sv_num_edicts; i++)
	{
		ent = (menuedict_t *)EDICT_NUM(prinst, i);
		if (ent->isfree)
			continue;
		t = *(string_t *)&((float*)ent->fields)[f] + prinst->stringtable;
		if (!t)
			continue;
		if (strcmp(t, s))
			continue;

		val = prinst->GetEdictFieldValue(prinst, (void*)ent, "chain", NULL);
		if (val)
			val->edict = EDICT_TO_PROG(prinst, (void*)chain);
		chain = ent;
	}

	RETURN_EDICT(prinst, (void*)chain);
}
//entity	findchainfloat(.float _field, float match) = #27;
void PF_menu_findchainfloat (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	int i, f;
	float s;
	menuedict_t	*ent, *chain;	//note, all edicts share the common header, but don't use it's fields!
	eval_t *val;

	chain = (menuedict_t *) *prinst->parms->sv_edicts;

	f = G_INT(OFS_PARM0)+prinst->fieldadjust;
	s = G_FLOAT(OFS_PARM1);

	for (i = 1; i < *prinst->parms->sv_num_edicts; i++)
	{
		ent = (menuedict_t*)EDICT_NUM(prinst, i);
		if (ent->isfree)
			continue;
		if (((float *)ent->fields)[f] != s)
			continue;

		val = prinst->GetEdictFieldValue(prinst, (void*)ent, "chain", NULL);
		if (val)
			val->edict = EDICT_TO_PROG(prinst, (void*)chain);
		chain = ent;
	}

	RETURN_EDICT(prinst, (void*)chain);
}

void PF_etof(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	G_FLOAT(OFS_RETURN) = G_EDICTNUM(prinst, OFS_PARM0);
}
void PF_ftoe(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	int entnum = G_FLOAT(OFS_PARM0);

	RETURN_EDICT(prinst, EDICT_NUM(prinst, entnum));
}

void PF_IsNotNull(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	int str = G_INT(OFS_PARM0);
	G_FLOAT(OFS_RETURN) = !!str;
}

void PF_cl_stringtokeynum(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	int i;
	int modifier;
	char *s;

	s = PR_GetStringOfs(prinst, OFS_PARM0);
	i = Key_StringToKeynum(s, &modifier);
	if (i < 0 || modifier != ~0)
	{
		G_FLOAT(OFS_RETURN) = -1;
		return;
	}
	i = MP_TranslateFTEtoDPCodes(i);
	G_FLOAT(OFS_RETURN) = i;
}


//float 	altstr_count(string str) = #82;
//returns number of single quoted strings in the string.
void PF_altstr_count(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char *s;
	int count = 0;
	s = PR_GetStringOfs(prinst, OFS_PARM0);
	for (;*s;s++)
	{
		if (*s == '\\')
		{
			if (!*++s)
				break;
		}
		else if (*s == '\'')
			count++;
	}
	G_FLOAT(OFS_RETURN) = count/2;
}
//string  altstr_prepare(string str) = #83;
void PF_altstr_prepare(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char *outstr, *out;
	char *instr, *in;
	int size;

//	VM_SAFEPARMCOUNT( 1, VM_altstr_prepare );

	instr = PR_GetStringOfs(prinst, OFS_PARM0 );
	//VM_CheckEmptyString( instr );
	outstr = PF_TempStr(prinst);

	for( out = outstr, in = instr, size = MAXTEMPBUFFERLEN - 1 ; size && *in ; size--, in++, out++ )
		if( *in == '\'' ) {
			*out++ = '\\';
			*out = '\'';
			size--;
		} else
			*out = *in;
	*out = 0;

	G_INT( OFS_RETURN ) = (int)PR_SetString( prinst, outstr );
}
//string  altstr_get(string str, float num) = #84;
void PF_altstr_get(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	char *altstr, *pos, *outstr, *out;
	int count, size;

//	VM_SAFEPARMCOUNT( 2, VM_altstr_get );

	altstr = PR_GetStringOfs(prinst, OFS_PARM0 );
	//VM_CheckEmptyString( altstr );

	count = G_FLOAT( OFS_PARM1 );
	count = count * 2 + 1;

	for( pos = altstr ; *pos && count ; pos++ )
		if( *pos == '\\' && !*++pos )
			break;
		else if( *pos == '\'' )
			count--;

	if( !*pos ) {
		G_INT( OFS_RETURN ) = (int)PR_SetString( prinst, "" );
		return;
	}

    outstr = PF_TempStr(prinst);
	for( out = outstr, size = MAXTEMPBUFFERLEN - 1 ; size && *pos ; size--, pos++, out++ )
		if( *pos == '\\' ) {
			if( !*++pos )
				break;
			*out = *pos;
			size--;
		} else if( *pos == '\'' )
			break;
		else
			*out = *pos;

	*out = 0;
	G_INT( OFS_RETURN ) = (int)PR_SetString( prinst, outstr );
}
//string  altstr_set(string str, float num, string set) = #85
void PF_altstr_set(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
	int num;
	char *altstr, *str;
	char *in;
	char *outstr, *out;

//	VM_SAFEPARMCOUNT( 3, VM_altstr_set );

	altstr = PR_GetStringOfs(prinst, OFS_PARM0 );
	//VM_CheckEmptyString( altstr );

	num = G_FLOAT( OFS_PARM1 );

	str = PR_GetStringOfs(prinst, OFS_PARM2 );
	//VM_CheckEmptyString( str );

	outstr = out = PF_TempStr(prinst);
	for( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ )
		if( *in == '\\' && !*++in )
			break;
		else if( *in == '\'' )
			num--;

	if( !in ) {
		G_INT( OFS_RETURN ) = (int)PR_SetString( prinst, "" );
		return;
	}
	// copy set in
	for( ; *str; *out++ = *str++ );
	// now jump over the old contents
	for( ; *in ; in++ )
		if( *in == '\'' || (*in == '\\' && !*++in) )
			break;

	if( !in ) {
		G_INT( OFS_RETURN ) = (int)PR_SetString( prinst, "" );
		return;
	}

	strcpy( out, in );
	G_INT( OFS_RETURN ) = (int)PR_SetString( prinst, outstr );

}

builtin_t menu_builtins[] = {
//0
	PF_Fixme,
	PF_menu_checkextension,				//void 	checkextension(string ext) = #1;
	PF_error,
	PF_nonfatalobjerror,
	PF_print,
	skip1				//void 	bprint(string text,...)	= #5;
	skip1				//void	sprint(float clientnum, string text,...) = #6;
	skip1				//void 	centerprint(string text,...) = #7;
	PF_normalize,		//vector	normalize(vector v) 	= #8;
	PF_vlen,			//float 	vlen(vector v)			= #9;
//10
	PF_vectoyaw,//10		//float  	vectoyaw(vector v)		= #10;
	PF_vectoangles,//11		//vector 	vectoangles(vector v)	= #11;
	PF_random,//12
	PF_localcmd,//13
	PF_menu_cvar,//14
	PF_menu_cvar_set,//15
	PF_dprint,//16
	PF_ftos,//17
	PF_fabs,//18
	PF_vtos,//19

	PF_etos,//20
	PF_stof,//21
	PF_Spawn,//22
	PF_Remove_,//23
	PF_FindString,//24
	PF_FindFloat,//25
	PF_menu_findchain,//26			//entity	findchainstring(.string _field, string match) = #26;
	PF_menu_findchainfloat,//27		//entity	findchainfloat(.float _field, float match) = #27;
	PF_CL_precache_file,//28
	PF_CL_precache_sound,//29

//30
	PF_coredump,				//void	coredump(void) = #30;
	PF_traceon,				//void	traceon(void) = #31;
	PF_traceoff,				//void	traceoff(void) = #32;
	PF_eprint,				//void	eprint(entity e)  = #33;
	PF_rint,
	PF_floor,
	PF_ceil,
	PF_nextent,
	PF_Sin,
	PF_Cos,
//40
	PF_Sqrt,
	PF_randomvector,
	PF_registercvar,
	PF_min,
	PF_max,
	PF_bound,
	PF_pow,
	PF_CopyEntity,
	PF_fopen,
	PF_fclose,
//50
	PF_fgets,
	PF_fputs,
	PF_strlen,
	PF_strcat,
	PF_substring,
	PF_stov,
	PF_dupstring,
	PF_forgetstring,
	PF_Tokenize,
	PF_ArgV,
//60
	PF_isserver,
	skip1						//float	clientcount(void)  = #61;
	PF_clientstate,
	skip1						//void	clientcommand(float client, string s)  = #63;
	skip1						//void	changelevel(string map)  = #64;
	PF_localsound,
	PF_cl_getmousepos,
	PF_gettime,
	PF_loadfromdata,
	PF_loadfromfile,
//70
	PF_mod,//0
	PF_menu_cvar_string,//1
	PF_Fixme,//2				//void	crash(void)	= #72;
	PF_Fixme,//3				//void	stackdump(void) = #73;
	PF_search_begin,//4
	PF_search_end,//5
	PF_search_getsize ,//6
	PF_search_getfilename,//7
	PF_chr2str,//8
	PF_etof,//9				//float 	etof(entity ent) = #79;
//80
	PF_ftoe,//10
	PF_IsNotNull,


	PF_altstr_count, //float 	altstr_count(string str) = #82;
	PF_altstr_prepare, //string  altstr_prepare(string str) = #83;
	PF_altstr_get, //string  altstr_get(string str, float num) = #84;
	PF_altstr_set, //string  altstr_set(string str, float num, string set) = #85

	skip1
	skip1
	skip1
	skip1
//90
	skip10
//100
	skip100
//200
	skip100
//300
	skip100
//400
	skip50
//450
	PF_Fixme,//0
	PF_CL_is_cached_pic,//1
	PF_CL_precache_pic,//2
	PF_CL_free_pic,//3
	PF_CL_drawcharacter,//4
	PF_CL_drawstring,//5
	PF_CL_drawpic,//6
	PF_CL_drawfill,//7
	PF_CL_drawsetcliparea,//8
	PF_CL_drawresetcliparea,//9

//460
	PF_CL_drawgetimagesize,//10
	skip1
	skip1
	skip1
	skip1
	skip1
	skip1
	skip1
	skip1
	skip1
//470
	skip10
//480
	skip10
//490
	skip10
//500
	skip100
//600
	skip1
	PF_cl_setkeydest,
	PF_cl_getkeydest,
	PF_cl_setmousetarget,
	PF_cl_getmousetarget,
	PF_callfunction,
	skip1				//void	writetofile(float fhandle, entity ent) = #606;
	PF_isfunction,
	PF_cl_getresolution,
	PF_cl_keynumtostring,
	PF_cl_findkeysforcommand,
	PF_M_gethostcachevalue,
	PF_M_gethostcachestring,
	PF_parseentitydata,			//void 	parseentitydata(entity ent, string data) = #613;

	PF_cl_stringtokeynum,

	PF_M_resethostcachemasks,
	PF_M_sethostcachemaskstring,
	PF_M_sethostcachemasknumber,
	PF_M_resorthostcache,
	PF_M_sethostcachesort,
	PF_M_refreshhostcache,
	PF_M_gethostcachenumber,
	PF_M_gethostcacheindexforkey,
	PF_M_addwantedhostcachekey
};
int menu_numbuiltins = sizeof(menu_builtins)/sizeof(menu_builtins[0]);




void M_Init_Internal (void);
void M_DeInit_Internal (void);

int inmenuprogs;
progfuncs_t *menuprogs;
progparms_t menuprogparms;
menuedict_t *menu_edicts;
int num_menu_edicts;

func_t mp_init_function;
func_t mp_shutdown_function;
func_t mp_draw_function;
func_t mp_keydown_function;
func_t mp_keyup_function;
func_t mp_toggle_function;

float *mp_time;

jmp_buf mp_abort;


void MP_Shutdown (void)
{
	extern int mouseusedforgui;
	func_t temp;
	if (!menuprogs)
		return;
/*
	{
		char *buffer;
		int size = 1024*1024*8;
		buffer = Z_Malloc(size);
		menuprogs->save_ents(menuprogs, buffer, &size, 1);
		COM_WriteFile("menucore.txt", buffer, size);
		Z_Free(buffer);
	}
*/
	temp = mp_shutdown_function;
	mp_shutdown_function = 0;
	if (temp && !inmenuprogs)
		PR_ExecuteProgram(menuprogs, temp);

	PR_fclose_progs(menuprogs);
	search_close_progs(menuprogs, true);

	CloseProgs(menuprogs);
	menuprogs = NULL;

	key_dest = key_game;
	m_state = 0;

	mouseusedforgui = false;

	M_Init_Internal();

	if (inmenuprogs)	//something in the menu caused the problem, so...
	{
		inmenuprogs = 0;
		longjmp(mp_abort, 1);
	}
}

int COM_FileSize(char *path);
pbool QC_WriteFile(char *name, void *data, int len);
void *VARGS PR_CB_Malloc(int size);	//these functions should be tracked by the library reliably, so there should be no need to track them ourselves.
void VARGS PR_CB_Free(void *mem);

//Any menu builtin error or anything like that will come here.
void VARGS Menu_Abort (char *format, ...)
{
	va_list		argptr;
	char		string[1024];

	va_start (argptr, format);
	vsnprintf (string,sizeof(string)-1, format,argptr);
	va_end (argptr);

	Con_Printf("Menu_Abort: %s\nShutting down menu.dat\n", string);

	if (pr_menuqc_coreonerror.value)
	{
		char *buffer;
		int size = 1024*1024*8;
		buffer = Z_Malloc(size);
		menuprogs->save_ents(menuprogs, buffer, &size, 3);
		COM_WriteFile("menucore.txt", buffer, size);
		Z_Free(buffer);
	}

	MP_Shutdown();
}

double  menutime;
void MP_Init (void)
{
	if (!qrenderer)
	{
		return;
	}

	if (forceqmenu.value)
		return;

	M_DeInit_Internal();


	menuprogparms.progsversion = PROGSTRUCT_VERSION;
	menuprogparms.ReadFile = COM_LoadStackFile;//char *(*ReadFile) (char *fname, void *buffer, int *len);
	menuprogparms.FileSize = COM_FileSize;//int (*FileSize) (char *fname);	//-1 if file does not exist
	menuprogparms.WriteFile = QC_WriteFile;//bool (*WriteFile) (char *name, void *data, int len);
	menuprogparms.printf = (void *)Con_Printf;//Con_Printf;//void (*printf) (char *, ...);
	menuprogparms.Sys_Error = Sys_Error;
	menuprogparms.Abort = Menu_Abort;
	menuprogparms.edictsize = sizeof(menuedict_t);

	menuprogparms.entspawn = NULL;//void (*entspawn) (struct edict_s *ent);	//ent has been spawned, but may not have all the extra variables (that may need to be set) set
	menuprogparms.entcanfree = NULL;//bool (*entcanfree) (struct edict_s *ent);	//return true to stop ent from being freed
	menuprogparms.stateop = NULL;//StateOp;//void (*stateop) (float var, func_t func);
	menuprogparms.cstateop = NULL;//CStateOp;
	menuprogparms.cwstateop = NULL;//CWStateOp;
	menuprogparms.thinktimeop = NULL;//ThinkTimeOp;

	//used when loading a game
	menuprogparms.builtinsfor = NULL;//builtin_t *(*builtinsfor) (int num);	//must return a pointer to the builtins that were used before the state was saved.
	menuprogparms.loadcompleate = NULL;//void (*loadcompleate) (int edictsize);	//notification to reset any pointers.

	menuprogparms.memalloc = PR_CB_Malloc;//void *(*memalloc) (int size);	//small string allocation	malloced and freed randomly
	menuprogparms.memfree = PR_CB_Free;//void (*memfree) (void * mem);


	menuprogparms.globalbuiltins = menu_builtins;//builtin_t *globalbuiltins;	//these are available to all progs
	menuprogparms.numglobalbuiltins = menu_numbuiltins;

	menuprogparms.autocompile = PR_COMPILEEXISTANDCHANGED;//enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILECHANGED, PR_COMPILEALWAYS} autocompile;

	menuprogparms.gametime = &menutime;

	menuprogparms.sv_edicts = (edict_t **)&menu_edicts;
	menuprogparms.sv_num_edicts = &num_menu_edicts;

	menuprogparms.useeditor = NULL;//sorry... QCEditor;//void (*useeditor) (char *filename, int line, int nump, char **parms);

	menutime = Sys_DoubleTime();
	if (!menuprogs)
	{
		Con_DPrintf("Initializing menu.dat\n");
		menuprogs = InitProgs(&menuprogparms);
		PR_Configure(menuprogs, -1, 1);
		if (PR_LoadProgs(menuprogs, "menu.dat", 10020, NULL, 0) < 0) //no per-progs builtins.
		{
			//failed to load or something
//			CloseProgs(menuprogs);
//			menuprogs = NULL;
			M_Init_Internal();
			return;
		}
		if (setjmp(mp_abort))
		{
			M_Init_Internal();
			Con_DPrintf("Failed to initialize menu.dat\n");
			inmenuprogs = false;
			return;
		}
		inmenuprogs++;

		PF_InitTempStrings(menuprogs);

		mp_time = (float*)PR_FindGlobal(menuprogs, "time", 0);
		if (mp_time)
			*mp_time = Sys_DoubleTime();

		menuentsize = PR_InitEnts(menuprogs, 8192);


		//'world' edict
//		EDICT_NUM(menuprogs, 0)->readonly = true;
		EDICT_NUM(menuprogs, 0)->isfree = false;


		mp_init_function = PR_FindFunction(menuprogs, "m_init", PR_ANY);
		mp_shutdown_function = PR_FindFunction(menuprogs, "m_shutdown", PR_ANY);
		mp_draw_function = PR_FindFunction(menuprogs, "m_draw", PR_ANY);
		mp_keydown_function = PR_FindFunction(menuprogs, "m_keydown", PR_ANY);
		mp_keyup_function = PR_FindFunction(menuprogs, "m_keyup", PR_ANY);
		mp_toggle_function = PR_FindFunction(menuprogs, "m_toggle", PR_ANY);
		if (mp_init_function)
			PR_ExecuteProgram(menuprogs, mp_init_function);
		inmenuprogs--;

		Con_DPrintf("Initialized menu.dat\n");
	}
}

void MP_CoreDump(void)
{
	if (!menuprogs)
	{
		Con_Printf("Can't core dump, you need to be running the CSQC progs first.");
		return;
	}

	{
		int size = 1024*1024*8;
		char *buffer = BZ_Malloc(size);
		menuprogs->save_ents(menuprogs, buffer, &size, 3);
		COM_WriteFile("menucore.txt", buffer, size);
		BZ_Free(buffer);
	}
}

void MP_RegisterCvarsAndCmds(void)
{
	Cmd_AddCommand("coredump_menuqc", MP_CoreDump);

	Cvar_Register(&forceqmenu, MENUPROGSGROUP);
	Cvar_Register(&pr_menuqc_coreonerror, MENUPROGSGROUP);

	if (COM_CheckParm("-qmenu"))
		Cvar_Set(&forceqmenu, "1");
}

void MP_Draw(void)
{
	if (setjmp(mp_abort))
		return;

#ifdef RGLQUAKE
	if (qrenderer == QR_OPENGL)
	{
		GL_TexEnv(GL_MODULATE);
		qglDisable(GL_ALPHA_TEST);
		qglEnable(GL_BLEND);
	}
#endif

	menutime = Sys_DoubleTime();
	if (mp_time)
		*mp_time = menutime;

	inmenuprogs++;
	if (mp_draw_function)
		PR_ExecuteProgram(menuprogs, mp_draw_function);
	inmenuprogs--;
}

int MP_TranslateFTEtoDPCodes(int code)
{
	switch(code)
	{
	case K_TAB:				return 9;
	case K_ENTER:			return 13;
	case K_ESCAPE:			return 27;
	case K_SPACE:			return 32;
	case K_BACKSPACE:		return 127;
	case K_UPARROW:			return 128;
	case K_DOWNARROW:		return 129;
	case K_LEFTARROW:		return 130;
	case K_RIGHTARROW:		return 131;
	case K_ALT:				return 132;
	case K_CTRL:			return 133;
	case K_SHIFT:			return 134;
	case K_F1:				return 135;
	case K_F2:				return 136;
	case K_F3:				return 137;
	case K_F4:				return 138;
	case K_F5:				return 139;
	case K_F6:				return 140;
	case K_F7:				return 141;
	case K_F8:				return 142;
	case K_F9:				return 143;
	case K_F10:				return 144;
	case K_F11:				return 145;
	case K_F12:				return 146;
	case K_INS:				return 147;
	case K_DEL:				return 148;
	case K_PGDN:			return 149;
	case K_PGUP:			return 150;
	case K_HOME:			return 151;
	case K_END:				return 152;
	case K_KP_HOME:			return 160;
	case K_KP_UPARROW:		return 161;
	case K_KP_PGUP:			return 162;
	case K_KP_LEFTARROW:	return 163;
	case K_KP_5:			return 164;
	case K_KP_RIGHTARROW:	return 165;
	case K_KP_END:			return 166;
	case K_KP_DOWNARROW:	return 167;
	case K_KP_PGDN:			return 168;
	case K_KP_ENTER:		return 169;
	case K_KP_INS:			return 170;
	case K_KP_DEL:			return 171;
	case K_KP_SLASH:		return 172;
	case K_KP_MINUS:		return 173;
	case K_KP_PLUS:			return 174;
	case K_PAUSE:			return 255;
	case K_JOY1:			return 768;
	case K_JOY2:			return 769;
	case K_JOY3:			return 770;
	case K_JOY4:			return 771;
	case K_AUX1:			return 772;
	case K_AUX2:			return 773;
	case K_AUX3:			return 774;
	case K_AUX4:			return 775;
	case K_AUX5:			return 776;
	case K_AUX6:			return 777;
	case K_AUX7:			return 778;
	case K_AUX8:			return 779;
	case K_AUX9:			return 780;
	case K_AUX10:			return 781;
	case K_AUX11:			return 782;
	case K_AUX12:			return 783;
	case K_AUX13:			return 784;
	case K_AUX14:			return 785;
	case K_AUX15:			return 786;
	case K_AUX16:			return 787;
	case K_AUX17:			return 788;
	case K_AUX18:			return 789;
	case K_AUX19:			return 790;
	case K_AUX20:			return 791;
	case K_AUX21:			return 792;
	case K_AUX22:			return 793;
	case K_AUX23:			return 794;
	case K_AUX24:			return 795;
	case K_AUX25:			return 796;
	case K_AUX26:			return 797;
	case K_AUX27:			return 798;
	case K_AUX28:			return 799;
	case K_AUX29:			return 800;
	case K_AUX30:			return 801;
	case K_AUX31:			return 802;
	case K_AUX32:			return 803;
	case K_MOUSE1:			return 512;
	case K_MOUSE2:			return 513;
	case K_MOUSE3:			return 514;
	case K_MOUSE4:			return 515;
	case K_MOUSE5:			return 516;
//	case K_MOUSE6:			return 517;
//	case K_MOUSE7:			return 518;
//	case K_MOUSE8:			return 519;
//	case K_MOUSE9:			return 520;
//	case K_MOUSE10:			return 521;
	case K_MWHEELDOWN:		return 515;//K_MOUSE4;
	case K_MWHEELUP:		return 516;//K_MOUSE5;
	default:				return code;
	}
}

int MP_TranslateDPtoFTECodes(int code)
{
	switch(code)
	{
	case 9:			return K_TAB;
	case 13:		return K_ENTER;
	case 27:		return K_ESCAPE;
	case 32:		return K_SPACE;
	case 127:		return K_BACKSPACE;
	case 128:		return K_UPARROW;
	case 129:		return K_DOWNARROW;
	case 130:		return K_LEFTARROW;
	case 131:		return K_RIGHTARROW;
	case 132:		return K_ALT;
	case 133:		return K_CTRL;
	case 134:		return K_SHIFT;
	case 135:		return K_F1;
	case 136:		return K_F2;
	case 137:		return K_F3;
	case 138:		return K_F4;
	case 139:		return K_F5;
	case 140:		return K_F6;
	case 141:		return K_F7;
	case 142:		return K_F8;
	case 143:		return K_F9;
	case 144:		return K_F10;
	case 145:		return K_F11;
	case 146:		return K_F12;
	case 147:		return K_INS;
	case 148:		return K_DEL;
	case 149:		return K_PGDN;
	case 150:		return K_PGUP;
	case 151:		return K_HOME;
	case 152:		return K_END;
	case 160:		return K_KP_HOME;
	case 161:		return K_KP_UPARROW;
	case 162:		return K_KP_PGUP;
	case 163:		return K_KP_LEFTARROW;
	case 164:		return K_KP_5;
	case 165:		return K_KP_RIGHTARROW;
	case 166:		return K_KP_END;
	case 167:		return K_KP_DOWNARROW;
	case 168:		return K_KP_PGDN;
	case 169:		return K_KP_ENTER;
	case 170:		return K_KP_INS;
	case 171:		return K_KP_DEL;
	case 172:		return K_KP_SLASH;
	case 173:		return K_KP_MINUS;
	case 174:		return K_KP_PLUS;
	case 255:		return K_PAUSE;

	case 768:		return K_JOY1;
	case 769:		return K_JOY2;
	case 770:		return K_JOY3;
	case 771:		return K_JOY4;
	case 772:		return K_AUX1;
	case 773:		return K_AUX2;
	case 774:		return K_AUX3;
	case 775:		return K_AUX4;
	case 776:		return K_AUX5;
	case 777:		return K_AUX6;
	case 778:		return K_AUX7;
	case 779:		return K_AUX8;
	case 780:		return K_AUX9;
	case 781:		return K_AUX10;
	case 782:		return K_AUX11;
	case 783:		return K_AUX12;
	case 784:		return K_AUX13;
	case 785:		return K_AUX14;
	case 786:		return K_AUX15;
	case 787:		return K_AUX16;
	case 788:		return K_AUX17;
	case 789:		return K_AUX18;
	case 790:		return K_AUX19;
	case 791:		return K_AUX20;
	case 792:		return K_AUX21;
	case 793:		return K_AUX22;
	case 794:		return K_AUX23;
	case 795:		return K_AUX24;
	case 796:		return K_AUX25;
	case 797:		return K_AUX26;
	case 798:		return K_AUX27;
	case 799:		return K_AUX28;
	case 800:		return K_AUX29;
	case 801:		return K_AUX30;
	case 802:		return K_AUX31;
	case 803:		return K_AUX32;
	case 512:		return K_MOUSE1;
	case 513:		return K_MOUSE2;
	case 514:		return K_MOUSE3;
//	case 515:		return K_MOUSE4;
//	case 516:		return K_MOUSE5;
	case 517:		return K_MOUSE6;
	case 518:		return K_MOUSE7;
	case 519:		return K_MOUSE8;
//	case 520:		return K_MOUSE9;
//	case 521:		return K_MOUSE10;
	case 515:		return K_MWHEELDOWN;//K_MOUSE4;
	case 516:		return K_MWHEELUP;//K_MOUSE5;
	default:		return code;
	}
}

void MP_Keydown(int key)
{
	if (setjmp(mp_abort))
		return;

	if (key == 'c')
	{
		extern int keydown[];
		if (keydown[K_CTRL])
		{
			MP_Shutdown();
			return;
		}
	}

	menutime = Sys_DoubleTime();
	if (mp_time)
		*mp_time = menutime;

	inmenuprogs++;
	if (mp_keydown_function)
	{
		void *pr_globals = PR_globals(menuprogs, PR_CURRENT);
		G_FLOAT(OFS_PARM0) = MP_TranslateFTEtoDPCodes(key);
		if (G_FLOAT(OFS_PARM0) > 127)
			G_FLOAT(OFS_PARM1) = 0;
		else
			G_FLOAT(OFS_PARM1) = G_FLOAT(OFS_PARM0);
		PR_ExecuteProgram(menuprogs, mp_keydown_function);
	}
	inmenuprogs--;
}

void MP_Keyup(int key)
{
	if (setjmp(mp_abort))
		return;

	menutime = Sys_DoubleTime();
	if (mp_time)
		*mp_time = menutime;

	inmenuprogs++;
	if (mp_keyup_function)
	{
		void *pr_globals = PR_globals(menuprogs, PR_CURRENT);
		G_FLOAT(OFS_PARM0) = MP_TranslateFTEtoDPCodes(key);
		if (G_FLOAT(OFS_PARM0) > 127)
			G_FLOAT(OFS_PARM1) = 0;
		else
			G_FLOAT(OFS_PARM1) = G_FLOAT(OFS_PARM0);
		PR_ExecuteProgram(menuprogs, mp_keyup_function);
	}
	inmenuprogs--;
}

qboolean MP_Toggle(void)
{
	if (!menuprogs)
		return false;

	if (setjmp(mp_abort))
		return false;

	menutime = Sys_DoubleTime();
	if (mp_time)
		*mp_time = menutime;

	inmenuprogs++;
	if (mp_toggle_function)
		PR_ExecuteProgram(menuprogs, mp_toggle_function);
	inmenuprogs--;

	return true;
}
#endif