#define PROGSUSED
#include "progsint.h"
//#include "editor.h"

#define HunkAlloc BADGDFG sdfhhsf FHS


#define Host_Error Sys_Error

// I put the following here to resolve "undefined reference to `__imp__vsnprintf'" with MinGW64 ~ Moodles
#ifdef _WIN32
	#if (_MSC_VER >= 1400)
		//with MSVC 8, use MS extensions
		#define snprintf linuxlike_snprintf_vc8
		int VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...) LIKEPRINTF(3);
		#define vsnprintf(a, b, c, d) vsnprintf_s(a, b, _TRUNCATE, c, d)
	#else
		//msvc crap
		#define snprintf linuxlike_snprintf
		int VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...) LIKEPRINTF(3);
		#define vsnprintf linuxlike_vsnprintf
		int VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr);
	#endif
#endif


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

/*
=================
PR_PrintStatement
=================
*/
static void PR_PrintStatement (progfuncs_t *progfuncs, int statementnum)
{
	int		i;
	unsigned int op;
	unsigned int arg[3];

	switch(current_progstate->structtype)
	{
	case PST_DEFAULT:
	case PST_QTEST:
		op = ((dstatement16_t*)current_progstate->statements + statementnum)->op;
		arg[0] = ((dstatement16_t*)current_progstate->statements + statementnum)->a;
		arg[1] = ((dstatement16_t*)current_progstate->statements + statementnum)->b;
		arg[2] = ((dstatement16_t*)current_progstate->statements + statementnum)->c;
		break;
	case PST_KKQWSV:
	case PST_FTE32:
		op = ((dstatement32_t*)current_progstate->statements + statementnum)->op;
		arg[0] = ((dstatement32_t*)current_progstate->statements + statementnum)->a;
		arg[1] = ((dstatement32_t*)current_progstate->statements + statementnum)->b;
		arg[2] = ((dstatement32_t*)current_progstate->statements + statementnum)->c;
		break;
	}

#ifndef MINIMAL
	if ( (unsigned)op < OP_NUMOPS)
	{
		printf ("%s ",  pr_opcodes[op].name);
		i = strlen(pr_opcodes[op].name);
		for ( ; i<10 ; i++)
			printf (" ");
	}
	else
#endif
		printf ("op%3i ");

	if (op == OP_IF_F || op == OP_IFNOT_F)
		printf ("%sbranch %i",PR_GlobalString(progfuncs, arg[0]),arg[1]);
	else if (op == OP_GOTO)
	{
		printf ("branch %i",arg[0]);
	}
	else if ( (unsigned)(op - OP_STORE_F) < 6)
	{
		printf ("%s",PR_GlobalString(progfuncs, arg[0]));
		printf ("%s", PR_GlobalStringNoContents(progfuncs, arg[1]));
	}
	else
	{
		if (arg[0])
			printf ("%s",PR_GlobalString(progfuncs, arg[0]));
		if (arg[1])
			printf ("%s",PR_GlobalString(progfuncs, arg[1]));
		if (arg[2])
			printf ("%s", PR_GlobalStringNoContents(progfuncs, arg[2]));
	}
	printf ("\n");
}

/*
============
PR_StackTrace
============
*/
char *QC_ucase(char *str)
{
	static char s[1024];
	strcpy(s, str);
	str = s;

	while(*str)
	{
		if (*str >= 'a' && *str <= 'z')
			*str = *str - 'a' + 'A';
		str++;
	}
	return s;
}

void PR_StackTrace (progfuncs_t *progfuncs)
{
	dfunction_t	*f;
	int			i;
	int progs;

#ifdef STACKTRACE
	int arg;
	int *globalbase;
#endif
	progs = -1;

	if (pr_depth == 0)
	{
		printf ("<NO STACK>\n");
		return;
	}

#ifdef STACKTRACE
	globalbase = (int *)pr_globals + pr_xfunction->parm_start - pr_xfunction->locals;
#endif

	pr_stack[pr_depth].f = pr_xfunction;
	pr_stack[pr_depth].s = pr_xstatement;
	for (i=pr_depth ; i>0 ; i--)
	{
		f = pr_stack[i].f;

		if (!f)
		{
			printf ("<NO FUNCTION>\n");
		}
		else
		{
			if (pr_stack[i].progsnum != progs)
			{
				progs = pr_stack[i].progsnum;

				printf ("<%s>\n", pr_progstate[progs].filename);
			}
			if (!f->s_file)
				printf ("stripped     : %s\n", f->s_name+progfuncs->stringtable);
			else
			{
				if (pr_progstate[progs].linenums)
					printf ("%12s %i : %s\n", f->s_file+progfuncs->stringtable, pr_progstate[progs].linenums[pr_stack[i].s], f->s_name+progfuncs->stringtable);
				else
					printf ("%12s : %s\n", f->s_file+progfuncs->stringtable, f->s_name+progfuncs->stringtable);
			}

#ifdef STACKTRACE

			for (arg = 0; arg < f->locals; arg++)
			{
				ddef16_t *local;
				local = ED_GlobalAtOfs16(progfuncs, f->parm_start+arg);
				if (!local)
				{
					printf("    ofs %i: %f : %i\n", f->parm_start+arg, *(float *)(globalbase - f->locals+arg), *(int *)(globalbase - f->locals+arg) );
				}
				else
				{
					printf("    %s: %s\n", local->s_name+progfuncs->stringtable, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg)));
					if (local->type == ev_vector)
						arg+=2;
				}
			}

			if (i == pr_depth)
				globalbase = localstack + localstack_used;
			else
				globalbase -= f->locals;
#endif
		}
	}
}

/*
============
PR_Profile_f

============
*/
/*
void PR_Profile_f (void)
{
	dfunction_t	*f, *best;
	int			max;
	int			num;
	unsigned int			i;

	num = 0;
	do
	{
		max = 0;
		best = NULL;
		for (i=0 ; i<pr_progs->numfunctions ; i++)
		{
			f = &pr_functions[i];
			if (f->profile > max && f->first_statement >=0)
			{
				max = f->profile;
				best = f;
			}
		}
		if (best)
		{
			if (num < 10)
				printf ("%7i %s\n", best->profile, best->s_name);
			num++;
			best->profile = 0;
		}
	} while (best);
}
*/


/*
============
PR_RunError

Aborts the currently executing function
============
*/
void VARGS PR_RunError (progfuncs_t *progfuncs, char *error, ...)
{
	va_list		argptr;
	char		string[1024];

	va_start (argptr,error);
	Q_vsnprintf (string,sizeof(string)-1, error,argptr);
	va_end (argptr);

//	{
//		void SV_EndRedirect (void);
//		SV_EndRedirect();
//	}

//	PR_PrintStatement (pr_statements + pr_xstatement);
	PR_StackTrace (progfuncs);
	printf ("\n");

//editbadfile(pr_strings + pr_xfunction->s_file, -1);

//	pr_depth = 0;		// dump the stack so host_error can shutdown functions
//	prinst->exitdepth = 0;

	Abort ("%s", string);
}

/*
============================================================================
PR_ExecuteProgram

The interpretation main loop
============================================================================
*/

/*
====================
PR_EnterFunction

Returns the new program statement counter
====================
*/
void	PR_AbortStack			(progfuncs_t *progfuncs);
int ASMCALL PR_EnterFunction (progfuncs_t *progfuncs, dfunction_t *f, int progsnum)
{
	int		i, j, c, o;

	pr_stack[pr_depth].s = pr_xstatement;
	pr_stack[pr_depth].f = pr_xfunction;
	pr_stack[pr_depth].progsnum = progsnum;
	pr_stack[pr_depth].pushed = pr_spushed;
	pr_depth++;
	if (pr_depth == MAX_STACK_DEPTH)
	{
		pr_depth--;
		PR_StackTrace (progfuncs);

		printf ("stack overflow on call to %s\n", progfuncs->stringtable+f->s_name);

		//comment this out if you want the progs to try to continue anyway (could cause infinate loops)
		PR_AbortStack(progfuncs);
		Abort("Stack Overflow in %s\n", progfuncs->stringtable+f->s_name);
		return pr_xstatement;
	}

	localstack_used += pr_spushed;	//make sure the call doesn't hurt pushed pointers

// save off any locals that the new function steps on (to a side place, fromwhere they are restored on exit)
	c = f->locals;
	if (localstack_used + c > LOCALSTACK_SIZE)
	{
		localstack_used -= pr_spushed;
		pr_depth--;
		PR_RunError (progfuncs, "PR_ExecuteProgram: locals stack overflow\n");
	}

	for (i=0 ; i < c ; i++)
		localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i];
	localstack_used += c;

// copy parameters (set initial values)
	o = f->parm_start;
	for (i=0 ; i<f->numparms ; i++)
	{
		for (j=0 ; j<f->parm_size[i] ; j++)
		{
			((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j];
			o++;
		}
	}

	pr_xfunction = f;
	return f->first_statement - 1;	// offset the s++
}

/*
====================
PR_LeaveFunction
====================
*/
int ASMCALL PR_LeaveFunction (progfuncs_t *progfuncs)
{
	int		i, c;

	if (pr_depth <= 0)
		Sys_Error ("prog stack underflow");

// restore locals from the stack
	c = pr_xfunction->locals;
	localstack_used -= c;
	if (localstack_used < 0)
		PR_RunError (progfuncs, "PR_ExecuteProgram: locals stack underflow\n");

	for (i=0 ; i < c ; i++)
		((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i];

// up stack
	pr_depth--;
	PR_MoveParms(progfuncs, pr_stack[pr_depth].progsnum, pr_typecurrent);
	PR_SwitchProgs(progfuncs, pr_stack[pr_depth].progsnum);
	pr_xfunction = pr_stack[pr_depth].f;
	pr_spushed = pr_stack[pr_depth].pushed;

	localstack_used -= pr_spushed;
	return pr_stack[pr_depth].s;
}

ddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, char *name, eval_t **val)
{
	static ddef32_t def;
	ddef32_t *def32;
	ddef16_t *def16;
	int i;

	if (pr_typecurrent < 0)
		return NULL;

	switch (pr_progstate[pr_typecurrent].structtype)
	{
	case PST_DEFAULT:
	case PST_KKQWSV:
		//this gets parms fine, but not locals
		if (pr_xfunction)
		for (i = 0; i < pr_xfunction->numparms; i++)
		{
			def16 = ED_GlobalAtOfs16(progfuncs, pr_xfunction->parm_start+i);
			if (!def16)
				continue;
			if (!strcmp(def16->s_name+progfuncs->stringtable, name))
			{
				*val = (eval_t *)&pr_progstate[pr_typecurrent].globals[pr_xfunction->parm_start+i];

				//we need something like this for functions that are not the top layer
	//			*val = (eval_t *)&localstack[localstack_used-pr_xfunction->numparms*4];
				def.ofs = def16->ofs;
				def.s_name = def16->s_name;
				def.type = def16->type;
				return &def;
			}
		}
		def16 = ED_FindGlobal16(progfuncs, name);
		if (!def16)
			return NULL;
		def.ofs = def16->ofs;
		def.type = def16->type;
		def.s_name = def16->s_name;
		def32 = &def;
		break;
	case PST_QTEST:
	case PST_FTE32:
		//this gets parms fine, but not locals
		if (pr_xfunction)
		for (i = 0; i < pr_xfunction->numparms; i++)
		{
			def32 = ED_GlobalAtOfs32(progfuncs, pr_xfunction->parm_start+i);
			if (!def32)
				continue;
			if (!strcmp(def32->s_name+progfuncs->stringtable, name))
			{
				*val = (eval_t *)&pr_progstate[pr_typecurrent].globals[pr_xfunction->parm_start+i];

				//we need something like this for functions that are not the top layer
	//			*val = (eval_t *)&localstack[localstack_used-pr_xfunction->numparms*4];
				return def32;
			}
		}
		def32 = ED_FindGlobal32(progfuncs, name);
		if (!def32)
			return NULL;
		break;
	default:
		Sys_Error("Bad struct type in ED_FindLocalOrGlobal");
		def32 = NULL;
	}

	*val = (eval_t *)&pr_progstate[pr_typecurrent].globals[def32->ofs];
	return &def;
}

char *COM_TrimString(char *str)
{
	int i;
	static char buffer[256];
	while (*str <= ' ' && *str>'\0')
		str++;

	for (i = 0; i < 255; i++)
	{
		if (*str <= ' ')
			break;
		buffer[i] = *str++;
	}
	buffer[i] = '\0';
	return buffer;
}

char *EvaluateDebugString(progfuncs_t *progfuncs, char *key)
{
	static char buf[256];
	char *c, *c2;
	ddef32_t *def;
	fdef_t *fdef;
	eval_t *val;
	char *assignment;
	int type;
	ddef32_t fakedef;
	eval_t fakeval;
	edictrun_t *ed;

	assignment = strchr(key, '=');
	if (assignment)
		*assignment = '\0';

	c = strchr(key, '.');
	if (c) *c = '\0';
	def = ED_FindLocalOrGlobal(progfuncs, key, &val);
	if (!def)
	{
		if (atoi(key))
		{
			def = &fakedef;
			def->ofs = 0;
			def->type = ev_entity;
			val = &fakeval;
			val->edict = atoi(key);
		}
	}
	if (c) *c = '.';
	if (!def)
	{
		return "(Bad string)";
	}
	type = def->type;

	//go through ent vars
	c = strchr(key, '.');
	while(c)
	{
		c2 = c+1;
		c = strchr(c2, '.');
		type = type &~DEF_SAVEGLOBAL;
		if (current_progstate && current_progstate->types)
			type = current_progstate->types[type].type;
		if (type != ev_entity)
			return "'.' without entity";
		if (c)*c = '\0';
		fdef = ED_FindField(progfuncs, COM_TrimString(c2));
		if (c)*c = '.';
		if (!fdef)
			return "(Bad string)";
		ed = PROG_TO_EDICT(progfuncs, val->_int);
		if (!ed)
			return "(Invalid Entity)";
		val = (eval_t *) (((char *)ed->fields) + fdef->ofs*4);
		type = fdef->type;
	}

	if (assignment)
	{
		assignment++;
		while(*assignment == ' ')
			assignment++;
		switch (type&~DEF_SAVEGLOBAL)
		{
		case ev_string:
			*(string_t *)val = PR_StringToProgs(progfuncs, ED_NewString (progfuncs, assignment, 0));
			break;

		case ev_float:
			if (assignment[0] == '0' && (assignment[1] == 'x' || assignment[1] == 'X'))
				*(float*)val = strtoul(assignment, NULL, 0);
			else
				*(float *)val = (float)atof (assignment);
			break;

		case ev_integer:
			*(int *)val = atoi (assignment);
			break;

/*		case ev_vector:
			strcpy (string, assignment);
			v = string;
			w = string;
			for (i=0 ; i<3 ; i++)
			{
				while (*v && *v != ' ')
					v++;
				*v = 0;
				((float *)d)[i] = (float)atof (w);
				w = v = v+1;
			}
			break;
*/
		case ev_entity:
			*(int *)val = EDICT_TO_PROG(progfuncs, EDICT_NUM(progfuncs, atoi (assignment)));
			break;

		case ev_field:
			fdef = ED_FindField (progfuncs, assignment);
			if (!fdef)
			{
				size_t l,nl = strlen(assignment);
				strcpy(buf, "Can't find field ");
				l = strlen(buf);
				if (nl > sizeof(buf)-l-2)
					nl = sizeof(buf)-l-2;
				memcpy(buf+l, assignment, nl);
				assignment[l+nl+0] = '\n';
				assignment[l+nl+1] = 0;
				return buf;
			}
			*(int *)val = G_INT(fdef->ofs);
			break;

		case ev_function:
			{
				dfunction_t *func;
				int i;
				int progsnum = -1;
				char *s = assignment;
				if (s[0] && s[1] == ':')
				{
					progsnum = atoi(s);
					s+=2;
				}
				else if (s[0] && s[1] && s[2] == ':')
				{
					progsnum = atoi(s);
					s+=3;
				}

				func = ED_FindFunction (progfuncs, s, &i, progsnum);
				if (!func)
				{
					size_t l,nl = strlen(s);

					assignment[-1] = '=';

					strcpy(buf, "Can't find field ");
					l = strlen(buf);
					if (nl > sizeof(buf)-l-2)
						nl = sizeof(buf)-l-2;
					memcpy(buf+l, assignment, nl);
					assignment[l+nl+0] = '\n';
					assignment[l+nl+1] = 0;
					return buf;
				}
				*(func_t *)val = (func - pr_progstate[i].functions) | (i<<24);
			}
			break;

		default:
			break;

		}
		assignment[-1] = '=';
	}
	strcpy(buf, PR_ValueString(progfuncs, type, val));

	return buf;
}

int debugstatement;
//int EditorHighlightLine(window_t *wnd, int line);
void SetExecutionToLine(progfuncs_t *progfuncs, int linenum)
{
	int pn = pr_typecurrent;
	int snum;
	dfunction_t *f = pr_xfunction;

	switch(current_progstate->structtype)
	{
	case PST_DEFAULT:
	case PST_QTEST:
		for (snum = f->first_statement; pr_progstate[pn].linenums[snum] < linenum; snum++)
		{
			if (pr_statements16[snum].op == OP_DONE)
				return;
		}
		break;
	case PST_KKQWSV:
	case PST_FTE32:
		for (snum = f->first_statement; pr_progstate[pn].linenums[snum] < linenum; snum++)
		{
			if (pr_statements32[snum].op == OP_DONE)
				return;
		}
		break;
	default:
		Sys_Error("Bad struct type");
		snum = 0;
	}
	debugstatement = snum;
//	EditorHighlightLine(editwnd, pr_progstate[pn].linenums[snum]);
}

//0 clear. 1 set, 2 toggle, 3 check
int PR_ToggleBreakpoint(progfuncs_t *progfuncs, char *filename, int linenum, int flag)	//write alternate route to work by function name.
{
	int ret=0;
	unsigned int fl;
	unsigned int i;
	int pn = pr_typecurrent;
	dfunction_t *f;
	int op = 0; //warning about not being initialized before use

	for (pn = 0; (unsigned)pn < maxprogs; pn++)
	{
		if (!pr_progstate || !pr_progstate[pn].progs)
			continue;

		if (linenum)	//linenum is set means to set the breakpoint on a file and line
		{
			if (!pr_progstate[pn].linenums)
				continue;

			for (f = pr_progstate[pn].functions, fl = 0; fl < pr_progstate[pn].progs->numfunctions; f++, fl++)
			{
				if (!stricmp(f->s_file+progfuncs->stringtable, filename))
				{
					for (i = f->first_statement; ; i++)
					{
						if (pr_progstate[pn].linenums[i] >= linenum)
						{
							fl = pr_progstate[pn].linenums[i];
							for (; ; i++)
							{
								if ((unsigned int)pr_progstate[pn].linenums[i] > fl)
									break;

								switch(pr_progstate[pn].structtype)
								{
								case PST_DEFAULT:
								case PST_QTEST:
									op = ((dstatement16_t*)pr_progstate[pn].statements + i)->op;
									break;
								case PST_KKQWSV:
								case PST_FTE32:
									op = ((dstatement32_t*)pr_progstate[pn].statements + i)->op;
									break;
								default:
									Sys_Error("Bad structtype");
									op = 0;
								}
								switch (flag)
								{
								default:
									if (op & 0x8000)
									{
										op &= ~0x8000;
										ret = false;
										flag = 0;
									}
									else
									{
										op |= 0x8000;
										ret = true;
										flag = 1;
									}
									break;
								case 0:
									op &= ~0x8000;
									ret = false;
									break;
								case 1:
									op |= 0x8000;
									ret = true;
									break;
								case 3:
									if (op & 0x8000)
										return true;
								}
								switch(pr_progstate[pn].structtype)
								{
								case PST_DEFAULT:
								case PST_QTEST:
									((dstatement16_t*)pr_progstate[pn].statements + i)->op = op;
									break;
								case PST_KKQWSV:
								case PST_FTE32:
									((dstatement32_t*)pr_progstate[pn].statements + i)->op = op;
									break;
								default:
									Sys_Error("Bad structtype");
									op = 0;
								}
							}
							goto cont;
						}
					}
				}
			}
		}
		else	//set the breakpoint on the first statement of the function specified.
		{
			for (f = pr_progstate[pn].functions, fl = 0; fl < pr_progstate[pn].progs->numfunctions; f++, fl++)
			{
				if (!strcmp(f->s_name+progfuncs->stringtable, filename))
				{
					i = f->first_statement;
					switch(pr_progstate[pn].structtype)
					{
					case PST_DEFAULT:
					case PST_QTEST:
						op = ((dstatement16_t*)pr_progstate[pn].statements + i)->op;
						break;
					case PST_KKQWSV:
					case PST_FTE32:
						op = ((dstatement32_t*)pr_progstate[pn].statements + i)->op;
						break;
					default:
						Sys_Error("Bad structtype");
					}
					switch (flag)
					{
					default:
						if (op & 0x8000)
						{
							op &= ~0x8000;
							ret = false;
							flag = 0;
						}
						else
						{
							op |= 0x8000;
							ret = true;
							flag = 1;
						}
						break;
					case 0:
						op &= ~0x8000;
						ret = false;
						break;
					case 1:
						op |= 0x8000;
						ret = true;
						break;
					case 3:
						if (op & 0x8000)
							return true;
					}
					switch(pr_progstate[pn].structtype)
					{
					case PST_DEFAULT:
					case PST_QTEST:
						((dstatement16_t*)pr_progstate[pn].statements + i)->op = op;
						break;
					case PST_KKQWSV:
					case PST_FTE32:
						((dstatement32_t*)pr_progstate[pn].statements + i)->op = op;
						break;
					default:
						Sys_Error("Bad structtype");
					}
					break;
				}
			}
		}
cont:
		continue;
	}

	return ret;
}

int ShowStep(progfuncs_t *progfuncs, int statement)
{
//	return statement;
//	texture realcursortex;
static int lastline = 0;
static char *lastfile = 0;

	int pn = pr_typecurrent;
	int i;
	dfunction_t *f = pr_xfunction;

	if (!externs->useeditor)
	{
		PR_PrintStatement(progfuncs, statement);
		return statement;
	}

	if (f && pr_progstate[pn].linenums && externs->useeditor)
	{
		if (lastline == pr_progstate[pn].linenums[statement] && lastfile == f->s_file+progfuncs->stringtable)
			return statement;	//no info/same line as last time

		lastline = pr_progstate[pn].linenums[statement];
		lastfile = f->s_file+progfuncs->stringtable;

		lastline = externs->useeditor(progfuncs, lastfile, lastline, 0, NULL);

		if (pr_progstate[pn].linenums[statement] != lastline)
		{
			for (i = f->first_statement; ; i++)
			{
				if (lastline == pr_progstate[pn].linenums[i])
				{
					return i;
				}
				else if (lastline <= pr_progstate[pn].linenums[i])
				{
					return statement;
				}
			}
		}
	}
	else if (f)	//annoying.
	{
		if (*(f->s_file+progfuncs->stringtable))	//if we can't get the filename, then it was stripped, and debugging it like this is useless
			if (externs->useeditor)
				externs->useeditor(progfuncs, f->s_file+progfuncs->stringtable, -1, 0, NULL);
		return statement;
	}


	return statement;
}

//DMW: all pointer functions are modified to be absoloute pointers from NULL not sv_edicts
/*
====================
PR_ExecuteProgram
====================
*/
void PR_ExecuteCode (progfuncs_t *progfuncs, int s)
{
	eval_t	*t, *swtch=NULL;

	int swtchtype = 0; //warning about not being initialized before use
	dstatement16_t	*st16;
	dstatement32_t	*st32;
	dfunction_t	*newf;
	int		runaway;
	int		i;
	edictrun_t	*ed;
	eval_t	*ptr;

	float *glob;

	int fnum;

	prinst->continuestatement = -1;
#ifdef QCJIT
	if (current_progstate->jit)
	{
		PR_EnterJIT(progfuncs, current_progstate->jit, s);
		return;
	}
#endif
	fnum = pr_xfunction - pr_functions;

	runaway = 100000000;

#define PRBOUNDSCHECK
#define RUNAWAYCHECK()							\
	if (!--runaway)								\
	{											\
		pr_xstatement = st-pr_statements;		\
		PR_StackTrace(progfuncs);				\
		printf ("runaway loop error\n");		\
		while(pr_depth > prinst->exitdepth)		\
			PR_LeaveFunction(progfuncs);		\
		pr_spushed = 0;							\
		return;									\
	}

#define OPA ((eval_t *)&glob[st->a])
#define OPB ((eval_t *)&glob[st->b])
#define OPC ((eval_t *)&glob[st->c])

restart:	//jumped to when the progs might have changed.
	glob = pr_globals;
	switch (current_progstate->structtype)
	{
	case PST_DEFAULT:
	case PST_QTEST:
#define INTSIZE 16
		st16 = &pr_statements16[s];
		while (pr_trace)
		{
			#define DEBUGABLE
			#ifdef SEPARATEINCLUDES
				#include "execloop16d.h"
			#else
				#include "execloop.h"
			#endif
			#undef DEBUGABLE
		}

		while(1)
		{
			#include "execloop.h"
		}
#undef INTSIZE
		Sys_Error("PR_ExecuteProgram - should be unreachable");
		break;
	case PST_KKQWSV:
	case PST_FTE32:
#define INTSIZE 32
		st32 = &pr_statements32[s];
		while (pr_trace)
		{
			#define DEBUGABLE
			#ifdef SEPARATEINCLUDES
				#include "execloop32d.h"
			#else
				#include "execloop.h"
			#endif
			#undef DEBUGABLE
		}

		while(1)
		{
			#ifdef SEPARATEINCLUDES
				#include "execloop32.h"
			#else
				#include "execloop.h"
			#endif
		}
#undef INTSIZE
		Sys_Error("PR_ExecuteProgram - should be unreachable");
		break;
	default:
		Sys_Error("PR_ExecuteProgram - bad structtype");
	}
}


void PR_ExecuteProgram (progfuncs_t *progfuncs, func_t fnum)
{
	dfunction_t	*f;
	int		i;
	unsigned int initial_progs;
	int		oldexitdepth;

	int s;

	int tempdepth;

	unsigned int newprogs = (fnum & 0xff000000)>>24;

	initial_progs = pr_typecurrent;
	if (newprogs != initial_progs)
	{
		if (newprogs >= maxprogs || !&pr_progstate[newprogs].globals)	//can happen with hexen2...
		{
			printf("PR_ExecuteProgram: tried branching into invalid progs\n");
			return;
		}
		PR_MoveParms(progfuncs, newprogs, pr_typecurrent);
		PR_SwitchProgs(progfuncs, newprogs);
	}

	if (!(fnum & ~0xff000000) || (signed)(fnum & ~0xff000000) >= pr_progs->numfunctions)
	{
//		if (pr_global_struct->self)
//			ED_Print (PROG_TO_EDICT(pr_global_struct->self));
		printf("PR_ExecuteProgram: NULL function from exe\n");
//		Host_Error ("PR_ExecuteProgram: NULL function from exe");

//		PR_MoveParms(0, pr_typecurrent);
		PR_SwitchProgs(progfuncs, initial_progs);
		return;
	}

	oldexitdepth = prinst->exitdepth;

	f = &pr_functions[fnum & ~0xff000000];

	if (f->first_statement < 0)
	{	// negative statements are built in functions
		i = -f->first_statement;

		if (i < externs->numglobalbuiltins)
			(*externs->globalbuiltins[i]) (progfuncs, (struct globalvars_s *)current_progstate->globals);
		else
		{
			i -= externs->numglobalbuiltins;
			if (i > current_progstate->numbuiltins)
			{
				printf ("Bad builtin call number %i (from exe)\n", -f->first_statement);
			//	PR_MoveParms(p, pr_typecurrent);
				PR_SwitchProgs(progfuncs, initial_progs);
				return;
			}
			current_progstate->builtins [i] (progfuncs, (struct globalvars_s *)current_progstate->globals);
		}
		PR_MoveParms(progfuncs, initial_progs, pr_typecurrent);
		PR_SwitchProgs(progfuncs, initial_progs);
		return;
	}

	if (pr_trace)
		pr_trace--;

// make a stack frame
	prinst->exitdepth = pr_depth;


	s = PR_EnterFunction (progfuncs, f, initial_progs);

	tempdepth = prinst->numtempstringsstack;
	PR_ExecuteCode(progfuncs, s);


	PR_MoveParms(progfuncs, initial_progs, pr_typecurrent);
	PR_SwitchProgs(progfuncs, initial_progs);

	PR_FreeTemps(progfuncs, tempdepth);
	prinst->numtempstringsstack = tempdepth;

	prinst->exitdepth = oldexitdepth;
}






typedef struct {
	int fnum;
	int progsnum;
	int statement;
} qcthreadstack_t;
typedef struct qcthread_s {
	int fstackdepth;
	qcthreadstack_t fstack[MAX_STACK_DEPTH];
	int lstackused;
	int lstack[LOCALSTACK_SIZE];
	int xstatement;
	int xfunction;
	progsnum_t xprogs;
} qcthread_t;

struct qcthread_s *PR_ForkStack(progfuncs_t *progfuncs)
{	//QC code can call builtins that call qc code.
	//to get around the problems of restoring the builtins we simply don't save the thread over the builtin.
	int i, l;
	int ed = prinst->exitdepth;
	int localsoffset, baselocalsoffset;
	qcthread_t *thread = memalloc(sizeof(qcthread_t));
	dfunction_t *f;

	//copy out the functions stack.
	for (i = 0,localsoffset=0; i < ed; i++)
	{
		if (i+1 == pr_depth)
			f = pr_xfunction;
		else
			f = pr_stack[i+1].f;
		localsoffset += f->locals;	//this is where it crashes
	}
	baselocalsoffset = localsoffset;
	for (i = ed; i < pr_depth; i++)
	{
		thread->fstack[i-ed].fnum = pr_stack[i].f - pr_progstate[pr_stack[i].progsnum].functions;
		thread->fstack[i-ed].progsnum = pr_stack[i].progsnum;
		thread->fstack[i-ed].statement = pr_stack[i].s;

		if (i+1 == pr_depth)
			f = pr_xfunction;
		else
			f = pr_stack[i+1].f;
		localsoffset += f->locals;
	}
	thread->fstackdepth = pr_depth - ed;

	for (i = pr_depth - 1; i >= ed ; i--)
	{
		if (i+1 == pr_depth)
			f = pr_xfunction;
		else
			f = pr_stack[i+1].f;
		localsoffset -= f->locals;
		for (l = 0; l < f->locals; l++)
		{
			thread->lstack[localsoffset-baselocalsoffset + l ] = ((int *)pr_globals)[f->parm_start + l];
			((int *)pr_globals)[f->parm_start + l] = localstack[localsoffset+l];	//copy the old value into the globals (so the older functions have the correct locals.
		}
	}

	for (i = ed; i < pr_depth ; i++)	//we need to get the locals back to how they were.
	{
		if (i+1 == pr_depth)
			f = pr_xfunction;
		else
			f = pr_stack[i+1].f;

		for (l = 0; l < f->locals; l++)
		{
			((int *)pr_globals)[f->parm_start + l] = thread->lstack[localsoffset-baselocalsoffset + l];
		}
		localsoffset += f->locals;
	}
	thread->lstackused = localsoffset - baselocalsoffset;

	thread->xstatement = pr_xstatement;
	thread->xfunction = pr_xfunction - pr_progstate[pr_typecurrent].functions;
	thread->xprogs = pr_typecurrent;

	return thread;
}

void PR_ResumeThread (progfuncs_t *progfuncs, struct qcthread_s *thread)
{
	dfunction_t	*f, *oldf;
	int		i,l,ls;
	progsnum_t initial_progs;
	int		oldexitdepth;

	int s;
	int tempdepth;

	progsnum_t prnum = thread->xprogs;
	int fnum = thread->xfunction;

	if (localstack_used + thread->lstackused > LOCALSTACK_SIZE)
		PR_RunError(progfuncs, "Too many locals on resumtion of QC thread\n");

	if (pr_depth + thread->fstackdepth > MAX_STACK_DEPTH)
		PR_RunError(progfuncs, "Too large stack on resumtion of QC thread\n");


	//do progs switching stuff as appropriate. (fteqw only)
	initial_progs = pr_typecurrent;
	PR_MoveParms(progfuncs, prnum, pr_typecurrent);
	PR_SwitchProgs(progfuncs, prnum);


	oldexitdepth = prinst->exitdepth;
	prinst->exitdepth = pr_depth;

	ls = 0;
	//add on the callstack.
	for (i = 0; i < thread->fstackdepth; i++)
	{
		if (pr_depth == prinst->exitdepth)
		{
			pr_stack[pr_depth].f = pr_xfunction;
			pr_stack[pr_depth].s = pr_xstatement;
			pr_stack[pr_depth].progsnum = initial_progs;
		}
		else
		{
			pr_stack[pr_depth].progsnum = thread->fstack[i].progsnum;
			pr_stack[pr_depth].f = pr_progstate[thread->fstack[i].progsnum].functions + thread->fstack[i].fnum;
			pr_stack[pr_depth].s = thread->fstack[i].statement;
		}

		if (i+1 == thread->fstackdepth)
			f = &pr_functions[fnum];
		else
			f = pr_progstate[thread->fstack[i+1].progsnum].functions + thread->fstack[i+1].fnum;
		for (l = 0; l < f->locals; l++)
		{
			localstack[localstack_used++] = ((int *)pr_globals)[f->parm_start + l];
			((int *)pr_globals)[f->parm_start + l] = thread->lstack[ls++];
		}

		pr_depth++;
	}

	if (ls != thread->lstackused)
		PR_RunError(progfuncs, "Thread stores incorrect locals count\n");


	f = &pr_functions[fnum];

//	thread->lstackused -= f->locals;	//the current function is the odd one out.

	//add on the locals stack
	memcpy(localstack+localstack_used, thread->lstack, sizeof(int)*thread->lstackused);
	localstack_used += thread->lstackused;

	//bung the locals of the current function on the stack.
//	for (i=0 ; i < f->locals ; i++)
//		((int *)pr_globals)[f->parm_start + i] = 0xff00ff00;//thread->lstack[thread->lstackused+i];


//	PR_EnterFunction (progfuncs, f, initial_progs);
	oldf = pr_xfunction;
	pr_xfunction = f;
	s = thread->xstatement;

	tempdepth = prinst->numtempstringsstack;
	PR_ExecuteCode(progfuncs, s);


	PR_MoveParms(progfuncs, initial_progs, pr_typecurrent);
	PR_SwitchProgs(progfuncs, initial_progs);
	PR_FreeTemps(progfuncs, tempdepth);
	prinst->numtempstringsstack = tempdepth;

	prinst->exitdepth = oldexitdepth;
	pr_xfunction = oldf;
}

void	PR_AbortStack			(progfuncs_t *progfuncs)
{
	while(pr_depth > prinst->exitdepth+1)
		PR_LeaveFunction(progfuncs);
	prinst->continuestatement = 0;
}