shithub: qk1

Download patch

ref: da5379d799f82ad247c9bb0283d583dcfeac6b32
parent: 2cf72989b0485bb960e4991cbd002787854a1e3f
author: Sigrid Solveig Haflínudóttir <[email protected]>
date: Wed Nov 22 14:35:58 EST 2023

initial multi-progs support work

--- a/cl_parse.c
+++ b/cl_parse.c
@@ -103,7 +103,7 @@
 	}
 	sound_num = (field_mask & cl.protocol.fl_large_sound) ? MSG_ReadShort() : MSG_ReadByte();
 
-	if(ent > MAX_EDICTS)
+	if(ent >= MAX_EDICTS)
 		Host_Error("CL_ParseStartSoundPacket: ent = %d", ent);
 
 	MSG_ReadVec(cl.protocol, pos);
--- a/fs.c
+++ b/fs.c
@@ -512,7 +512,7 @@
 }
 
 static void
-dumpedicts(FILE *bf, edict_t *ed)
+dumpedict(FILE *bf, pr_t *pr, edict_t *ed)
 {
 	int *vp, *ve;
 	char *s;
@@ -524,9 +524,9 @@
 	if(ed->free)
 		goto end;
 	ev = (uchar *)&ed->v;
-	de = pr_fielddefs + progs->numfielddefs;
-	for(d=pr_fielddefs+1; d<de; d++){
-		s = PR_Str(d->s_name);
+	de = pr->fielddefs + pr->numfielddefs;
+	for(d=pr->fielddefs+1; d<de; d++){
+		s = PR_Str(pr, d->s_name);
 		if(s[strlen(s)-2] == '_')
 			continue;
 		/* TODO: pragma pack hazard */
@@ -539,7 +539,7 @@
 		if(vp == ve)
 			continue;
 		fprintf(bf, "\"%s\" ", s);
-		fprintf(bf, "\"%s\"\n", PR_UglyValueString(d->type, v));
+		fprintf(bf, "\"%s\"\n", PR_UglyValueString(pr, d->type, v));
 	}
 end:
 	fprintf(bf, "}\n");
@@ -546,14 +546,14 @@
 }
 
 static void
-dumpdefs(FILE *bf)
+dumpdefs(FILE *bf, pr_t *pr)
 {
 	ushort t;
 	ddef_t *d, *de;
 
 	fprintf(bf, "{\n");
-	de = pr_globaldefs + progs->numglobaldefs;
-	for(d=pr_globaldefs; d<de; d++){
+	de = pr->globaldefs + pr->numglobaldefs;
+	for(d=pr->globaldefs; d<de; d++){
 		t = d->type;
 		if((t & DEF_SAVEGLOBAL) == 0)
 			continue;
@@ -560,8 +560,8 @@
 		t &= ~DEF_SAVEGLOBAL;
 		if(t != ev_string && t != ev_float && t != ev_entity)
 			continue;
-		fprintf(bf, "\"%s\" \"%s\"\n", PR_Str(d->s_name),
-			PR_UglyValueString(t, (eval_t *)&pr_globals[d->ofs]));
+		fprintf(bf, "\"%s\" \"%s\"\n", PR_Str(pr, d->s_name),
+			PR_UglyValueString(pr, t, (eval_t *)&pr->globals[d->ofs]));
 	}
 	fprintf(bf, "}\n");
 }
@@ -589,15 +589,15 @@
 		fprintf(bf, "%s\n", *s != nil ? *s : "m");
 		s++;
 	}
-	dumpdefs(bf);
-	for(i=0; i<sv.num_edicts; i++)
-		dumpedicts(bf, EDICT_NUM(i));
+	dumpdefs(bf, sv.pr);
+	for(i=0; i<sv.pr->num_edicts; i++)
+		dumpedict(bf, sv.pr, EDICT_NUM(sv.pr, i));
 	fclose(bf);
 	return 0;
 }
 
 static void
-loadedicts(FILE *bf)
+loadedicts(FILE *bf, pr_t *pr)
 {
 	int ent, c;
 	char sb[32768], *s;
@@ -624,19 +624,19 @@
 		if(strcmp(com_token, "{") != 0)
 			fatal("loadgame: missing opening brace");
 		if(ent == -1)
-			ED_ParseGlobals(s);
+			ED_ParseGlobals(pr, s);
 		else{
-			ed = EDICT_NUM(ent);
+			ed = EDICT_NUM(pr, ent);
 			/* TODO: pragma pack hazard */
-			memset(&ed->v, 0, progs->entityfields * 4);
+			memset(&ed->v, 0, pr->entityfields * 4);
 			ed->free = 0;
-			ED_ParseEdict(s, ed);
+			ED_ParseEdict(pr, s, ed);
 			if(!ed->free)
 				SV_LinkEdict(ed, 0);
 		}
 		ent++;
 	}while(!feof(bf) && !ferror(bf));
-	sv.num_edicts = ent;
+	pr->num_edicts = ent;
 }
 
 static int
@@ -680,7 +680,7 @@
 		free(s);
 	}
 	r = 0;
-	loadedicts(bf);
+	loadedicts(bf, sv.pr);
 	memcpy(svs.clients->spawn_parms, sp, sizeof sp);
 exit:
 	return r;
--- a/host.c
+++ b/host.c
@@ -269,10 +269,10 @@
 		{
 			// call the prog function for removing a client
 			// this will set the body to a dead frame, among other things
-			saveSelf = pr_global_struct->self;
-			pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
-			PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
-			pr_global_struct->self = saveSelf;
+			saveSelf = sv.pr->global_struct->self;
+			sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, host_client->edict);
+			PR_ExecuteProgram(sv.pr, sv.pr->global_struct->ClientDisconnect);
+			sv.pr->global_struct->self = saveSelf;
 		}
 
 		Con_DPrintf("client %s removed\n", host_client->name);
@@ -404,7 +404,7 @@
 void Host_ServerFrame (void)
 {
 	// run the world state
-	pr_global_struct->frametime = host_frametime;
+	sv.pr->global_struct->frametime = host_frametime;
 
 	// set the time and clear the general datagram
 	SV_ClearDatagram ();
--- a/host_cmd.c
+++ b/host_cmd.c
@@ -90,7 +90,7 @@
 		return;
 	}
 
-	if (pr_global_struct->deathmatch)
+	if (sv.pr->global_struct->deathmatch)
 		return;
 
 	sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE;
@@ -108,7 +108,7 @@
 		return;
 	}
 
-	if (pr_global_struct->deathmatch)
+	if (sv.pr->global_struct->deathmatch)
 		return;
 
 	sv_player->v.flags = (int)sv_player->v.flags ^ FL_NOTARGET;
@@ -128,7 +128,7 @@
 		return;
 	}
 
-	if (pr_global_struct->deathmatch)
+	if (sv.pr->global_struct->deathmatch)
 		return;
 
 	if (sv_player->v.movetype != MOVETYPE_NOCLIP)
@@ -160,7 +160,7 @@
 		return;
 	}
 
-	if (pr_global_struct->deathmatch)
+	if (sv.pr->global_struct->deathmatch)
 		return;
 
 	if (sv_player->v.movetype != MOVETYPE_FLY)
@@ -380,7 +380,7 @@
 		if(strcmp(host_client->name, newName) != 0)
 			Con_Printf ("%s renamed to %s\n", host_client->name, newName);
 	strcpy(host_client->name, newName);
-	host_client->edict->v.netname = PR_SetStr(host_client->name);
+	host_client->edict->v.netname = PR_SetStr(sv.pr, host_client->name);
 
 	// send notification to all clients
 
@@ -595,9 +595,9 @@
 		return;
 	}
 
-	pr_global_struct->time = sv.time;
-	pr_global_struct->self = EDICT_TO_PROG(sv_player);
-	PR_ExecuteProgram (pr_global_struct->ClientKill);
+	sv.pr->global_struct->time = sv.time;
+	sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, sv_player);
+	PR_ExecuteProgram (sv.pr, sv.pr->global_struct->ClientKill);
 }
 
 
@@ -622,11 +622,11 @@
 
 		if (sv.paused)
 		{
-			SV_BroadcastPrintf ("%s paused the game\n", PR_Str(sv_player->v.netname));
+			SV_BroadcastPrintf ("%s paused the game\n", PR_Str(sv.pr, sv_player->v.netname));
 		}
 		else
 		{
-			SV_BroadcastPrintf ("%s unpaused the game\n", PR_Str(sv_player->v.netname));
+			SV_BroadcastPrintf ("%s unpaused the game\n", PR_Str(sv.pr, sv_player->v.netname));
 		}
 
 		// send notification to all clients
@@ -698,26 +698,26 @@
 		// set up the edict
 		ent = host_client->edict;
 
-		memset (&ent->v, 0, progs->entityfields * 4);
-		ent->v.colormap = NUM_FOR_EDICT(ent);
+		memset (&ent->v, 0, sv.pr->entityfields * 4);
+		ent->v.colormap = NUM_FOR_EDICT(sv.pr, ent);
 		ent->v.team = (host_client->colors & 15) + 1;
-		ent->v.netname = PR_SetStr(host_client->name);
+		ent->v.netname = PR_SetStr(sv.pr, host_client->name);
 
 		// copy spawn parms out of the client_t
 
 		for (i=0 ; i< Nparms ; i++)
-			(&pr_global_struct->parm1)[i] = host_client->spawn_parms[i];
+			(&sv.pr->global_struct->parm1)[i] = host_client->spawn_parms[i];
 
 		// call the spawn function
 
-		pr_global_struct->time = sv.time;
-		pr_global_struct->self = EDICT_TO_PROG(sv_player);
-		PR_ExecuteProgram (pr_global_struct->ClientConnect);
+		sv.pr->global_struct->time = sv.time;
+		sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, sv_player);
+		PR_ExecuteProgram (sv.pr, sv.pr->global_struct->ClientConnect);
 
 		if ((dtime() - host_client->netconnection->connecttime) <= sv.time)
 			Con_DPrintf("%s entered the game\n", host_client->name);
 
-		PR_ExecuteProgram (pr_global_struct->PutClientInServer);
+		PR_ExecuteProgram (sv.pr, sv.pr->global_struct->PutClientInServer);
 	}
 
 
@@ -752,19 +752,19 @@
 	// send some stats
 	MSG_WriteByte (&host_client->message, svc_updatestat);
 	MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS);
-	MSG_WriteLong (&host_client->message, pr_global_struct->total_secrets);
+	MSG_WriteLong (&host_client->message, sv.pr->global_struct->total_secrets);
 
 	MSG_WriteByte (&host_client->message, svc_updatestat);
 	MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS);
-	MSG_WriteLong (&host_client->message, pr_global_struct->total_monsters);
+	MSG_WriteLong (&host_client->message, sv.pr->global_struct->total_monsters);
 
 	MSG_WriteByte (&host_client->message, svc_updatestat);
 	MSG_WriteByte (&host_client->message, STAT_SECRETS);
-	MSG_WriteLong (&host_client->message, pr_global_struct->found_secrets);
+	MSG_WriteLong (&host_client->message, sv.pr->global_struct->found_secrets);
 
 	MSG_WriteByte (&host_client->message, svc_updatestat);
 	MSG_WriteByte (&host_client->message, STAT_MONSTERS);
-	MSG_WriteLong (&host_client->message, pr_global_struct->killed_monsters);
+	MSG_WriteLong (&host_client->message, sv.pr->global_struct->killed_monsters);
 
 
 	// send a fixangle
@@ -772,7 +772,7 @@
 	// in a state where it is expecting the client to correct the angle
 	// and it won't happen if the game was just loaded, so you wind up
 	// with a permanent head tilt
-	ent = EDICT_NUM( 1 + (host_client - svs.clients) );
+	ent = EDICT_NUM(sv.pr, 1 + (host_client - svs.clients) );
 	MSG_WriteByte (&host_client->message, svc_setangle);
 	a = sv.loadgame ? ent->v.v_angle : ent->v.angles;
 	for(i = 0; i < 2; i++)
@@ -916,7 +916,7 @@
 			return;
 		}
 	}
-	else if (pr_global_struct->deathmatch)
+	else if (sv.pr->global_struct->deathmatch)
 		return;
 
 	save = host_client;
@@ -1004,7 +1004,7 @@
 		return;
 	}
 
-	if (pr_global_struct->deathmatch)
+	if (sv.pr->global_struct->deathmatch)
 		return;
 
 	t = Cmd_Argv(1);
@@ -1049,7 +1049,7 @@
 	case 's':
 		if (rogue)
 		{
-			val = GetEdictFieldValue(sv_player, "ammo_shells1");
+			val = GetEdictFieldValue(sv.pr, sv_player, "ammo_shells1");
 			if (val)
 				val->_float = v;
 		}
@@ -1059,7 +1059,7 @@
 	case 'n':
 		if (rogue)
 		{
-			val = GetEdictFieldValue(sv_player, "ammo_nails1");
+			val = GetEdictFieldValue(sv.pr, sv_player, "ammo_nails1");
 			if (val)
 			{
 				val->_float = v;
@@ -1075,7 +1075,7 @@
 	case 'l':
 		if (rogue)
 		{
-			val = GetEdictFieldValue(sv_player, "ammo_lava_nails");
+			val = GetEdictFieldValue(sv.pr, sv_player, "ammo_lava_nails");
 			if (val)
 			{
 				val->_float = v;
@@ -1087,7 +1087,7 @@
 	case 'r':
 		if (rogue)
 		{
-			val = GetEdictFieldValue(sv_player, "ammo_rockets1");
+			val = GetEdictFieldValue(sv.pr, sv_player, "ammo_rockets1");
 			if (val)
 			{
 				val->_float = v;
@@ -1103,7 +1103,7 @@
 	case 'm':
 		if (rogue)
 		{
-			val = GetEdictFieldValue(sv_player, "ammo_multi_rockets");
+			val = GetEdictFieldValue(sv.pr, sv_player, "ammo_multi_rockets");
 			if (val)
 			{
 				val->_float = v;
@@ -1118,7 +1118,7 @@
 	case 'c':
 		if (rogue)
 		{
-			val = GetEdictFieldValue(sv_player, "ammo_cells1");
+			val = GetEdictFieldValue(sv.pr, sv_player, "ammo_cells1");
 			if (val)
 			{
 				val->_float = v;
@@ -1134,7 +1134,7 @@
 	case 'p':
 		if (rogue)
 		{
-			val = GetEdictFieldValue(sv_player, "ammo_plasma");
+			val = GetEdictFieldValue(sv.pr, sv_player, "ammo_plasma");
 			if (val)
 			{
 				val->_float = v;
@@ -1151,10 +1151,10 @@
 	int		i;
 	edict_t	*e;
 
-	for (i=0 ; i<sv.num_edicts ; i++)
+	for (i=0 ; i<sv.pr->num_edicts ; i++)
 	{
-		e = EDICT_NUM(i);
-		if(strcmp(PR_Str(e->v.classname), "viewthing") == 0)
+		e = EDICT_NUM(sv.pr, i);
+		if(strcmp(PR_Str(sv.pr, e->v.classname), "viewthing") == 0)
 			return e;
 	}
 	Con_Printf ("No viewthing on map\n");
--- a/model.c
+++ b/model.c
@@ -277,13 +277,10 @@
 Loads in a model for the given name
 ==================
 */
-model_t *Mod_ForName (char *name, bool crash)
+model_t *
+Mod_ForName(char *name, bool crash)
 {
-	model_t	*mod;
-
-	mod = Mod_FindName (name);
-
-	return Mod_LoadModel (mod, crash);
+	return Mod_LoadModel(Mod_FindName(name), crash);
 }
 
 /*
--- a/pr_cmds.c
+++ b/pr_cmds.c
@@ -1,6 +1,6 @@
 #include "quakedef.h"
 
-#define	RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e))
+#define	RETURN_EDICT(pr, e) (((int *)pr->globals)[OFS_RETURN] = EDICT_TO_PROG(pr, e))
 
 /*
 ===============================================================================
@@ -10,15 +10,15 @@
 ===============================================================================
 */
 
-char *PF_VarString (int	first)
+char *PF_VarString (pr_t *pr, int first)
 {
 	int		i;
 	static char out[256];
 
 	out[0] = 0;
-	for (i=first ; i<pr_argc ; i++)
+	for (i=first ; i<pr->argc ; i++)
 	{
-		strcat (out, G_STRING((OFS_PARM0+i*3)));
+		strcat (out, G_STRING(pr, (OFS_PARM0+i*3)));
 	}
 	return out;
 }
@@ -34,16 +34,15 @@
 error(value)
 =================
 */
-void PF_error (void)
+void PF_error (pr_t *pr)
 {
 	char	*s;
 	edict_t	*ed;
 
-	s = PF_VarString(0);
-	Con_Printf ("======SERVER ERROR in %s:\n%s\n"
-	, PR_Str(pr_xfunction->s_name),s);
-	ed = PROG_TO_EDICT(pr_global_struct->self);
-	ED_Print (ed);
+	s = PF_VarString(pr, 0);
+	Con_Printf ("======SERVER ERROR in %s:\n%s\n", PR_Str(pr, pr->xfunction->s_name),s);
+	ed = PROG_TO_EDICT(pr, pr->global_struct->self);
+	ED_Print (pr, ed);
 
 	Host_Error ("Program error");
 }
@@ -58,16 +57,15 @@
 objerror(value)
 =================
 */
-void PF_objerror (void)
+void PF_objerror (pr_t *pr)
 {
 	char	*s;
 	edict_t	*ed;
 
-	s = PF_VarString(0);
-	Con_Printf ("======OBJECT ERROR in %s:\n%s\n"
-	, PR_Str(pr_xfunction->s_name),s);
-	ed = PROG_TO_EDICT(pr_global_struct->self);
-	ED_Print (ed);
+	s = PF_VarString(pr, 0);
+	Con_Printf ("======OBJECT ERROR in %s:\n%s\n", PR_Str(pr, pr->xfunction->s_name),s);
+	ed = PROG_TO_EDICT(pr, pr->global_struct->self);
+	ED_Print (pr, ed);
 	ED_Free (ed);
 
 	Host_Error ("Program error");
@@ -83,9 +81,9 @@
 makevectors(vector)
 ==============
 */
-void PF_makevectors (void)
+void PF_makevectors (pr_t *pr)
 {
-	AngleVectors (G_VECTOR(OFS_PARM0), pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up);
+	AngleVectors (G_VECTOR(pr, OFS_PARM0), pr->global_struct->v_forward, pr->global_struct->v_right, pr->global_struct->v_up);
 }
 
 /*
@@ -97,19 +95,19 @@
 setorigin (entity, origin)
 =================
 */
-void PF_setorigin (void)
+void PF_setorigin (pr_t *pr)
 {
 	edict_t	*e;
 	float	*org;
 
-	e = G_EDICT(OFS_PARM0);
-	org = G_VECTOR(OFS_PARM1);
+	e = G_EDICT(pr, OFS_PARM0);
+	org = G_VECTOR(pr, OFS_PARM1);
 	VectorCopy (org, e->v.origin);
 	SV_LinkEdict (e, false);
 }
 
 
-void SetMinMaxSize (edict_t *e, float *min, float *max, bool rotate)
+void SetMinMaxSize (pr_t *pr, edict_t *e, float *min, float *max, bool rotate)
 {
 	float	*angles;
 	vec3_t	rmin, rmax;
@@ -121,7 +119,7 @@
 
 	for (i=0 ; i<3 ; i++)
 		if (min[i] > max[i])
-			PR_RunError ("backwards mins/maxs");
+			PR_RunError (pr, "backwards mins/maxs");
 
 	rotate = false;		// FIXME: implement rotation properly again
 
@@ -192,15 +190,15 @@
 setsize (entity, minvector, maxvector)
 =================
 */
-void PF_setsize (void)
+void PF_setsize (pr_t *pr)
 {
 	edict_t	*e;
 	float	*min, *max;
 
-	e = G_EDICT(OFS_PARM0);
-	min = G_VECTOR(OFS_PARM1);
-	max = G_VECTOR(OFS_PARM2);
-	SetMinMaxSize (e, min, max, false);
+	e = G_EDICT(pr, OFS_PARM0);
+	min = G_VECTOR(pr, OFS_PARM1);
+	max = G_VECTOR(pr, OFS_PARM2);
+	SetMinMaxSize (pr, e, min, max, false);
 }
 
 
@@ -211,7 +209,7 @@
 setmodel(entity, model)
 =================
 */
-void PF_setmodel (void)
+void PF_setmodel (pr_t *pr)
 {
 	edict_t	*e;
 	char	*m, **check;
@@ -218,8 +216,8 @@
 	model_t	*mod;
 	int		i;
 
-	e = G_EDICT(OFS_PARM0);
-	m = G_STRING(OFS_PARM1);
+	e = G_EDICT(pr, OFS_PARM0);
+	m = G_STRING(pr, OFS_PARM1);
 
 	// check to see if model was properly precached
 	for (i=0, check = sv.model_precache ; *check ; i++, check++)
@@ -226,16 +224,16 @@
 		if (!strcmp(*check, m))
 			break;
 	if (!*check)
-		PR_RunError ("no precache: %s\n", m);
-	e->v.model = PR_SetStr(m);
+		PR_RunError (pr, "no precache: %s\n", m);
+	e->v.model = PR_SetStr(pr, m);
 	e->v.modelindex = i; //SV_ModelIndex (m);
 
 	mod = sv.models[ (int)e->v.modelindex];  // Mod_ForName (m, true);
 
 	if(!mod)
-		SetMinMaxSize (e, vec3_origin, vec3_origin, true);
+		SetMinMaxSize (pr, e, vec3_origin, vec3_origin, true);
 	else
-		SetMinMaxSize (e, mod->mins, mod->maxs, true);
+		SetMinMaxSize (pr, e, mod->mins, mod->maxs, true);
 }
 
 /*
@@ -247,11 +245,11 @@
 bprint(value)
 =================
 */
-void PF_bprint (void)
+void PF_bprint (pr_t *pr)
 {
 	char		*s;
 
-	s = PF_VarString(0);
+	s = PF_VarString(pr, 0);
 	SV_BroadcastPrintf ("%s", s);
 }
 
@@ -264,14 +262,14 @@
 sprint(clientent, value)
 =================
 */
-void PF_sprint (void)
+void PF_sprint (pr_t *pr)
 {
 	char		*s;
 	client_t	*client;
 	int			entnum;
 
-	entnum = G_EDICTNUM(OFS_PARM0);
-	s = PF_VarString(1);
+	entnum = G_EDICTNUM(pr, OFS_PARM0);
+	s = PF_VarString(pr, 1);
 
 	if (entnum < 1 || entnum > svs.maxclients)
 	{
@@ -295,14 +293,14 @@
 centerprint(clientent, value)
 =================
 */
-void PF_centerprint (void)
+void PF_centerprint (pr_t *pr)
 {
 	char		*s;
 	client_t	*client;
 	int			entnum;
 
-	entnum = G_EDICTNUM(OFS_PARM0);
-	s = PF_VarString(1);
+	entnum = G_EDICTNUM(pr, OFS_PARM0);
+	s = PF_VarString(pr, 1);
 
 	if (entnum < 1 || entnum > svs.maxclients)
 	{
@@ -324,13 +322,13 @@
 vector normalize(vector)
 =================
 */
-void PF_normalize (void)
+void PF_normalize (pr_t *pr)
 {
 	float	*v;
 	vec3_t	newvalue;
 	double	ln, a, b, c;
 
-	v = G_VECTOR(OFS_PARM0);
+	v = G_VECTOR(pr, OFS_PARM0);
 	a = v[0];
 	b = v[1];
 	c = v[2];
@@ -344,7 +342,7 @@
 		newvalue[2] = c / ln;
 	}
 
-	VectorCopy(newvalue, G_VECTOR(OFS_RETURN));
+	VectorCopy(newvalue, G_VECTOR(pr, OFS_RETURN));
 }
 
 /*
@@ -354,16 +352,16 @@
 scalar vlen(vector)
 =================
 */
-void PF_vlen (void)
+void PF_vlen (pr_t *pr)
 {
 	float	*v;
 	double	a, b, c;
 
-	v = G_VECTOR(OFS_PARM0);
+	v = G_VECTOR(pr, OFS_PARM0);
 	a = v[0];
 	b = v[1];
 	c = v[2];
-	G_FLOAT(OFS_RETURN) = sqrt(a*a + b*b + c*c);
+	G_FLOAT(pr, OFS_RETURN) = sqrt(a*a + b*b + c*c);
 }
 
 /*
@@ -373,12 +371,12 @@
 float vectoyaw(vector)
 =================
 */
-void PF_vectoyaw (void)
+void PF_vectoyaw (pr_t *pr)
 {
 	float	*value1;
 	float	yaw;
 
-	value1 = G_VECTOR(OFS_PARM0);
+	value1 = G_VECTOR(pr, OFS_PARM0);
 
 	if (value1[1] == 0 && value1[0] == 0)
 		yaw = 0;
@@ -389,7 +387,7 @@
 			yaw += 360;
 	}
 
-	G_FLOAT(OFS_RETURN) = yaw;
+	G_FLOAT(pr, OFS_RETURN) = yaw;
 }
 
 
@@ -400,13 +398,13 @@
 vector vectoangles(vector)
 =================
 */
-void PF_vectoangles (void)
+void PF_vectoangles (pr_t *pr)
 {
 	float	*value1;
 	double	forward;
 	double	yaw, pitch;
 
-	value1 = G_VECTOR(OFS_PARM0);
+	value1 = G_VECTOR(pr, OFS_PARM0);
 
 	if (value1[1] == 0 && value1[0] == 0)
 	{
@@ -428,9 +426,9 @@
 			pitch += 360;
 	}
 
-	G_FLOAT(OFS_RETURN+0) = pitch;
-	G_FLOAT(OFS_RETURN+1) = yaw;
-	G_FLOAT(OFS_RETURN+2) = 0;
+	G_FLOAT(pr, OFS_RETURN+0) = pitch;
+	G_FLOAT(pr, OFS_RETURN+1) = yaw;
+	G_FLOAT(pr, OFS_RETURN+2) = 0;
 }
 
 /*
@@ -442,13 +440,13 @@
 random()
 =================
 */
-void PF_random (void)
+void PF_random (pr_t *pr)
 {
 	float		num;
 
 	num = (rand ()&0x7fff) / ((float)0x7fff);
 
-	G_FLOAT(OFS_RETURN) = num;
+	G_FLOAT(pr, OFS_RETURN) = num;
 }
 
 /*
@@ -458,16 +456,16 @@
 particle(origin, color, count)
 =================
 */
-void PF_particle (void)
+void PF_particle (pr_t *pr)
 {
 	float		*org, *dir;
 	float		color;
 	float		count;
 
-	org = G_VECTOR(OFS_PARM0);
-	dir = G_VECTOR(OFS_PARM1);
-	color = G_FLOAT(OFS_PARM2);
-	count = G_FLOAT(OFS_PARM3);
+	org = G_VECTOR(pr, OFS_PARM0);
+	dir = G_VECTOR(pr, OFS_PARM1);
+	color = G_FLOAT(pr, OFS_PARM2);
+	count = G_FLOAT(pr, OFS_PARM3);
 	SV_StartParticle (org, dir, color, count);
 }
 
@@ -478,7 +476,7 @@
 
 =================
 */
-void PF_ambientsound (void)
+void PF_ambientsound (pr_t *pr)
 {
 	char		**check;
 	char		*samp;
@@ -486,10 +484,10 @@
 	float 		vol, attenuation;
 	int			i, soundnum;
 
-	pos = G_VECTOR (OFS_PARM0);
-	samp = G_STRING(OFS_PARM1);
-	vol = G_FLOAT(OFS_PARM2);
-	attenuation = G_FLOAT(OFS_PARM3);
+	pos = G_VECTOR(pr, OFS_PARM0);
+	samp = G_STRING(pr, OFS_PARM1);
+	vol = G_FLOAT(pr, OFS_PARM2);
+	attenuation = G_FLOAT(pr, OFS_PARM3);
 
 	// check to see if samp was properly precached
 	for (soundnum=0, check = sv.sound_precache ; *check ; check++, soundnum++)
@@ -537,7 +535,7 @@
 
 =================
 */
-void PF_sound (void)
+void PF_sound (pr_t *pr)
 {
 	char		*sample;
 	int			channel;
@@ -545,11 +543,11 @@
 	int 		volume;
 	float attenuation;
 
-	entity = G_EDICT(OFS_PARM0);
-	channel = G_FLOAT(OFS_PARM1);
-	sample = G_STRING(OFS_PARM2);
-	volume = G_FLOAT(OFS_PARM3) * 255;
-	attenuation = G_FLOAT(OFS_PARM4);
+	entity = G_EDICT(pr, OFS_PARM0);
+	channel = G_FLOAT(pr, OFS_PARM1);
+	sample = G_STRING(pr, OFS_PARM2);
+	volume = G_FLOAT(pr, OFS_PARM3) * 255;
+	attenuation = G_FLOAT(pr, OFS_PARM4);
 
 	if (volume < 0 || volume > 255)
 		fatal ("SV_StartSound: volume = %d", volume);
@@ -570,11 +568,12 @@
 break()
 =================
 */
-void PF_break (void)
+void PF_break (pr_t *pr)
 {
-	Con_Printf ("break statement\n");
+	USED(pr);
+	Con_Printf("break statement\n");
 	assert(nil);
-	//	PR_RunError ("break statement");
+	//	PR_RunError (pr, "break statement");
 }
 
 /*
@@ -588,7 +587,7 @@
 traceline (vector1, vector2, tryents)
 =================
 */
-void PF_traceline (void)
+void PF_traceline (pr_t *pr)
 {
 	float	*v1, *v2;
 	trace_t	trace;
@@ -595,25 +594,25 @@
 	int		nomonsters;
 	edict_t	*ent;
 
-	v1 = G_VECTOR(OFS_PARM0);
-	v2 = G_VECTOR(OFS_PARM1);
-	nomonsters = G_FLOAT(OFS_PARM2);
-	ent = G_EDICT(OFS_PARM3);
+	v1 = G_VECTOR(pr, OFS_PARM0);
+	v2 = G_VECTOR(pr, OFS_PARM1);
+	nomonsters = G_FLOAT(pr, OFS_PARM2);
+	ent = G_EDICT(pr, OFS_PARM3);
 
 	trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent);
 
-	pr_global_struct->trace_allsolid = trace.allsolid;
-	pr_global_struct->trace_startsolid = trace.startsolid;
-	pr_global_struct->trace_fraction = trace.fraction;
-	pr_global_struct->trace_inwater = trace.inwater;
-	pr_global_struct->trace_inopen = trace.inopen;
-	VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
-	VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
-	pr_global_struct->trace_plane_dist =  trace.plane.dist;
+	pr->global_struct->trace_allsolid = trace.allsolid;
+	pr->global_struct->trace_startsolid = trace.startsolid;
+	pr->global_struct->trace_fraction = trace.fraction;
+	pr->global_struct->trace_inwater = trace.inwater;
+	pr->global_struct->trace_inopen = trace.inopen;
+	VectorCopy (trace.endpos, pr->global_struct->trace_endpos);
+	VectorCopy (trace.plane.normal, pr->global_struct->trace_plane_normal);
+	pr->global_struct->trace_plane_dist =  trace.plane.dist;
 	if (trace.ent)
-		pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
+		pr->global_struct->trace_ent = EDICT_TO_PROG(pr, trace.ent);
 	else
-		pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
+		pr->global_struct->trace_ent = EDICT_TO_PROG(pr, pr->edicts);
 }
 
 //============================================================================
@@ -621,7 +620,7 @@
 static byte *checkpvs;
 static int checkpvs_size;
 
-int PF_newcheckclient (int check)
+int PF_newcheckclient (pr_t *pr, int check)
 {
 	int		i, size;
 	byte	*pvs;
@@ -646,7 +645,7 @@
 		if (i == svs.maxclients+1)
 			i = 1;
 
-		ent = EDICT_NUM(i);
+		ent = EDICT_NUM(pr, i);
 
 		if (i == check)
 			break;	// didn't find anything else
@@ -691,7 +690,7 @@
 =================
 */
 #define	MAX_CHECK	16
-void PF_checkclient (void)
+void PF_checkclient (pr_t *pr)
 {
 	edict_t	*ent, *self;
 	mleaf_t	*leaf;
@@ -701,31 +700,31 @@
 	// find a new check if on a new frame
 	if (sv.time - sv.lastchecktime >= 0.1)
 	{
-		sv.lastcheck = PF_newcheckclient (sv.lastcheck);
+		sv.lastcheck = PF_newcheckclient (pr, sv.lastcheck);
 		sv.lastchecktime = sv.time;
 	}
 
 	// return check if it might be visible
-	ent = EDICT_NUM(sv.lastcheck);
+	ent = EDICT_NUM(pr, sv.lastcheck);
 	if (ent->free || ent->v.health <= 0)
 	{
-		RETURN_EDICT(sv.edicts);
+		RETURN_EDICT(pr, pr->edicts);
 		return;
 	}
 
 	// if current entity can't possibly see the check entity, return 0
-	self = PROG_TO_EDICT(pr_global_struct->self);
+	self = PROG_TO_EDICT(pr, pr->global_struct->self);
 	VectorAdd (self->v.origin, self->v.view_ofs, view);
 	leaf = Mod_PointInLeaf (view, sv.worldmodel);
 	l = (leaf - sv.worldmodel->leafs) - 1;
 	if ( (l<0) || !(checkpvs[l>>3] & (1<<(l&7)) ) )
 	{
-		RETURN_EDICT(sv.edicts);
+		RETURN_EDICT(pr, pr->edicts);
 		return;
 	}
 
 	// might be able to see it
-	RETURN_EDICT(ent);
+	RETURN_EDICT(pr, ent);
 }
 
 //============================================================================
@@ -740,16 +739,16 @@
 stuffcmd (clientent, value)
 =================
 */
-void PF_stuffcmd (void)
+void PF_stuffcmd (pr_t *pr)
 {
 	int		entnum;
 	char	*str;
 	client_t	*old;
 
-	entnum = G_EDICTNUM(OFS_PARM0);
+	entnum = G_EDICTNUM(pr, OFS_PARM0);
 	if (entnum < 1 || entnum > svs.maxclients)
-		PR_RunError ("Parm 0 not a client");
-	str = G_STRING(OFS_PARM1);
+		PR_RunError (pr, "Parm 0 not a client");
+	str = G_STRING(pr, OFS_PARM1);
 
 	old = host_client;
 	host_client = &svs.clients[entnum-1];
@@ -766,11 +765,11 @@
 localcmd (string)
 =================
 */
-void PF_localcmd (void)
+void PF_localcmd (pr_t *pr)
 {
 	char	*str;
 
-	str = G_STRING(OFS_PARM0);
+	str = G_STRING(pr, OFS_PARM0);
 	Cbuf_AddText (str);
 }
 
@@ -781,13 +780,13 @@
 float cvar (string)
 =================
 */
-void PF_cvar (void)
+void PF_cvar (pr_t *pr)
 {
 	char	*str;
 
-	str = G_STRING(OFS_PARM0);
+	str = G_STRING(pr, OFS_PARM0);
 
-	G_FLOAT(OFS_RETURN) = Cvar_VariableValue (str);
+	G_FLOAT(pr, OFS_RETURN) = Cvar_VariableValue (str);
 }
 
 /*
@@ -797,12 +796,12 @@
 float cvar (string)
 =================
 */
-void PF_cvar_set (void)
+void PF_cvar_set (pr_t *pr)
 {
 	char	*var, *val;
 
-	var = G_STRING(OFS_PARM0);
-	val = G_STRING(OFS_PARM1);
+	var = G_STRING(pr, OFS_PARM0);
+	val = G_STRING(pr, OFS_PARM1);
 
 	setcvar (var, val);
 }
@@ -816,7 +815,7 @@
 findradius (origin, radius)
 =================
 */
-void PF_findradius (void)
+void PF_findradius (pr_t *pr)
 {
 	edict_t	*ent, *chain;
 	float	rad;
@@ -824,13 +823,13 @@
 	vec3_t	eorg;
 	int		i, j;
 
-	chain = (edict_t *)sv.edicts;
+	chain = (edict_t *)pr->edicts;
 
-	org = G_VECTOR(OFS_PARM0);
-	rad = G_FLOAT(OFS_PARM1);
+	org = G_VECTOR(pr, OFS_PARM0);
+	rad = G_FLOAT(pr, OFS_PARM1);
 
-	ent = NEXT_EDICT(sv.edicts);
-	for (i=1 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
+	ent = NEXT_EDICT(pr, pr->edicts);
+	for (i=1 ; i<pr->num_edicts ; i++, ent = NEXT_EDICT(pr, ent))
 	{
 		if (ent->free)
 			continue;
@@ -841,66 +840,66 @@
 		if (Length(eorg) > rad)
 			continue;
 
-		ent->v.chain = EDICT_TO_PROG(chain);
+		ent->v.chain = EDICT_TO_PROG(pr, chain);
 		chain = ent;
 	}
 
-	RETURN_EDICT(chain);
+	RETURN_EDICT(pr, chain);
 }
 
 void
-PF_dprint(void)
+PF_dprint(pr_t *pr)
 {
-	Con_DPrintf("%s", PF_VarString(0));
+	Con_DPrintf("%s", PF_VarString(pr, 0));
 }
 
-void PF_ftos (void)
+void PF_ftos (pr_t *pr)
 {
 	float	v;
 	char *s;
 
-	v = G_FLOAT(OFS_PARM0);
-	s = PR_StrTmp();
+	v = G_FLOAT(pr, OFS_PARM0);
+	s = PR_StrTmp(pr);
 	if (v == (int)v)
 		sprint (s, "%d",(int)v);
 	else
 		sprint (s, "%5.1f",v);
-	G_INT(OFS_RETURN) = PR_SetStr(s);
+	G_INT(pr, OFS_RETURN) = PR_SetStr(pr, s);
 }
 
-void PF_fabs (void)
+void PF_fabs (pr_t *pr)
 {
 	float	v;
-	v = G_FLOAT(OFS_PARM0);
-	G_FLOAT(OFS_RETURN) = fabs(v);
+	v = G_FLOAT(pr, OFS_PARM0);
+	G_FLOAT(pr, OFS_RETURN) = fabs(v);
 }
 
-void PF_vtos (void)
+void PF_vtos (pr_t *pr)
 {
 	char *s;
-	s = PR_StrTmp();
-	sprint (s, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]);
-	G_INT(OFS_RETURN) = PR_SetStr(s);
+	s = PR_StrTmp(pr);
+	sprint (s, "'%5.1f %5.1f %5.1f'", G_VECTOR(pr, OFS_PARM0)[0], G_VECTOR(pr, OFS_PARM0)[1], G_VECTOR(pr, OFS_PARM0)[2]);
+	G_INT(pr, OFS_RETURN) = PR_SetStr(pr, s);
 }
 
-void PF_Spawn (void)
+void PF_Spawn (pr_t *pr)
 {
 	edict_t	*ed;
-	ed = ED_Alloc();
-	RETURN_EDICT(ed);
+	ed = ED_Alloc(pr);
+	RETURN_EDICT(pr, ed);
 }
 
-void PF_Remove (void)
+void PF_Remove (pr_t *pr)
 {
 	edict_t	*ed;
 
-	ed = G_EDICT(OFS_PARM0);
+	ed = G_EDICT(pr, OFS_PARM0);
 	ED_Free (ed);
 }
 
 
 // entity (entity start, .string field, string match) find = #5;
-void PF_Find (void)
+void PF_Find (pr_t *pr)
 {
 	int		e;
 	int		f;
@@ -907,52 +906,97 @@
 	char	*s, *t;
 	edict_t	*ed;
 
-	e = G_EDICTNUM(OFS_PARM0);
-	f = G_INT(OFS_PARM1);
-	s = G_STRING(OFS_PARM2);
+	e = G_EDICTNUM(pr, OFS_PARM0);
+	f = G_INT(pr, OFS_PARM1);
+	s = G_STRING(pr, OFS_PARM2);
 	if (!s)
-		PR_RunError ("PF_Find: bad search string");
+		PR_RunError (pr, "PF_Find: bad search string");
 
-	for (e++ ; e < sv.num_edicts ; e++)
+	for (e++ ; e < pr->num_edicts ; e++)
 	{
-		ed = EDICT_NUM(e);
+		ed = EDICT_NUM(pr, e);
 		if (ed->free)
 			continue;
-		t = E_STRING(ed,f);
+		t = E_STRING(pr, ed, f);
 		if (!t)
 			continue;
-		if (!strcmp(t,s))
+		if (!strcmp(t, s))
 		{
-			RETURN_EDICT(ed);
+			RETURN_EDICT(pr, ed);
 			return;
 		}
 	}
 
-	RETURN_EDICT(sv.edicts);
+	RETURN_EDICT(pr, pr->edicts);
 }
 
-void PR_CheckEmptyString (char *s)
+void PR_CheckEmptyString (pr_t *pr, char *s)
 {
 	if (s[0] <= ' ')
-		PR_RunError ("Bad string");
+		PR_RunError (pr, "Bad string");
 }
 
-void PF_precache_file (void)
+static bool
+CloseEnough(edict_t *ent, edict_t *goal, float dist)
+{
+	int		i;
+
+	for (i=0 ; i<3 ; i++)
+	{
+		if (goal->v.absmin[i] > ent->v.absmax[i] + dist)
+			return false;
+		if (goal->v.absmax[i] < ent->v.absmin[i] - dist)
+			return false;
+	}
+	return true;
+}
+
+bool SV_StepDirection (edict_t *ent, float yaw, float dist);
+void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist);
+
+void PF_MoveToGoal (pr_t *pr)
+{
+	edict_t		*ent, *goal;
+	float		dist;
+
+	ent = PROG_TO_EDICT(pr, pr->global_struct->self);
+	goal = PROG_TO_EDICT(pr, ent->v.goalentity);
+	dist = G_FLOAT(pr, OFS_PARM0);
+
+	if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
+	{
+		G_FLOAT(pr, OFS_RETURN) = 0;
+		return;
+	}
+
+	// if the next step hits the enemy, return immediately
+	if ( PROG_TO_EDICT(pr, ent->v.enemy) != pr->edicts &&  CloseEnough (ent, goal, dist) )
+		return;
+
+	// bump around...
+	if ( (rand()&3)==1 ||
+	!SV_StepDirection (ent, ent->v.ideal_yaw, dist))
+	{
+		SV_NewChaseDir (ent, goal, dist);
+	}
+}
+
+void PF_precache_file (pr_t *pr)
 {	// precache_file is only used to copy files with qcc, it does nothing
-	G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
+	G_INT(pr, OFS_RETURN) = G_INT(pr, OFS_PARM0);
 }
 
-void PF_precache_sound (void)
+void PF_precache_sound (pr_t *pr)
 {
 	char	*s;
 	int		i;
 
 	if (sv.state != ss_loading)
-		PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions");
+		PR_RunError (pr, "PF_Precache_*: Precache can only be done in spawn functions");
 
-	s = G_STRING(OFS_PARM0);
-	G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
-	PR_CheckEmptyString (s);
+	s = G_STRING(pr, OFS_PARM0);
+	G_INT(pr, OFS_RETURN) = G_INT(pr, OFS_PARM0);
+	PR_CheckEmptyString(pr, s);
 
 	for (i=0 ; i<MAX_SOUNDS ; i++)
 	{
@@ -964,20 +1008,20 @@
 		if (!strcmp(sv.sound_precache[i], s))
 			return;
 	}
-	PR_RunError ("PF_precache_sound: overflow");
+	PR_RunError(pr, "PF_precache_sound: overflow");
 }
 
-void PF_precache_model (void)
+void PF_precache_model (pr_t *pr)
 {
 	char	*s;
 	int		i;
 
 	if (sv.state != ss_loading)
-		PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions");
+		PR_RunError (pr, "PF_Precache_*: Precache can only be done in spawn functions");
 
-	s = G_STRING(OFS_PARM0);
-	G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
-	PR_CheckEmptyString (s);
+	s = G_STRING(pr, OFS_PARM0);
+	G_INT(pr, OFS_RETURN) = G_INT(pr, OFS_PARM0);
+	PR_CheckEmptyString (pr, s);
 
 	for (i=0 ; i<MAX_MODELS ; i++)
 	{
@@ -990,28 +1034,30 @@
 		if (!strcmp(sv.model_precache[i], s))
 			return;
 	}
-	PR_RunError ("PF_precache_model: overflow");
+	PR_RunError (pr,"PF_precache_model: overflow");
 }
 
 
-void PF_coredump (void)
+void PF_coredump (pr_t *pr)
 {
-	ED_PrintEdicts ();
+	// FIXME(sigrid): needs to be pr-specific
+	USED(pr);
+	ED_PrintEdicts();
 }
 
-void PF_traceon (void)
+void PF_traceon (pr_t *pr)
 {
-	pr_trace = true;
+	pr->trace = true;
 }
 
-void PF_traceoff (void)
+void PF_traceoff (pr_t *pr)
 {
-	pr_trace = false;
+	pr->trace = false;
 }
 
-void PF_eprint (void)
+void PF_eprint (pr_t *pr)
 {
-	ED_PrintNum (G_EDICTNUM(OFS_PARM0));
+	ED_PrintNum(pr, G_EDICTNUM(pr, OFS_PARM0));
 }
 
 /*
@@ -1021,7 +1067,7 @@
 float(float yaw, float dist) walkmove
 ===============
 */
-void PF_walkmove (void)
+void PF_walkmove (pr_t *pr)
 {
 	edict_t	*ent;
 	float	yaw, dist;
@@ -1029,13 +1075,13 @@
 	dfunction_t	*oldf;
 	int 	oldself;
 
-	ent = PROG_TO_EDICT(pr_global_struct->self);
-	yaw = G_FLOAT(OFS_PARM0);
-	dist = G_FLOAT(OFS_PARM1);
+	ent = PROG_TO_EDICT(pr, pr->global_struct->self);
+	yaw = G_FLOAT(pr, OFS_PARM0);
+	dist = G_FLOAT(pr, OFS_PARM1);
 
 	if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
 	{
-		G_FLOAT(OFS_RETURN) = 0;
+		G_FLOAT(pr, OFS_RETURN) = 0;
 		return;
 	}
 
@@ -1046,15 +1092,15 @@
 	move[2] = 0;
 
 	// save program state, because SV_movestep may call other progs
-	oldf = pr_xfunction;
-	oldself = pr_global_struct->self;
+	oldf = pr->xfunction;
+	oldself = pr->global_struct->self;
 
-	G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true);
+	G_FLOAT(pr, OFS_RETURN) = SV_movestep(ent, move, true);
 
 
 	// restore program state
-	pr_xfunction = oldf;
-	pr_global_struct->self = oldself;
+	pr->xfunction = oldf;
+	pr->global_struct->self = oldself;
 }
 
 /*
@@ -1064,13 +1110,13 @@
 void() droptofloor
 ===============
 */
-void PF_droptofloor (void)
+void PF_droptofloor (pr_t *pr)
 {
 	edict_t		*ent;
 	vec3_t		end;
 	trace_t		trace;
 
-	ent = PROG_TO_EDICT(pr_global_struct->self);
+	ent = PROG_TO_EDICT(pr, pr->global_struct->self);
 
 	VectorCopy (ent->v.origin, end);
 	end[2] -= 256;
@@ -1078,14 +1124,14 @@
 	trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent);
 
 	if (trace.fraction == 1 || trace.allsolid)
-		G_FLOAT(OFS_RETURN) = 0;
+		G_FLOAT(pr, OFS_RETURN) = 0;
 	else
 	{
 		VectorCopy (trace.endpos, ent->v.origin);
 		SV_LinkEdict (ent, false);
 		ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
-		ent->v.groundentity = EDICT_TO_PROG(trace.ent);
-		G_FLOAT(OFS_RETURN) = 1;
+		ent->v.groundentity = EDICT_TO_PROG(pr, trace.ent);
+		G_FLOAT(pr, OFS_RETURN) = 1;
 	}
 }
 
@@ -1096,7 +1142,7 @@
 void(float style, string value) lightstyle
 ===============
 */
-void PF_lightstyle (void)
+void PF_lightstyle (pr_t *pr)
 {
 	int		style;
 	char	*val;
@@ -1103,8 +1149,8 @@
 	client_t	*client;
 	int			j;
 
-	style = G_FLOAT(OFS_PARM0);
-	val = G_STRING(OFS_PARM1);
+	style = G_FLOAT(pr, OFS_PARM0);
+	val = G_STRING(pr, OFS_PARM1);
 
 	// change the string in sv
 	sv.lightstyles[style] = val;
@@ -1122,21 +1168,21 @@
 		}
 }
 
-void PF_rint (void)
+void PF_rint (pr_t *pr)
 {
 	float	f;
-	f = G_FLOAT(OFS_PARM0);
-	G_FLOAT(OFS_RETURN) = Qrint(f);
+	f = G_FLOAT(pr, OFS_PARM0);
+	G_FLOAT(pr, OFS_RETURN) = Qrint(f);
 }
 
-void PF_floor (void)
+void PF_floor (pr_t *pr)
 {
-	G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0));
+	G_FLOAT(pr, OFS_RETURN) = floor(G_FLOAT(pr, OFS_PARM0));
 }
 
-void PF_ceil (void)
+void PF_ceil (pr_t *pr)
 {
-	G_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0));
+	G_FLOAT(pr, OFS_RETURN) = ceil(G_FLOAT(pr, OFS_PARM0));
 }
 
 /*
@@ -1144,13 +1190,13 @@
 PF_checkbottom
 =============
 */
-void PF_checkbottom (void)
+void PF_checkbottom (pr_t *pr)
 {
 	edict_t	*ent;
 
-	ent = G_EDICT(OFS_PARM0);
+	ent = G_EDICT(pr, OFS_PARM0);
 
-	G_FLOAT(OFS_RETURN) = SV_CheckBottom (ent);
+	G_FLOAT(pr, OFS_RETURN) = SV_CheckBottom (ent);
 }
 
 /*
@@ -1158,13 +1204,13 @@
 PF_pointcontents
 =============
 */
-void PF_pointcontents (void)
+void PF_pointcontents (pr_t *pr)
 {
 	float	*v;
 
-	v = G_VECTOR(OFS_PARM0);
+	v = G_VECTOR(pr, OFS_PARM0);
 
-	G_FLOAT(OFS_RETURN) = SV_PointContents (v);
+	G_FLOAT(pr, OFS_RETURN) = SV_PointContents (v);
 }
 
 /*
@@ -1174,24 +1220,24 @@
 entity nextent(entity)
 =============
 */
-void PF_nextent (void)
+void PF_nextent (pr_t *pr)
 {
 	int		i;
 	edict_t	*ent;
 
-	i = G_EDICTNUM(OFS_PARM0);
+	i = G_EDICTNUM(pr, OFS_PARM0);
 	while (1)
 	{
 		i++;
-		if (i == sv.num_edicts)
+		if (i == pr->num_edicts)
 		{
-			RETURN_EDICT(sv.edicts);
+			RETURN_EDICT(pr, pr->edicts);
 			return;
 		}
-		ent = EDICT_NUM(i);
+		ent = EDICT_NUM(pr, i);
 		if (!ent->free)
 		{
-			RETURN_EDICT(ent);
+			RETURN_EDICT(pr, ent);
 			return;
 		}
 	}
@@ -1206,7 +1252,7 @@
 =============
 */
 cvar_t	sv_aim = {"sv_aim", "0.93"};
-void PF_aim (void)
+void PF_aim (pr_t *pr)
 {
 	edict_t	*ent, *check, *bestent;
 	vec3_t	start, dir, end, bestdir;
@@ -1214,19 +1260,19 @@
 	trace_t	tr;
 	float	dist, bestdist;
 
-	ent = G_EDICT(OFS_PARM0);
+	ent = G_EDICT(pr, OFS_PARM0);
 
 	VectorCopy (ent->v.origin, start);
 	start[2] += 20;
 
 	// try sending a trace straight
-	VectorCopy (pr_global_struct->v_forward, dir);
+	VectorCopy (pr->global_struct->v_forward, dir);
 	VectorMA (start, 2048, dir, end);
 	tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent);
 	if (tr.ent && tr.ent->v.takedamage == DAMAGE_AIM
 	&& (!teamplay.value || ent->v.team <=0 || ent->v.team != tr.ent->v.team) )
 	{
-		VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN));
+		VectorCopy (pr->global_struct->v_forward, G_VECTOR(pr, OFS_RETURN));
 		return;
 	}
 
@@ -1236,8 +1282,8 @@
 	bestdist = sv_aim.value;
 	bestent = nil;
 
-	check = NEXT_EDICT(sv.edicts);
-	for (i=1 ; i<sv.num_edicts ; i++, check = NEXT_EDICT(check) )
+	check = NEXT_EDICT(pr, pr->edicts);
+	for (i=1 ; i<pr->num_edicts ; i++, check = NEXT_EDICT(pr, check) )
 	{
 		if (check->v.takedamage != DAMAGE_AIM)
 			continue;
@@ -1250,7 +1296,7 @@
 			+ 0.5*(check->v.mins[j] + check->v.maxs[j]);
 		VectorSubtract (end, start, dir);
 		VectorNormalize (dir);
-		dist = DotProduct (dir, pr_global_struct->v_forward);
+		dist = DotProduct (dir, pr->global_struct->v_forward);
 		if (dist < bestdist)
 			continue;	// to far to turn
 		tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent);
@@ -1264,15 +1310,15 @@
 	if (bestent)
 	{
 		VectorSubtract (bestent->v.origin, ent->v.origin, dir);
-		dist = DotProduct (dir, pr_global_struct->v_forward);
-		VectorScale (pr_global_struct->v_forward, dist, end);
+		dist = DotProduct (dir, pr->global_struct->v_forward);
+		VectorScale (pr->global_struct->v_forward, dist, end);
 		end[2] = dir[2];
 		VectorNormalize (end);
-		VectorCopy (end, G_VECTOR(OFS_RETURN));
+		VectorCopy (end, G_VECTOR(pr, OFS_RETURN));
 	}
 	else
 	{
-		VectorCopy (bestdir, G_VECTOR(OFS_RETURN));
+		VectorCopy (bestdir, G_VECTOR(pr, OFS_RETURN));
 	}
 }
 
@@ -1283,12 +1329,12 @@
 This was a major timewaster in progs, so it was converted to C
 ==============
 */
-void PF_changeyaw (void)
+void PF_changeyaw (pr_t *pr)
 {
 	edict_t		*ent;
 	float		ideal, current, move, speed;
 
-	ent = PROG_TO_EDICT(pr_global_struct->self);
+	ent = PROG_TO_EDICT(pr, pr->global_struct->self);
 	current = anglemod( ent->v.angles[1] );
 	ideal = ent->v.ideal_yaw;
 	speed = ent->v.yaw_speed;
@@ -1333,13 +1379,13 @@
 #define	MSG_ALL			2		// reliable to all
 #define	MSG_INIT		3		// write to the init string
 
-sizebuf_t *WriteDest (void)
+sizebuf_t *WriteDest (pr_t *pr)
 {
 	int		entnum;
 	int		dest;
 	edict_t	*ent;
 
-	dest = G_FLOAT(OFS_PARM0);
+	dest = G_FLOAT(pr, OFS_PARM0);
 	switch (dest)
 	{
 	case MSG_BROADCAST:
@@ -1346,10 +1392,10 @@
 		return &sv.datagram;
 
 	case MSG_ONE:
-		ent = PROG_TO_EDICT(pr_global_struct->msg_entity);
-		entnum = NUM_FOR_EDICT(ent);
+		ent = PROG_TO_EDICT(pr, pr->global_struct->msg_entity);
+		entnum = NUM_FOR_EDICT(pr, ent);
 		if (entnum < 1 || entnum > svs.maxclients)
-			PR_RunError ("WriteDest: not a client");
+			PR_RunError (pr, "WriteDest: not a client");
 		return &svs.clients[entnum-1].message;
 
 	case MSG_ALL:
@@ -1359,7 +1405,7 @@
 		return &sv.signon;
 
 	default:
-		PR_RunError ("WriteDest: bad destination");
+		PR_RunError (pr, "WriteDest: bad destination");
 		break;
 	}
 
@@ -1366,45 +1412,45 @@
 	return nil;
 }
 
-void PF_WriteByte (void)
+void PF_WriteByte (pr_t *pr)
 {
-	MSG_WriteByte (WriteDest(), G_FLOAT(OFS_PARM1));
+	MSG_WriteByte (WriteDest(pr), G_FLOAT(pr, OFS_PARM1));
 }
 
-void PF_WriteChar (void)
+void PF_WriteChar (pr_t *pr)
 {
-	MSG_WriteChar (WriteDest(), G_FLOAT(OFS_PARM1));
+	MSG_WriteChar (WriteDest(pr), G_FLOAT(pr, OFS_PARM1));
 }
 
-void PF_WriteShort (void)
+void PF_WriteShort (pr_t *pr)
 {
-	MSG_WriteShort (WriteDest(), G_FLOAT(OFS_PARM1));
+	MSG_WriteShort (WriteDest(pr), G_FLOAT(pr, OFS_PARM1));
 }
 
-void PF_WriteLong (void)
+void PF_WriteLong (pr_t *pr)
 {
-	MSG_WriteLong (WriteDest(), G_FLOAT(OFS_PARM1));
+	MSG_WriteLong (WriteDest(pr), G_FLOAT(pr, OFS_PARM1));
 }
 
-void PF_WriteAngle (void)
+void PF_WriteAngle (pr_t *pr)
 {
-	sv.protocol->MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1));
+	sv.protocol->MSG_WriteAngle (WriteDest(pr), G_FLOAT(pr, OFS_PARM1));
 }
 
-void PF_WriteCoord (void)
+void PF_WriteCoord (pr_t *pr)
 {
-	sv.protocol->MSG_WriteCoord (WriteDest(), G_FLOAT(OFS_PARM1));
+	sv.protocol->MSG_WriteCoord (WriteDest(pr), G_FLOAT(pr, OFS_PARM1));
 }
 
-void PF_WriteString (void)
+void PF_WriteString (pr_t *pr)
 {
-	MSG_WriteString (WriteDest(), G_STRING(OFS_PARM1));
+	MSG_WriteString (WriteDest(pr), G_STRING(pr, OFS_PARM1));
 }
 
 
-void PF_WriteEntity (void)
+void PF_WriteEntity (pr_t *pr)
 {
-	MSG_WriteShort (WriteDest(), G_EDICTNUM(OFS_PARM1));
+	MSG_WriteShort (WriteDest(pr), G_EDICTNUM(pr, OFS_PARM1));
 }
 
 //=============================================================================
@@ -1411,13 +1457,13 @@
 
 int SV_ModelIndex (char *name);
 
-void PF_makestatic (void)
+void PF_makestatic (pr_t *pr)
 {
 	edict_t	*ent;
 	int		i, model, frame, bits;
 
-	ent = G_EDICT(OFS_PARM0);
-	model = SV_ModelIndex(PR_Str(ent->v.model));
+	ent = G_EDICT(pr, OFS_PARM0);
+	model = SV_ModelIndex(PR_Str(pr, ent->v.model));
 	frame = ent->v.frame;
 	bits = 0;
 	if(model >= sv.protocol->limit_model)
@@ -1464,22 +1510,22 @@
 PF_setspawnparms
 ==============
 */
-void PF_setspawnparms (void)
+void PF_setspawnparms (pr_t *pr)
 {
 	edict_t	*ent;
 	int		i;
 	client_t	*client;
 
-	ent = G_EDICT(OFS_PARM0);
-	i = NUM_FOR_EDICT(ent);
+	ent = G_EDICT(pr, OFS_PARM0);
+	i = NUM_FOR_EDICT(pr, ent);
 	if (i < 1 || i > svs.maxclients)
-		PR_RunError ("Entity is not a client");
+		PR_RunError (pr, "Entity is not a client");
 
 	// copy spawn parms out of the client_t
 	client = svs.clients + (i-1);
 
 	for (i=0 ; i< Nparms ; i++)
-		(&pr_global_struct->parm1)[i] = client->spawn_parms[i];
+		(&pr->global_struct->parm1)[i] = client->spawn_parms[i];
 }
 
 /*
@@ -1487,7 +1533,7 @@
 PF_changelevel
 ==============
 */
-void PF_changelevel (void)
+void PF_changelevel (pr_t *pr)
 {
 	char	*s;
 
@@ -1496,13 +1542,13 @@
 		return;
 	svs.changelevel_issued = true;
 
-	s = G_STRING(OFS_PARM0);
+	s = G_STRING(pr, OFS_PARM0);
 	Cbuf_AddText (va("changelevel %s\n",s));
 }
 
-void PF_Fixme (void)
+void PF_Fixme (pr_t *pr)
 {
-	PR_RunError ("unimplemented builtin");
+	PR_RunError (pr, "unimplemented builtin");
 }
 
 static const char *exts[] = {
@@ -1509,29 +1555,30 @@
 	"DP_EF_NODRAW",
 };
 
-static void PF_checkextension (void)
+static void PF_checkextension (pr_t *pr)
 {
-	const char *ext = G_STRING(OFS_PARM0);
+	const char *ext = G_STRING(pr, OFS_PARM0);
 	int i;
 
-	G_FLOAT(OFS_RETURN) = false;
+	G_FLOAT(pr, OFS_RETURN) = false;
 	Con_DPrintf("checking extension %s\n", ext);
 	for(i = 0; i < nelem(exts); i++){
 		if(strcmp(ext, exts[i]) == 0){
-			G_FLOAT(OFS_RETURN) = true;
+			G_FLOAT(pr, OFS_RETURN) = true;
 			break;
 		}
 	}
 }
 
-static void PF_clientstat (void)
+static void PF_clientstat (pr_t *pr)
 {
 	// FIXME
 	// Arcane Dimensions will fall off if this one isn't defined
 	// even though it does *not* check for the extension
+	USED(pr);
 }
 
-static const builtin_t pr_builtin[] =
+static const builtin_t pr_sv_builtins[] =
 {
 PF_Fixme,
 PF_makevectors,	// void(entity e)	makevectors 		= #1;
@@ -1603,7 +1650,7 @@
 PF_Fixme,
 PF_Fixme,
 
-SV_MoveToGoal,
+PF_MoveToGoal,
 PF_precache_file,
 PF_makestatic,
 
@@ -1625,6 +1672,9 @@
 [232] = PF_clientstat,
 };
 
-const builtin_t *pr_builtins = pr_builtin;
-const int pr_numbuiltins = nelem(pr_builtin);
-
+void
+PR_SetBuiltinsSV(pr_t *pr)
+{
+	pr->builtins = pr_sv_builtins;
+	pr->numbuiltins = nelem(pr_sv_builtins);
+}
--- a/pr_comp.h
+++ b/pr_comp.h
@@ -3,22 +3,45 @@
 typedef int	func_t;
 typedef int	string_t;
 
-typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer} etype_t;
+typedef enum {
+	ev_void,
+	ev_string,
+	ev_float,
+	ev_vector,
+	ev_entity,
+	ev_field,
+	ev_function,
+	ev_pointer,
+}etype_t;
 
+enum {
+	OFS_NULL,
+	OFS_RETURN,
+	OFS_PARM0 = 4, // leave 3 ofs for each parm to hold vectors
+	OFS_PARM1 = 7,
+	OFS_PARM2 = 10,
+	OFS_PARM3 = 13,
+	OFS_PARM4 = 16,
+	OFS_PARM5 = 19,
+	OFS_PARM6 = 22,
+	OFS_PARM7 = 25,
+	RESERVED_OFS = 28,
 
-#define	OFS_NULL		0
-#define	OFS_RETURN		1
-#define	OFS_PARM0		4		// leave 3 ofs for each parm to hold vectors
-#define	OFS_PARM1		7
-#define	OFS_PARM2		10
-#define	OFS_PARM3		13
-#define	OFS_PARM4		16
-#define	OFS_PARM5		19
-#define	OFS_PARM6		22
-#define	OFS_PARM7		25
-#define	RESERVED_OFS	28
+	PROG_VERSION = 6,
 
+	MAX_PARMS = 8,
 
+	DEF_SAVEGLOBAL = 1<<15,
+
+	PR_LUMP_STATEMENTS = 0, // statement 0 is an error
+	PR_LUMP_GLOBALDEFS,
+	PR_LUMP_FIELDDEFS,
+	PR_LUMP_FUNCTIONS, // function 0 is an empty one
+	PR_LUMP_STRINGS, // first string is a null string
+	PR_LUMP_GLOBALS,
+	NUM_PR_LUMPS,
+};
+
 enum {
 	OP_DONE,
 	OP_MUL_F,
@@ -97,74 +120,38 @@
 	OP_BITOR
 };
 
-#ifdef __plan9__
-#pragma pack on
-#else
-#pragma pack(1)
-#endif
+typedef struct {
+	u16int op;
+	s16int a,b,c;
+}dstatement_t;
 
-typedef struct statement_s
-{
-	unsigned short	op;
-	short	a,b,c;
-} dstatement_t;
+typedef struct {
+	// if DEF_SAVEGLOBGAL bit is set
+	// the variable needs to be saved in savegames
+	u16int type;
+	u16int ofs;
+	int s_name;
+}ddef_t;
 
-typedef struct
-{
-	unsigned short	type;		// if DEF_SAVEGLOBGAL bit is set
-								// the variable needs to be saved in savegames
-	unsigned short	ofs;
-	int			s_name;
-} ddef_t;
-#define	DEF_SAVEGLOBAL	(1<<15)
+typedef struct {
+	int first_statement; // negative numbers are builtins
+	int parm_start;
+	int locals; // total ints of parms + locals
+	int profile; // runtime
+	int s_name;
+	int s_file; // source file defined in
+	int numparms;
+	byte parm_size[MAX_PARMS];
+}dfunction_t;
 
-#define	MAX_PARMS	8
+typedef struct {
+	int off;
+	int num;
+}dproglump_t;
 
-typedef struct
-{
-	int		first_statement;	// negative numbers are builtins
-	int		parm_start;
-	int		locals;				// total ints of parms + locals
-
-	int		profile;		// runtime
-
-	int		s_name;
-	int		s_file;			// source file defined in
-
-	int		numparms;
-	byte	parm_size[MAX_PARMS];
-} dfunction_t;
-
-
-#define	PROG_VERSION	6
-typedef struct
-{
-	int		version;
-	int		crc;			// check of header file
-
-	int		ofs_statements;
-	int		numstatements;	// statement 0 is an error
-
-	int		ofs_globaldefs;
-	int		numglobaldefs;
-
-	int		ofs_fielddefs;
-	int		numfielddefs;
-
-	int		ofs_functions;
-	int		numfunctions;	// function 0 is an empty
-
-	int		ofs_strings;
-	int		numstrings;		// first string is a null string
-
-	int		ofs_globals;
-	int		numglobals;
-
-	int		entityfields;
-} dprograms_t;
-
-#ifdef __plan9__
-#pragma pack off
-#else
-#pragma pack(0)
-#endif
+typedef struct {
+	int version;
+	int crc; // check of header file
+	dproglump_t lumps[NUM_PR_LUMPS];
+	int entityfields;
+}dprograms_t;
--- a/pr_edict.c
+++ b/pr_edict.c
@@ -1,17 +1,6 @@
 #include "quakedef.h"
 
-dprograms_t		*progs;
-dfunction_t		*pr_functions;
-static char		*pr_strings;
-static int		pr_strings_size;
-ddef_t			*pr_fielddefs;
-ddef_t			*pr_globaldefs;
-dstatement_t	*pr_statements;
-globalvars_t	*pr_global_struct;
-float			*pr_globals;			// same as pr_global_struct
-int				pr_edict_size;	// in bytes
-
-int type_size[8] = {
+const int type_size[8] = {
 	[ev_void] = 1,
 	[ev_string] = sizeof(string_t)/4,
 	[ev_float] = 1,
@@ -22,9 +11,11 @@
 	[ev_pointer] = sizeof(void *)/4,
 };
 
-ddef_t *ED_FieldAtOfs (int ofs);
-bool	ED_ParseEpair (void *base, ddef_t *key, char *s);
+ddef_t *ED_FieldAtOfs (pr_t *pr, int ofs);
+bool	ED_ParseEpair (pr_t *pr, void *base, ddef_t *key, char *s);
 
+void PR_SetBuiltinsSV(pr_t *pr);
+
 cvar_t	nomonsters = {"nomonsters", "0"};
 
 static cvar_t scratch1 = {"scratch1", "0"};
@@ -39,9 +30,15 @@
 static cvar_t savedgamecfg = {"savedgamecfg", "0", true};
 static cvar_t pr_checkextension = {"pr_checkextension", "1"};
 
-#define	MAX_FIELD_LEN	64
-#define GEFV_CACHESIZE	2
+enum {
+	MAX_PRSTR = 1024,
+	MAX_PRTEMPSTR = 1024,
+	PRTEMPSTR_SIZE = 1024,
 
+	MAX_FIELD_LEN = 64,
+	GEFV_CACHESIZE = 2,
+};
+
 typedef struct {
 	ddef_t	*pcache;
 	char	field[MAX_FIELD_LEN];
@@ -49,65 +46,55 @@
 
 static gefv_cache	gefvCache[GEFV_CACHESIZE] = {{nil, ""}, {nil, ""}};
 
-#define MAX_PRSTR 1024
-static char **prstr;
-static int max_prstr;
-static int num_prstr;
-
-#define MAX_PRTEMPSTR 1024
-#define PRTEMPSTR_SIZE 1024
-static char *prtempstr;
-static int num_prtempstr;
-
 char *
-PR_StrTmp(void)
+PR_StrTmp(pr_t *pr)
 {
-	return &prtempstr[PRTEMPSTR_SIZE * (num_prtempstr++ & MAX_PRTEMPSTR)];
+	return &pr->tempstr[PRTEMPSTR_SIZE * (pr->num_tempstr++ & MAX_PRTEMPSTR)];
 }
 
 int
-PR_CopyStrTmp(char *s)
+PR_CopyStrTmp(pr_t *pr, char *s)
 {
-	char *t = PR_StrTmp();
+	char *t = PR_StrTmp(pr);
 	snprint(t, PRTEMPSTR_SIZE, "%s", s);
-	return PR_SetStr(t);
+	return PR_SetStr(pr, t);
 }
 
 int
-PR_StrSlot(void)
+PR_StrSlot(pr_t *pr)
 {
-	if(num_prstr >= max_prstr){
-		max_prstr *= 2;
-		prstr = realloc(prstr, max_prstr*sizeof(*prstr));
+	if(pr->num_str >= pr->max_str){
+		pr->str = Hunk_Double(pr->str);
+		pr->max_str *= 2;
 	}
-	return num_prstr++;
+	return pr->num_str++;
 }
 
 int
-PR_SetStr(char *s)
+PR_SetStr(pr_t *pr, char *s)
 {
 	int i;
 
 	if(s == nil)
 		return 0;
-	if(s >= pr_strings && s < pr_strings+pr_strings_size-1)
-		return s - pr_strings;
-	for(i = 0; i < num_prstr; i++){
-		if(s == prstr[i])
+	if(s >= pr->strings && s < pr->strings+pr->strings_size-1)
+		return s - pr->strings;
+	for(i = 0; i < pr->num_str; i++){
+		if(s == pr->str[i])
 			return -1-i;
 	}
-	i = PR_StrSlot();
-	prstr[i] = s;
+	i = PR_StrSlot(pr);
+	pr->str[i] = s;
 	return -1-i;
 }
 
 char *
-PR_Str(int i)
+PR_Str(pr_t *pr, int i)
 {
-	if(i >= 0 && i < pr_strings_size)
-		return pr_strings+i;
-	if(i < 0 && i >= -num_prstr && prstr[-1-i] != nil)
-		return prstr[-1-i];
+	if(i >= 0 && i < pr->strings_size)
+		return pr->strings+i;
+	if(i < 0 && i >= -pr->num_str && pr->str[-1-i] != nil)
+		return pr->str[-1-i];
 	Host_Error("PR_Str: invalid offset %d", i);
 }
 
@@ -118,9 +105,10 @@
 Sets everything to nil
 =================
 */
-void ED_ClearEdict (edict_t *e)
+void
+ED_ClearEdict(pr_t *pr, edict_t *e)
 {
-	memset (&e->v, 0, progs->entityfields * 4);
+	memset(&e->v, 0, pr->entityfields * 4);
 	e->free = false;
 }
 
@@ -135,29 +123,28 @@
 angles and bad trails.
 =================
 */
-edict_t *ED_Alloc (void)
+edict_t *
+ED_Alloc(pr_t *pr)
 {
 	int			i;
 	edict_t		*e;
 
-	for ( i=svs.maxclients+1 ; i<sv.num_edicts ; i++)
-	{
-		e = EDICT_NUM(i);
+	for(i = svs.maxclients+1; i<pr->num_edicts ; i++){
+		e = EDICT_NUM(pr, i);
 		// the first couple seconds of server time can involve a lot of
 		// freeing and allocating, so relax the replacement policy
-		if (e->free && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) )
-		{
-			ED_ClearEdict (e);
+		if(e->free && (e->freetime < 2 || sv.time - e->freetime > 0.5)){
+			ED_ClearEdict(pr, e);
 			return e;
 		}
 	}
 
-	if (i == MAX_EDICTS)
-		fatal ("ED_Alloc: no free edicts");
+	if(i == pr->max_edicts)
+		fatal("ED_Alloc: no free edicts");
 
-	sv.num_edicts++;
-	e = EDICT_NUM(i);
-	ED_ClearEdict (e);
+	pr->num_edicts++;
+	e = EDICT_NUM(pr, i);
+	ED_ClearEdict(pr, e);
 
 	return e;
 }
@@ -196,14 +183,14 @@
 ED_GlobalAtOfs
 ============
 */
-ddef_t *ED_GlobalAtOfs (int ofs)
+ddef_t *ED_GlobalAtOfs (pr_t *pr, int ofs)
 {
 	ddef_t		*def;
 	int			i;
 
-	for (i=0 ; i<progs->numglobaldefs ; i++)
+	for (i=0 ; i<pr->numglobaldefs ; i++)
 	{
-		def = &pr_globaldefs[i];
+		def = &pr->globaldefs[i];
 		if (def->ofs == ofs)
 			return def;
 	}
@@ -215,14 +202,14 @@
 ED_FieldAtOfs
 ============
 */
-ddef_t *ED_FieldAtOfs (int ofs)
+ddef_t *ED_FieldAtOfs (pr_t *pr, int ofs)
 {
 	ddef_t		*def;
 	int			i;
 
-	for (i=0 ; i<progs->numfielddefs ; i++)
+	for (i=0 ; i<pr->numfielddefs ; i++)
 	{
-		def = &pr_fielddefs[i];
+		def = &pr->fielddefs[i];
 		if (def->ofs == ofs)
 			return def;
 	}
@@ -234,15 +221,15 @@
 ED_FindField
 ============
 */
-ddef_t *ED_FindField (char *name)
+ddef_t *ED_FindField (pr_t *pr, char *name)
 {
 	ddef_t		*def;
 	int			i;
 
-	for (i=0 ; i<progs->numfielddefs ; i++)
+	for (i=0 ; i<pr->numfielddefs ; i++)
 	{
-		def = &pr_fielddefs[i];
-		if (!strcmp(PR_Str(def->s_name),name) )
+		def = &pr->fielddefs[i];
+		if (!strcmp(PR_Str(pr, def->s_name),name) )
 			return def;
 	}
 	return nil;
@@ -254,15 +241,15 @@
 ED_FindGlobal
 ============
 */
-ddef_t *ED_FindGlobal (char *name)
+ddef_t *ED_FindGlobal (pr_t *pr, char *name)
 {
 	ddef_t		*def;
 	int			i;
 
-	for (i=0 ; i<progs->numglobaldefs ; i++)
+	for (i=0 ; i<pr->numglobaldefs ; i++)
 	{
-		def = &pr_globaldefs[i];
-		if (!strcmp(PR_Str(def->s_name),name) )
+		def = &pr->globaldefs[i];
+		if (!strcmp(PR_Str(pr, def->s_name),name) )
 			return def;
 	}
 	return nil;
@@ -274,15 +261,15 @@
 ED_FindFunction
 ============
 */
-dfunction_t *ED_FindFunction (char *name)
+dfunction_t *ED_FindFunction (pr_t *pr, char *name)
 {
 	dfunction_t		*func;
 	int				i;
 
-	for (i=0 ; i<progs->numfunctions ; i++)
+	for (i=0 ; i<pr->numfunctions ; i++)
 	{
-		func = &pr_functions[i];
-		if (!strcmp(PR_Str(func->s_name),name) )
+		func = &pr->functions[i];
+		if (!strcmp(PR_Str(pr, func->s_name),name) )
 			return func;
 	}
 	return nil;
@@ -289,7 +276,7 @@
 }
 
 
-eval_t *GetEdictFieldValue(edict_t *ed, char *field)
+eval_t *GetEdictFieldValue(pr_t *pr, edict_t *ed, char *field)
 {
 	ddef_t			*def;
 	int				i;
@@ -304,7 +291,7 @@
 		}
 	}
 
-	def = ED_FindField (field);
+	def = ED_FindField (pr, field);
 
 	if (strlen(field) < MAX_FIELD_LEN)
 	{
@@ -328,7 +315,7 @@
 Returns a string describing *data in a type specific manner
 =============
 */
-char *PR_ValueString (etype_t type, eval_t *val)
+char *PR_ValueString (pr_t *pr, etype_t type, eval_t *val)
 {
 	static char	line[256];
 	ddef_t		*def;
@@ -339,18 +326,18 @@
 	switch (type)
 	{
 	case ev_string:
-		sprint (line, "%s", PR_Str(val->string));
+		sprint (line, "%s", PR_Str(pr, val->string));
 		break;
 	case ev_entity:
-		sprint (line, "entity %d", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) );
+		sprint (line, "entity %d", NUM_FOR_EDICT(pr, PROG_TO_EDICT(pr, val->edict)) );
 		break;
 	case ev_function:
-		f = pr_functions + val->function;
-		sprint (line, "%s()", PR_Str(f->s_name));
+		f = pr->functions + val->function;
+		sprint (line, "%s()", PR_Str(pr, f->s_name));
 		break;
 	case ev_field:
-		def = ED_FieldAtOfs ( val->_int );
-		sprint (line, ".%s", PR_Str(def->s_name));
+		def = ED_FieldAtOfs (pr, val->_int );
+		sprint (line, ".%s", PR_Str(pr, def->s_name));
 		break;
 	case ev_void:
 		sprint (line, "void");
@@ -380,7 +367,7 @@
 Easier to parse than PR_ValueString
 =============
 */
-char *PR_UglyValueString (etype_t type, eval_t *val)
+char *PR_UglyValueString (pr_t *pr, etype_t type, eval_t *val)
 {
 	static char	line[256];
 	ddef_t		*def;
@@ -391,18 +378,18 @@
 	switch (type)
 	{
 	case ev_string:
-		sprint (line, "%s", PR_Str(val->string));
+		sprint (line, "%s", PR_Str(pr, val->string));
 		break;
 	case ev_entity:
-		sprint (line, "%d", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)));
+		sprint (line, "%d", NUM_FOR_EDICT(pr, PROG_TO_EDICT(pr, val->edict)));
 		break;
 	case ev_function:
-		f = pr_functions + val->function;
-		sprint (line, "%s", PR_Str(f->s_name));
+		f = pr->functions + val->function;
+		sprint (line, "%s", PR_Str(pr, f->s_name));
 		break;
 	case ev_field:
-		def = ED_FieldAtOfs ( val->_int );
-		sprint (line, "%s", PR_Str(def->s_name));
+		def = ED_FieldAtOfs (pr, val->_int );
+		sprint (line, "%s", PR_Str(pr, def->s_name));
 		break;
 	case ev_void:
 		sprint (line, "void");
@@ -429,7 +416,7 @@
 padded to 20 field width
 ============
 */
-char *PR_GlobalString (int ofs)
+char *PR_GlobalString (pr_t *pr, int ofs)
 {
 	char	*s;
 	int		i;
@@ -437,14 +424,14 @@
 	void	*val;
 	static char	line[128];
 
-	val = (void *)&pr_globals[ofs];
-	def = ED_GlobalAtOfs(ofs);
+	val = (void *)&pr->globals[ofs];
+	def = ED_GlobalAtOfs(pr, ofs);
 	if (!def)
 		sprint (line,"%d(?)", ofs);
 	else
 	{
-		s = PR_ValueString (def->type, val);
-		sprint (line,"%d(%s)%s", ofs, PR_Str(def->s_name), s);
+		s = PR_ValueString(pr, def->type, val);
+		sprint(line,"%d(%s)%s", ofs, PR_Str(pr, def->s_name), s);
 	}
 
 	i = strlen(line);
@@ -455,17 +442,17 @@
 	return line;
 }
 
-char *PR_GlobalStringNoContents (int ofs)
+char *PR_GlobalStringNoContents (pr_t *pr, int ofs)
 {
 	int		i;
 	ddef_t	*def;
 	static char	line[128];
 
-	def = ED_GlobalAtOfs(ofs);
+	def = ED_GlobalAtOfs(pr, ofs);
 	if (!def)
 		sprint (line,"%d(?)", ofs);
 	else
-		sprint (line,"%d(%s)", ofs, PR_Str(def->s_name));
+		sprint (line,"%d(%s)", ofs, PR_Str(pr, def->s_name));
 
 	i = strlen(line);
 	for ( ; i<20 ; i++)
@@ -483,7 +470,7 @@
 For debugging
 =============
 */
-void ED_Print (edict_t *ed)
+void ED_Print (pr_t *pr, edict_t *ed)
 {
 	int		l;
 	ddef_t	*d;
@@ -498,11 +485,11 @@
 		return;
 	}
 
-	Con_Printf("\nEDICT %d:\n", NUM_FOR_EDICT(ed));
-	for (i=1 ; i<progs->numfielddefs ; i++)
+	Con_Printf("\nEDICT %d:\n", NUM_FOR_EDICT(pr, ed));
+	for (i=1 ; i<pr->numfielddefs ; i++)
 	{
-		d = &pr_fielddefs[i];
-		name = PR_Str(d->s_name);
+		d = &pr->fielddefs[i];
+		name = PR_Str(pr, d->s_name);
 		if (name[strlen(name)-2] == '_')
 			continue;	// skip _x, _y, _z vars
 
@@ -522,13 +509,13 @@
 		while (l++ < 15)
 			Con_Printf (" ");
 
-		Con_Printf ("%s\n", PR_ValueString(d->type, (eval_t *)v));
+		Con_Printf ("%s\n", PR_ValueString(pr, d->type, (eval_t *)v));
 	}
 }
 
-void ED_PrintNum (int ent)
+void ED_PrintNum (pr_t *pr, int ent)
 {
-	ED_Print (EDICT_NUM(ent));
+	ED_Print (pr, EDICT_NUM(pr, ent));
 }
 
 /*
@@ -542,9 +529,9 @@
 {
 	int		i;
 
-	Con_Printf ("%d entities\n", sv.num_edicts);
-	for (i=0 ; i<sv.num_edicts ; i++)
-		ED_PrintNum (i);
+	Con_Printf ("%d entities\n", sv.pr->num_edicts);
+	for (i=0 ; i<sv.pr->num_edicts ; i++)
+		ED_PrintNum(sv.pr, i);
 }
 
 /*
@@ -559,12 +546,12 @@
 	int		i;
 
 	i = atoi(Cmd_Argv(1));
-	if (i >= sv.num_edicts)
+	if (i >= sv.pr->num_edicts)
 	{
 		Con_Printf("Bad edict number\n");
 		return;
 	}
-	ED_PrintNum (i);
+	ED_PrintNum (sv.pr, i);
 }
 
 /*
@@ -581,9 +568,9 @@
 	int		active, models, solid, step;
 
 	active = models = solid = step = 0;
-	for (i=0 ; i<sv.num_edicts ; i++)
+	for (i=0 ; i<sv.pr->num_edicts ; i++)
 	{
-		ent = EDICT_NUM(i);
+		ent = EDICT_NUM(sv.pr, i);
 		if (ent->free)
 			continue;
 		active++;
@@ -595,7 +582,7 @@
 			step++;
 	}
 
-	Con_Printf ("num_edicts:%3d\n", sv.num_edicts);
+	Con_Printf ("num_edicts:%3d\n", sv.pr->num_edicts);
 	Con_Printf ("active    :%3d\n", active);
 	Con_Printf ("view      :%3d\n", models);
 	Con_Printf ("touch     :%3d\n", solid);
@@ -617,7 +604,7 @@
 ED_ParseGlobals
 =============
 */
-void ED_ParseGlobals (char *data)
+void ED_ParseGlobals (pr_t *pr, char *data)
 {
 	char	keyname[64];
 	ddef_t	*key;
@@ -641,7 +628,7 @@
 		if (com_token[0] == '}')
 			fatal ("ED_ParseGlobals: closing brace without data");
 
-		key = ED_FindGlobal (keyname);
+		key = ED_FindGlobal (pr, keyname);
 		if (!key)
 		{
 			Con_Printf ("ED_ParseGlobals: '%s' is not a global\n", keyname);
@@ -648,7 +635,7 @@
 			continue;
 		}
 
-		if (!ED_ParseEpair ((void *)pr_globals, key, com_token))
+		if (!ED_ParseEpair (pr, (void *)pr->globals, key, com_token))
 			Host_Error ("ED_ParseGlobals: parse error");
 	}
 }
@@ -661,15 +648,15 @@
 ED_NewString
 =============
 */
-string_t ED_NewString (char *string)
+string_t ED_NewString (pr_t *pr, char *string)
 {
 	char	*new, *new_p;
 	int		i,l, slot;
 
 	l = strlen(string) + 1;
-	new = Hunk_Alloc (l);
+	new = Hunk_Alloc(l);
 	new_p = new;
-	slot = PR_StrSlot();
+	slot = PR_StrSlot(pr);
 
 	for (i=0 ; i< l ; i++)
 	{
@@ -684,7 +671,7 @@
 		else
 			*new_p++ = string[i];
 	}
-	prstr[slot] = new;
+	pr->str[slot] = new;
 
 	return -1-slot;
 }
@@ -698,7 +685,7 @@
 returns false if error
 =============
 */
-bool	ED_ParseEpair (void *base, ddef_t *key, char *s)
+bool	ED_ParseEpair (pr_t *pr, void *base, ddef_t *key, char *s)
 {
 	ddef_t	*def;
 	char	*v, *w;
@@ -711,7 +698,7 @@
 	switch (key->type & ~DEF_SAVEGLOBAL)
 	{
 	case ev_string:
-		*(string_t *)d = ED_NewString(s);
+		*(string_t *)d = ED_NewString(pr, s);
 		break;
 
 	case ev_float:
@@ -733,27 +720,27 @@
 		break;
 
 	case ev_entity:
-		*(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s)));
+		*(int *)d = EDICT_TO_PROG(pr, EDICT_NUM(pr, atoi (s)));
 		break;
 
 	case ev_field:
-		def = ED_FindField (s);
+		def = ED_FindField (pr, s);
 		if (!def)
 		{
 			Con_Printf ("Can't find field %s\n", s);
 			return false;
 		}
-		*(int *)d = G_INT(def->ofs);
+		*(int *)d = G_INT(pr, def->ofs);
 		break;
 
 	case ev_function:
-		func = ED_FindFunction (s);
+		func = ED_FindFunction (pr, s);
 		if (!func)
 		{
 			Con_Printf ("Can't find function %s\n", s);
 			return false;
 		}
-		*(func_t *)d = func - pr_functions;
+		*(func_t *)d = func - pr->functions;
 		break;
 
 	default:
@@ -771,7 +758,7 @@
 Used for initial level load and for savegames.
 ====================
 */
-char *ED_ParseEdict (char *data, edict_t *ent)
+char *ED_ParseEdict (pr_t *pr, char *data, edict_t *ent)
 {
 	ddef_t		*key;
 	bool	anglehack;
@@ -782,8 +769,8 @@
 	init = false;
 
 	// clear it
-	if (ent != sv.edicts)	// hack
-		memset (&ent->v, 0, progs->entityfields * 4);
+	if (ent != pr->edicts)	// hack
+		memset (&ent->v, 0, pr->entityfields * 4);
 
 	// go through all the dictionary pairs
 	while (1)
@@ -834,7 +821,7 @@
 		if (keyname[0] == '_')
 			continue;
 
-		key = ED_FindField (keyname);
+		key = ED_FindField (pr, keyname);
 		if (!key)
 		{
 			if(strcmp(keyname, "alpha") == 0)
@@ -850,7 +837,7 @@
 			sprint (com_token, "0 %s 0", temp);
 		}
 
-		if (!ED_ParseEpair ((void *)&ent->v, key, com_token))
+		if (!ED_ParseEpair (pr, (void *)&ent->v, key, com_token))
 			Host_Error ("ED_ParseEdict: parse error");
 	}
 
@@ -876,7 +863,7 @@
 to call ED_CallSpawnFunctions () to let the objects initialize themselves.
 ================
 */
-void ED_LoadFromFile (char *data)
+void ED_LoadFromFile (pr_t *pr, char *data)
 {
 	edict_t		*ent;
 	int			inhibit;
@@ -884,7 +871,7 @@
 
 	ent = nil;
 	inhibit = 0;
-	pr_global_struct->time = sv.time;
+	pr->global_struct->time = sv.time;
 	// parse ents
 	while (1)
 	{
@@ -893,13 +880,13 @@
 		if (!data)
 			break;
 		if (com_token[0] != '{')
-			fatal ("ED_LoadFromFile: found %s when expecting {",com_token);
+			fatal ("ED_LoadFromFile: found %s when expecting {", com_token);
 
 		if (!ent)
-			ent = EDICT_NUM(0);
+			ent = EDICT_NUM(pr, 0);
 		else
-			ent = ED_Alloc ();
-		data = ED_ParseEdict (data, ent);
+			ent = ED_Alloc(pr);
+		data = ED_ParseEdict (pr, data, ent);
 
 		// remove things from different skill levels or deathmatch
 		if (deathmatch.value)
@@ -924,23 +911,23 @@
 		if (!ent->v.classname)
 		{
 			Con_Printf ("No classname for:\n");
-			ED_Print (ent);
+			ED_Print (pr, ent);
 			ED_Free (ent);
 			continue;
 		}
 
 		// look for the spawn function
-		func = ED_FindFunction ( PR_Str(ent->v.classname) );
+		func = ED_FindFunction (pr,  PR_Str(pr, ent->v.classname) );
 		if (!func)
 		{
 			Con_Printf ("No spawn function for:\n");
-			ED_Print (ent);
+			ED_Print (pr, ent);
 			ED_Free (ent);
 			continue;
 		}
 
-		pr_global_struct->self = EDICT_TO_PROG(ent);
-		PR_ExecuteProgram (func - pr_functions);
+		pr->global_struct->self = EDICT_TO_PROG(pr, ent);
+		PR_ExecuteProgram (pr, func - pr->functions);
 	}
 
 	Con_DPrintf("%d entities inhibited\n", inhibit);
@@ -958,7 +945,7 @@
 };
 
 static void
-PR_FieldDefs(ddef_t *in)
+PR_FieldDefs(pr_t *pr, byte *in, int num)
 {
 	extra_field_t *e;
 	ddef_t *d;
@@ -965,41 +952,34 @@
 	int i, n;
 
 	// allocate to fit *all* extra fields, if needed, and copy over
-	n = progs->numfielddefs;
+	pr->numfielddefs = n = num;
 	for(i = 0; i < nelem(extra_fields); i++)
 		n += extra_fields[i].type == ev_vector ? 4 : 1;
-	d = malloc(n * sizeof(*in));
-	memmove(d, in, progs->numfielddefs * sizeof(*in));
-	free(pr_fielddefs);
-	pr_fielddefs = d;
-
-	// convert endianess of fields that loaded from disk
-	for(i = 0 ; i < progs->numfielddefs; i++, d++){
-		d->type = LittleShort(d->type);
-		if(d->type & DEF_SAVEGLOBAL)
-			fatal("PR_FieldDefs: d->type & DEF_SAVEGLOBAL");
-		d->ofs = LittleShort(d->ofs);
-		d->s_name = LittleLong(d->s_name);
+	pr->fielddefs = d = Hunk_Alloc(n * sizeof(*d));
+	for(i = 0; i < pr->numfielddefs; i++){
+		d[i].type = le16u(in);
+		d[i].ofs = le16u(in);
+		d[i].s_name = le32(in);
 	}
 
 	// add missing extra fields
-	d = &pr_fielddefs[progs->numfielddefs];
+	d += pr->numfielddefs;
 	for(i = 0, e = extra_fields; i < nelem(extra_fields); i++, e++){
-		if(ED_FindField(e->name) != nil)
+		if(ED_FindField(pr, e->name) != nil)
 			continue;
 		d->type = e->type;
-		d->s_name = ED_NewString(e->name);
-		d->ofs = progs->numfielddefs++;
+		d->s_name = ED_NewString(pr, e->name);
+		d->ofs = pr->numfielddefs++;
 		d++;
 		if(e->type == ev_vector){
 			for(n = 0; n < 3; n++){
 				d->type = ev_float;
-				d->s_name = ED_NewString(va("%s_%c", e->name, 'x'+n));
-				d->ofs = progs->numfielddefs++;
+				d->s_name = ED_NewString(pr, va("%s_%c", e->name, 'x'+n));
+				d->ofs = pr->numfielddefs++;
 				d++;
 			}
 		}
-		progs->entityfields += type_size[e->type];
+		pr->entityfields += type_size[e->type];
 	}
 }
 
@@ -1008,85 +988,129 @@
 PR_LoadProgs
 ===============
 */
-void PR_LoadProgs (void)
+pr_t *PR_LoadProgs (char *name)
 {
-	int i, n;
+	int i, n, version, hdrcrc;
+	byte *in, *in0;
+	dfunction_t *f;
+	dproglump_t *pl;
+	pr_t *pr;
+	static const int elsz[NUM_PR_LUMPS] = {
+		[PR_LUMP_STATEMENTS] = 4*2,
+		[PR_LUMP_GLOBALDEFS] = 2*2+1*4,
+		[PR_LUMP_FIELDDEFS] = 2*2+1*4,
+		[PR_LUMP_FUNCTIONS] = 7*4+MAX_PARMS*1,
+		[PR_LUMP_STRINGS] = 1,
+		[PR_LUMP_GLOBALS] = 4,
+	};
+	dproglump_t lumps[NUM_PR_LUMPS];
 
 	// flush the non-C variable lookup cache
 	for (i=0 ; i<GEFV_CACHESIZE ; i++)
 		gefvCache[i].field[0] = 0;
 
-	if(prstr == nil){
-		max_prstr = MAX_PRSTR;
-		prstr = malloc(max_prstr*sizeof(*prstr));
-	}
-	if(prtempstr == nil)
-		prtempstr = malloc(MAX_PRTEMPSTR*PRTEMPSTR_SIZE);
+	pr = Hunk_Alloc(sizeof(*pr));
 
-	memset(prstr, 0, max_prstr*sizeof(*prstr));
-	memset(prtempstr, 0, MAX_PRTEMPSTR*PRTEMPSTR_SIZE);
-	num_prstr = 0;
-	num_prtempstr = 0;
-	PR_SetStr("");
+	pr->max_str = MAX_PRSTR;
+	pr->str = Hunk_Alloc(pr->max_str*sizeof(*pr->str));
+	pr->num_str = 0;
+	pr->tempstr = Hunk_Alloc(MAX_PRTEMPSTR*PRTEMPSTR_SIZE);
+	pr->num_tempstr = 0;
+	PR_SetStr(pr, "");
 
-	initcrc();
-
-	progs = loadhunklmp("progs.dat", &n);
-	if(progs == nil)
+	in = in0 = loadhunklmp(name, &n);
+	if(in == nil){
+err:
 		fatal("PR_LoadProgs: %s", lerr());
-	Con_DPrintf("Programs occupy %dK.\n", n/1024);
+	}
+	if(n < 15*4){
+		werrstr("too small");
+		goto err;
+	}
 
-	for (i=0 ; i<n ; i++)
-		crc (((byte *)progs)[i]);
+	version = le32(in);
+	if(version != PROG_VERSION){
+		werrstr("wrong version number (%d should be %d)", version, PROG_VERSION);
+		goto err;
+	}
 
-	// byte swap the header
-	for (i=0 ; i<(int)sizeof(*progs)/4 ; i++)
-		((int *)progs)[i] = LittleLong ( ((int *)progs)[i] );
+	hdrcrc = le32(in);
+	initcrc();
+	for(i = 0; i < n; i++)
+		crc(in0[i]);
+	if(hdrcrc != PROGHEADER_CRC){
+		werrstr("system vars have been modified, progdefs.h is out of date");
+		goto err;
+	}
 
-	if (progs->version != PROG_VERSION)
-		fatal ("progs.dat has wrong version number (%d should be %d)", progs->version, PROG_VERSION);
-	if (progs->crc != PROGHEADER_CRC)
-		fatal ("progs.dat system vars have been modified, progdefs.h is out of date");
+	for(i = 0, pl = lumps; i < nelem(lumps); i++, pl++){
+		pl->off = le32(in);
+		pl->num = le32(in);
+		if(pl->num <= 0 || pl->off < 2*4+nelem(lumps)*2*4+4 || pl->off+pl->num*elsz[i] > n){
+			werrstr("invalid lump: off=%d num=%d elsz=%d total=%d", pl->off, pl->num, elsz[i], n);
+			goto err;
+		}
+	}
+	if((pr->entityfields = le32(in)) <= 0){
+		werrstr("invalid entityfields");
+		goto err;
+	}
 
-	pr_functions = (dfunction_t *)((byte *)progs + progs->ofs_functions);
-	pr_strings = (char *)progs + progs->ofs_strings;
-	pr_strings_size = progs->numstrings;
-	pr_globaldefs = (ddef_t *)((byte *)progs + progs->ofs_globaldefs);
-	pr_statements = (dstatement_t *)((byte *)progs + progs->ofs_statements);
-	pr_global_struct = (globalvars_t *)((byte *)progs + progs->ofs_globals);
-	pr_globals = (float *)pr_global_struct;
-
-	// byte swap the lumps
-	for (i=0 ; i<progs->numstatements ; i++)
-	{
-		pr_statements[i].op = LittleShort(pr_statements[i].op);
-		pr_statements[i].a = LittleShort(pr_statements[i].a);
-		pr_statements[i].b = LittleShort(pr_statements[i].b);
-		pr_statements[i].c = LittleShort(pr_statements[i].c);
+	pl = &lumps[PR_LUMP_FUNCTIONS];
+	pr->functions = f = Hunk_Alloc(pl->num * sizeof(*f));
+	pr->numfunctions = pl->num;
+	for(i = 0, in = in0 + pl->off; i < pl->num; i++, f++){
+		f->first_statement = le32(in);
+		f->parm_start = le32(in);
+		f->locals = le32(in);
+		f->profile = le32(in);
+		f->s_name = le32(in);
+		f->s_file = le32(in);
+		f->numparms = le32(in);
+		memmove(f->parm_size, in, MAX_PARMS);
+		in += MAX_PARMS;
 	}
 
-	for (i=0 ; i<progs->numfunctions; i++)
-	{
-	pr_functions[i].first_statement = LittleLong (pr_functions[i].first_statement);
-	pr_functions[i].parm_start = LittleLong (pr_functions[i].parm_start);
-	pr_functions[i].s_name = LittleLong (pr_functions[i].s_name);
-	pr_functions[i].s_file = LittleLong (pr_functions[i].s_file);
-	pr_functions[i].numparms = LittleLong (pr_functions[i].numparms);
-	pr_functions[i].locals = LittleLong (pr_functions[i].locals);
+	pl = &lumps[PR_LUMP_STATEMENTS];
+	pr->statements = Hunk_Alloc(pl->num * sizeof(*pr->statements));
+	for(i = 0, in = in0 + pl->off; i < pl->num; i++){
+		pr->statements[i].op = le16u(in);
+		pr->statements[i].a = le16(in);
+		pr->statements[i].b = le16(in);
+		pr->statements[i].c = le16(in);
 	}
 
-	for (i=0 ; i<progs->numglobaldefs ; i++)
-	{
-		pr_globaldefs[i].type = LittleShort (pr_globaldefs[i].type);
-		pr_globaldefs[i].ofs = LittleShort (pr_globaldefs[i].ofs);
-		pr_globaldefs[i].s_name = LittleLong (pr_globaldefs[i].s_name);
+	pl = &lumps[PR_LUMP_STRINGS];
+	pr->strings = Hunk_Alloc(pl->num + 1);
+	memmove(pr->strings, in0 + pl->off, pl->num);
+	pr->strings[pr->strings_size = pl->num] = 0;
+
+	pl = &lumps[PR_LUMP_GLOBALDEFS];
+	pr->globaldefs = Hunk_Alloc(pl->num * sizeof(*pr->globaldefs));
+	for(i = 0, in = in0 + pl->off; i < pl->num; i++){
+		// FIXME(sigrid): verify all of these as well
+		pr->globaldefs[i].type = le16u(in);
+		pr->globaldefs[i].ofs = le16u(in);
+		pr->globaldefs[i].s_name = le32(in);
 	}
 
-	for (i=0 ; i<progs->numglobals ; i++)
-		((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);
+	pl = &lumps[PR_LUMP_GLOBALS];
+	pr->globals = (float *)(pr->global_struct = Hunk_Alloc(pl->num * 4));
+	for(i = 0, in = in0 + pl->off; i < pl->num; i++)
+		((int*)pr->globals)[i] = le32(in);
 
-	PR_FieldDefs((ddef_t *)((byte *)progs + progs->ofs_fielddefs));
-	pr_edict_size = progs->entityfields * 4 + sizeof(edict_t) - sizeof(entvars_t);
+	pl = &lumps[PR_LUMP_FIELDDEFS];
+	PR_FieldDefs(pr, in0 + pl->off, pl->num);
+	pr->edict_size = pr->entityfields*4 + sizeof(edict_t) - sizeof(entvars_t);
+
+	Con_DPrintf("Programs occupy %dK.\n", n/1024);
+
+	pr->max_edicts = MAX_EDICTS;
+	pr->edicts = Hunk_Alloc(pr->max_edicts*pr->edict_size);
+
+	PR_SetBuiltinsSV(pr);
+
+	return pr;
 }
 
 
@@ -1115,21 +1139,21 @@
 	Cvar_RegisterVariable (&pr_checkextension);
 }
 
-edict_t *EDICT_NUM(int n)
+edict_t *EDICT_NUM(pr_t *pr, int n)
 {
-	if (n < 0 || n >= sv.max_edicts)
+	if (n < 0 || n >= pr->max_edicts)
 		fatal ("EDICT_NUM: bad number %d", n);
-	return (edict_t *)((byte *)sv.edicts+ (n)*pr_edict_size);
+	return (edict_t *)((byte *)pr->edicts+ (n)*pr->edict_size);
 }
 
-int NUM_FOR_EDICT(edict_t *e)
+int NUM_FOR_EDICT(pr_t *pr, edict_t *e)
 {
 	int		b;
 
-	b = (byte *)e - (byte *)sv.edicts;
-	b = b / pr_edict_size;
+	b = (byte *)e - (byte *)pr->edicts;
+	b = b / pr->edict_size;
 
-	if (b < 0 || b >= sv.num_edicts)
+	if (b < 0 || b >= pr->num_edicts)
 		fatal ("NUM_FOR_EDICT: bad pointer");
 	return b;
 }
--- a/pr_exec.c
+++ b/pr_exec.c
@@ -1,27 +1,9 @@
 #include "quakedef.h"
 
-typedef struct
-{
-	int				s;
-	dfunction_t		*f;
-} prstack_t;
-
-#define	MAX_STACK_DEPTH		2048
-static prstack_t	pr_stack[MAX_STACK_DEPTH];
-static int			pr_depth;
-
 #define	LOCALSTACK_SIZE		32768
 static int			localstack[LOCALSTACK_SIZE];
 static int			localstack_used;
 
-
-bool	pr_trace;
-dfunction_t	*pr_xfunction;
-static int pr_xstatement;
-
-
-int		pr_argc;
-
 static const char *pr_opnames[] =
 {
 "DONE",
@@ -111,8 +93,8 @@
 "BITOR"
 };
 
-char *PR_GlobalString (int ofs);
-char *PR_GlobalStringNoContents (int ofs);
+char *PR_GlobalString (pr_t *pr, int ofs);
+char *PR_GlobalStringNoContents (pr_t *pr, int ofs);
 
 
 //=============================================================================
@@ -122,7 +104,7 @@
 PR_PrintStatement
 =================
 */
-void PR_PrintStatement (dstatement_t *s)
+void PR_PrintStatement (pr_t *pr, dstatement_t *s)
 {
 	int		i;
 
@@ -135,7 +117,7 @@
 	}
 
 	if (s->op == OP_IF || s->op == OP_IFNOT)
-		Con_Printf ("%sbranch %d",PR_GlobalString(s->a),s->b);
+		Con_Printf ("%sbranch %d",PR_GlobalString(pr, s->a),s->b);
 	else if (s->op == OP_GOTO)
 	{
 		Con_Printf ("branch %d",s->a);
@@ -142,17 +124,17 @@
 	}
 	else if ( (unsigned)(s->op - OP_STORE_F) < 6)
 	{
-		Con_Printf ("%s",PR_GlobalString(s->a));
-		Con_Printf ("%s", PR_GlobalStringNoContents(s->b));
+		Con_Printf ("%s",PR_GlobalString(pr, s->a));
+		Con_Printf ("%s", PR_GlobalStringNoContents(pr, s->b));
 	}
 	else
 	{
 		if (s->a)
-			Con_Printf ("%s",PR_GlobalString(s->a));
+			Con_Printf ("%s",PR_GlobalString(pr, s->a));
 		if (s->b)
-			Con_Printf ("%s",PR_GlobalString(s->b));
+			Con_Printf ("%s",PR_GlobalString(pr, s->b));
 		if (s->c)
-			Con_Printf ("%s", PR_GlobalStringNoContents(s->c));
+			Con_Printf ("%s", PR_GlobalStringNoContents(pr, s->c));
 	}
 	Con_Printf ("\n");
 }
@@ -162,28 +144,26 @@
 PR_StackTrace
 ============
 */
-void PR_StackTrace (void)
+void PR_StackTrace (pr_t *pr)
 {
 	dfunction_t	*f;
 	int			i;
 
-	if (pr_depth == 0)
+	if (pr->depth == 0)
 	{
 		Con_Printf ("<NO STACK>\n");
 		return;
 	}
 
-	pr_stack[pr_depth].f = pr_xfunction;
-	for (i=pr_depth ; i>=0 ; i--)
+	pr->stack[pr->depth].f = pr->xfunction;
+	for (i=pr->depth ; i>=0 ; i--)
 	{
-		f = pr_stack[i].f;
+		f = pr->stack[i].f;
 
-		if (!f)
-		{
+		if(!f){
 			Con_Printf ("<NO FUNCTION>\n");
-		}
-		else
-			Con_Printf ("%12s : %s\n", PR_Str(f->s_file), PR_Str(f->s_name));
+		}else
+			Con_Printf ("%12s : %s\n", PR_Str(pr, f->s_file), PR_Str(pr, f->s_name));
 	}
 }
 
@@ -209,9 +189,9 @@
 	{
 		max = 0;
 		best = nil;
-		for (i=0 ; i<progs->numfunctions ; i++)
+		for (i=0 ; i<sv.pr->numfunctions ; i++)
 		{
-			f = &pr_functions[i];
+			f = &sv.pr->functions[i];
 			if (f->profile > max)
 			{
 				max = f->profile;
@@ -221,7 +201,7 @@
 		if (best)
 		{
 			if (num < 10)
-				Con_Printf ("%7d %s\n", best->profile, PR_Str(best->s_name));
+				Con_Printf ("%7d %s\n", best->profile, PR_Str(sv.pr, best->s_name));
 			num++;
 			best->profile = 0;
 		}
@@ -236,7 +216,7 @@
 Aborts the currently executing function
 ============
 */
-void PR_RunError (char *fmt, ...)
+void PR_RunError (pr_t *pr, char *fmt, ...)
 {
 	va_list arg;
 	char s[1024];
@@ -245,11 +225,11 @@
 	vsnprint(s, sizeof s, fmt, arg);
 	va_end(arg);
 
-	PR_PrintStatement(pr_statements + pr_xstatement);
-	PR_StackTrace();
+	PR_PrintStatement(pr, pr->statements + pr->xstatement);
+	PR_StackTrace(pr);
 	Con_Printf("%s\n", s);
 
-	pr_depth = 0;	// dump the stack so host_error can shutdown functions
+	pr->depth = 0;	// dump the stack so host_error can shutdown functions
 	Host_Error("Program error");
 }
 
@@ -268,23 +248,23 @@
 Returns the new program statement counter
 ====================
 */
-int PR_EnterFunction (dfunction_t *f)
+int PR_EnterFunction (pr_t *pr, dfunction_t *f)
 {
 	int		i, j, c, o;
 
-	pr_stack[pr_depth].s = pr_xstatement;
-	pr_stack[pr_depth].f = pr_xfunction;
-	pr_depth++;
-	if (pr_depth >= MAX_STACK_DEPTH)
-		PR_RunError ("stack overflow");
+	pr->stack[pr->depth].s = pr->xstatement;
+	pr->stack[pr->depth].f = pr->xfunction;
+	pr->depth++;
+	if(pr->depth >= MAX_STACK_DEPTH)
+		PR_RunError (pr, "stack overflow");
 
 	// save off any locals that the new function steps on
 	c = f->locals;
 	if (localstack_used + c > LOCALSTACK_SIZE)
-		PR_RunError ("PR_ExecuteProgram: locals stack overflow\n");
+		PR_RunError(pr, "PR_ExecuteProgram: locals stack overflow\n");
 
 	for (i=0 ; i < c ; i++)
-		localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i];
+		localstack[localstack_used+i] = ((int *)pr->globals)[f->parm_start + i];
 	localstack_used += c;
 
 	// copy parameters
@@ -293,12 +273,12 @@
 	{
 		for (j=0 ; j<f->parm_size[i] ; j++)
 		{
-			((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j];
+			((int *)pr->globals)[o] = ((int *)pr->globals)[OFS_PARM0+i*3+j];
 			o++;
 		}
 	}
 
-	pr_xfunction = f;
+	pr->xfunction = f;
 	return f->first_statement - 1;	// offset the s++
 }
 
@@ -307,26 +287,26 @@
 PR_LeaveFunction
 ====================
 */
-int PR_LeaveFunction (void)
+int PR_LeaveFunction (pr_t *pr)
 {
 	int		i, c;
 
-	if (pr_depth <= 0)
+	if (pr->depth <= 0)
 		fatal ("prog stack underflow");
 
 	// restore locals from the stack
-	c = pr_xfunction->locals;
+	c = pr->xfunction->locals;
 	localstack_used -= c;
 	if (localstack_used < 0)
-		PR_RunError ("PR_ExecuteProgram: locals stack underflow\n");
+		PR_RunError (pr, "PR_ExecuteProgram: locals stack underflow\n");
 
 	for (i=0 ; i < c ; i++)
-		((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i];
+		((int *)pr->globals)[pr->xfunction->parm_start + i] = localstack[localstack_used+i];
 
 	// up stack
-	pr_depth--;
-	pr_xfunction = pr_stack[pr_depth].f;
-	return pr_stack[pr_depth].s;
+	pr->depth--;
+	pr->xfunction = pr->stack[pr->depth].f;
+	return pr->stack[pr->depth].s;
 }
 
 
@@ -335,7 +315,7 @@
 PR_ExecuteProgram
 ====================
 */
-void PR_ExecuteProgram (func_t fnum)
+void PR_ExecuteProgram (pr_t *pr, func_t fnum)
 {
 	eval_t	*a, *b, *c;
 	int			s;
@@ -347,40 +327,40 @@
 	int		exitdepth;
 	eval_t	*ptr;
 
-	if (!fnum || fnum >= progs->numfunctions)
+	if (!fnum || fnum >= pr->numfunctions)
 	{
-		if (pr_global_struct->self)
-			ED_Print (PROG_TO_EDICT(pr_global_struct->self));
+		if (pr->global_struct->self)
+			ED_Print (pr, PROG_TO_EDICT(pr, pr->global_struct->self));
 		Host_Error ("PR_ExecuteProgram: NULL function");
 	}
 
-	f = &pr_functions[fnum];
+	f = &pr->functions[fnum];
 
 	runaway = 100000;
-	pr_trace = false;
+	pr->trace = false;
 
 	// make a stack frame
-	exitdepth = pr_depth;
+	exitdepth = pr->depth;
 
-	s = PR_EnterFunction (f);
+	s = PR_EnterFunction (pr, f);
 
 	while (1)
 	{
 		s++;	// next statement
 
-		st = &pr_statements[s];
-		a = (eval_t *)&pr_globals[(unsigned short)st->a];
-		b = (eval_t *)&pr_globals[(unsigned short)st->b];
-		c = (eval_t *)&pr_globals[(unsigned short)st->c];
+		st = &pr->statements[s];
+		a = (eval_t *)&pr->globals[(unsigned short)st->a];
+		b = (eval_t *)&pr->globals[(unsigned short)st->b];
+		c = (eval_t *)&pr->globals[(unsigned short)st->c];
 
 		if (!--runaway)
-			PR_RunError ("runaway loop error");
+			PR_RunError (pr, "runaway loop error");
 
-		pr_xfunction->profile++;
-		pr_xstatement = s;
+		pr->xfunction->profile++;
+		pr->xstatement = s;
 
-		if (pr_trace)
-			PR_PrintStatement (st);
+		if (pr->trace)
+			PR_PrintStatement (pr, st);
 
 		switch (st->op)
 		{
@@ -459,13 +439,13 @@
 			c->_float = !a->vector[0] && !a->vector[1] && !a->vector[2];
 			break;
 		case OP_NOT_S:
-			c->_float = !a->string || !*PR_Str(a->string);
+			c->_float = !a->string || !*PR_Str(pr, a->string);
 			break;
 		case OP_NOT_FNC:
 			c->_float = !a->function;
 			break;
 		case OP_NOT_ENT:
-			c->_float = (PROG_TO_EDICT(a->edict) == sv.edicts);
+			c->_float = (PROG_TO_EDICT(pr, a->edict) == pr->edicts);
 			break;
 
 		case OP_EQ_F:
@@ -477,7 +457,7 @@
 						(a->vector[2] == b->vector[2]);
 			break;
 		case OP_EQ_S:
-			c->_float = !strcmp(PR_Str(a->string),PR_Str(b->string));
+			c->_float = !strcmp(PR_Str(pr, a->string), PR_Str(pr, b->string));
 			break;
 		case OP_EQ_E:
 			c->_float = a->_int == b->_int;
@@ -495,7 +475,7 @@
 						(a->vector[2] != b->vector[2]);
 			break;
 		case OP_NE_S:
-			c->_float = strcmp(PR_Str(a->string),PR_Str(b->string));
+			c->_float = strcmp(PR_Str(pr, a->string), PR_Str(pr, b->string));
 			break;
 		case OP_NE_E:
 			c->_float = a->_int != b->_int;
@@ -523,11 +503,11 @@
 		case OP_STOREP_FLD:		// integers
 		case OP_STOREP_S:
 		case OP_STOREP_FNC:		// pointers
-			ptr = (eval_t *)((byte *)sv.edicts + b->_int);
+			ptr = (eval_t *)((byte *)pr->edicts + b->_int);
 			ptr->_int = a->_int;
 			break;
 		case OP_STOREP_V:
-			ptr = (eval_t *)((byte *)sv.edicts + b->_int);
+			ptr = (eval_t *)((byte *)pr->edicts + b->_int);
 			ptr->vector[0] = a->vector[0];
 			ptr->vector[1] = a->vector[1];
 			ptr->vector[2] = a->vector[2];
@@ -534,13 +514,13 @@
 			break;
 
 		case OP_ADDRESS:
-			ed = PROG_TO_EDICT(a->edict);
+			ed = PROG_TO_EDICT(pr, a->edict);
 #ifdef PARANOID
 			NUM_FOR_EDICT(ed);		// make sure it's in range
 #endif
-			if (ed == (edict_t *)sv.edicts && sv.state == ss_active)
-				PR_RunError ("assignment to world entity");
-			c->_int = (byte *)((int *)&ed->v + b->_int) - (byte *)sv.edicts;
+			if (ed == (edict_t *)pr->edicts && sv.state == ss_active)
+				PR_RunError(pr, "assignment to world entity");
+			c->_int = (byte *)((int *)&ed->v + b->_int) - (byte *)pr->edicts;
 			break;
 
 		case OP_LOAD_F:
@@ -548,7 +528,7 @@
 		case OP_LOAD_ENT:
 		case OP_LOAD_S:
 		case OP_LOAD_FNC:
-			ed = PROG_TO_EDICT(a->edict);
+			ed = PROG_TO_EDICT(pr, a->edict);
 #ifdef PARANOID
 			NUM_FOR_EDICT(ed);		// make sure it's in range
 #endif
@@ -557,7 +537,7 @@
 			break;
 
 		case OP_LOAD_V:
-			ed = PROG_TO_EDICT(a->edict);
+			ed = PROG_TO_EDICT(pr, a->edict);
 #ifdef PARANOID
 			NUM_FOR_EDICT(ed);		// make sure it's in range
 #endif
@@ -592,38 +572,38 @@
 		case OP_CALL6:
 		case OP_CALL7:
 		case OP_CALL8:
-			pr_argc = st->op - OP_CALL0;
+			pr->argc = st->op - OP_CALL0;
 			if (!a->function)
-				PR_RunError ("NULL function");
+				PR_RunError (pr, "NULL function");
 
-			newf = &pr_functions[a->function];
+			newf = &pr->functions[a->function];
 
 			if (newf->first_statement < 0)
 			{	// negative statements are built in functions
 				i = -newf->first_statement;
-				if (i >= pr_numbuiltins)
-					PR_RunError ("Bad builtin call number %d", i);
-				pr_builtins[i] ();
+				if (i >= pr->numbuiltins)
+					PR_RunError (pr, "Bad builtin call number %d", i);
+				pr->builtins[i](pr);
 				break;
 			}
 
-			s = PR_EnterFunction (newf);
+			s = PR_EnterFunction (pr, newf);
 			break;
 
 		case OP_DONE:
 		case OP_RETURN:
-			pr_globals[OFS_RETURN] = pr_globals[(unsigned short)st->a];
-			pr_globals[OFS_RETURN+1] = pr_globals[(unsigned short)st->a+1];
-			pr_globals[OFS_RETURN+2] = pr_globals[(unsigned short)st->a+2];
+			pr->globals[OFS_RETURN] = pr->globals[(unsigned short)st->a];
+			pr->globals[OFS_RETURN+1] = pr->globals[(unsigned short)st->a+1];
+			pr->globals[OFS_RETURN+2] = pr->globals[(unsigned short)st->a+2];
 
-			s = PR_LeaveFunction ();
-			if (pr_depth == exitdepth)
+			s = PR_LeaveFunction (pr);
+			if (pr->depth == exitdepth)
 				return;		// all done
 			break;
 
 		case OP_STATE:
-			ed = PROG_TO_EDICT(pr_global_struct->self);
-			ed->v.nextthink = pr_global_struct->time + 0.1;
+			ed = PROG_TO_EDICT(pr, pr->global_struct->self);
+			ed->v.nextthink = pr->global_struct->time + 0.1;
 			if (a->_float != ed->v.frame)
 			{
 				ed->v.frame = a->_float;
@@ -632,7 +612,7 @@
 			break;
 
 		default:
-			PR_RunError ("Bad opcode %d", st->op);
+			PR_RunError (pr, "Bad opcode %d", st->op);
 		}
 	}
 }
--- a/progs.h
+++ b/progs.h
@@ -1,120 +1,141 @@
 #include "pr_comp.h"			// defs shared with qcc
 #include "progdefs.h"			// generated by program cdefs
 
-typedef union eval_s
+typedef struct pr_t pr_t;
+
+typedef struct
 {
-	string_t		string;
-	float			_float;
-	float			vector[3];
-	func_t			function;
-	int				_int;
-	int				edict;
-} eval_t;
+	int				s;
+	dfunction_t		*f;
+} prstack_t;
 
+#define	MAX_STACK_DEPTH		2048
+
+typedef void (*builtin_t) (pr_t *pr);
+
 #define	MAX_ENT_LEAFS	32
 typedef struct edict_s
 {
-	bool	free;
-	link_t		area;				// linked to a division node or leaf
-	byte		alpha;
+	bool free;
+	link_t area;				// linked to a division node or leaf
+	byte alpha;
 
-	int			num_leafs;
-	int			leafnums[MAX_ENT_LEAFS];
+	int num_leafs;
+	int leafnums[MAX_ENT_LEAFS];
 
-	entity_state_t	baseline;
+	entity_state_t baseline;
 
-	float		freetime;			// sv.time when the object was freed
-	entvars_t	v;					// C exported fields from progs
+	float freetime; // sv.time when the object was freed
+	entvars_t v; // C exported fields from progs
 // other fields from progs come immediately after
 } edict_t;
 #define	EDICT_FROM_AREA(l) (edict_t*)((byte*)l - offsetof(edict_t, area))
 
-//============================================================================
+struct pr_t {
+	dfunction_t *functions;
+	ddef_t *globaldefs;
+	ddef_t *fielddefs;
+	dstatement_t *statements;
+	union {
+		globalvars_t *global_struct;
+		float *globals;
+	};
+	char *strings;
+	int strings_size;
+	int numfunctions;
+	int numglobaldefs;
+	int numfielddefs;
+	int entityfields;
+	int edict_size; // in bytes
 
-extern	dprograms_t		*progs;
-extern	dfunction_t		*pr_functions;
-extern	ddef_t			*pr_globaldefs;
-extern	ddef_t			*pr_fielddefs;
-extern	dstatement_t	*pr_statements;
-extern	globalvars_t	*pr_global_struct;
-extern	float			*pr_globals;			// same as pr_global_struct
+	const builtin_t *builtins;
+	int numbuiltins;
+	int argc;
+	bool trace;
+	dfunction_t	*xfunction;
+	int xstatement;
 
-extern	int				pr_edict_size;	// in bytes
+	prstack_t stack[MAX_STACK_DEPTH];
+	int depth;
 
+	// allocated/temp strings
+	char **str;
+	char *tempstr;
+	int max_str;
+	int num_str;
+	int num_tempstr;
+
+	edict_t *edicts;
+	int num_edicts;
+	int max_edicts;
+};
+
+typedef union eval_s
+{
+	string_t		string;
+	float			_float;
+	float			vector[3];
+	func_t			function;
+	int				_int;
+	int				edict;
+} eval_t;
+
 //============================================================================
 
 void PR_Init (void);
 
-void PR_ExecuteProgram (func_t fnum);
-void PR_LoadProgs (void);
+void PR_ExecuteProgram (pr_t *pr, func_t fnum);
+pr_t *PR_LoadProgs (char *name);
 
-char *PR_StrTmp(void);
-int PR_CopyStrTmp(char *s);
-int PR_SetStr(char *s);
-char *PR_Str (int ofs);
-char *PR_UglyValueString (etype_t, eval_t *);
+char *PR_StrTmp(pr_t *pr);
+int PR_CopyStrTmp(pr_t *pr, char *s);
+int PR_SetStr(pr_t *pr, char *s);
+char *PR_Str(pr_t *pr, int ofs);
+char *PR_UglyValueString(pr_t *pr, etype_t, eval_t *);
 
 void PR_Profile_f (void);
 
-edict_t *ED_Alloc (void);
-void ED_Free (edict_t *ed);
+edict_t *ED_Alloc(pr_t *pr);
+void ED_Free(edict_t *ed);
 
-string_t ED_NewString (char *string);
+string_t ED_NewString (pr_t *pr, char *string);
 // returns a copy of the string allocated from the server's string heap
 
-void ED_Print (edict_t *ed);
-char *ED_ParseEdict (char *data, edict_t *ent);
+void ED_Print (pr_t *pr, edict_t *ed);
+char *ED_ParseEdict (pr_t *pr, char *data, edict_t *ent);
 
-void ED_ParseGlobals (char *data);
+void ED_ParseGlobals (pr_t *pr, char *data);
+void ED_LoadFromFile (pr_t *pr, char *data);
 
-void ED_LoadFromFile (char *data);
+edict_t *EDICT_NUM(pr_t *pr, int n);
+int NUM_FOR_EDICT(pr_t *pr, edict_t *e);
 
-//define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size))
-//define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size)
+#define	NEXT_EDICT(pr,e) ((edict_t *)( (byte *)e + pr->edict_size))
 
-edict_t *EDICT_NUM(int n);
-int NUM_FOR_EDICT(edict_t *e);
+#define	EDICT_TO_PROG(pr,e) ((byte *)e - (byte *)pr->edicts)
+#define PROG_TO_EDICT(pr,e) ((edict_t *)((byte *)pr->edicts + e))
 
-#define	NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size))
-
-#define	EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts)
-#define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e))
-
 //============================================================================
 
-#define	G_FLOAT(o) (pr_globals[o])
-#define	G_INT(o) (*(int *)&pr_globals[o])
-#define	G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o]))
-#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o))
-#define	G_VECTOR(o) (&pr_globals[o])
-#define	G_STRING(o) (PR_Str(*(string_t *)&pr_globals[o]))
-#define	G_FUNCTION(o) (*(func_t *)&pr_globals[o])
+#define	G_FLOAT(pr,o) (pr->globals[o])
+#define	G_INT(pr,o) (*(int *)&pr->globals[o])
+#define	G_EDICT(pr,o) ((edict_t *)((byte *)pr->edicts+ *(int *)&pr->globals[o]))
+#define G_EDICTNUM(pr,o) NUM_FOR_EDICT(pr, G_EDICT(pr,o))
+#define	G_VECTOR(pr,o) (&pr->globals[o])
+#define	G_STRING(pr,o) (PR_Str(pr, *(string_t *)&pr->globals[o]))
+#define	G_FUNCTION(pr,o) (*(func_t *)&pr->globals[o])
 
 #define	E_FLOAT(e,o) (((float*)&e->v)[o])
 #define	E_INT(e,o) (*(int *)&((float*)&e->v)[o])
 #define	E_VECTOR(e,o) (&((float*)&e->v)[o])
-#define	E_STRING(e,o) (PR_Str(*(string_t *)&((float*)&e->v)[o]))
+#define	E_STRING(pr,e,o) (PR_Str(pr,*(string_t *)&((float*)&e->v)[o]))
 
-extern	int		type_size[8];
+extern const int type_size[8];
 
-typedef void (*builtin_t) (void);
-extern const builtin_t *pr_builtins;
-extern const int pr_numbuiltins;
+#pragma varargck	argpos	PR_RunError	2
+void PR_RunError (pr_t *pr, char *error, ...);
 
-extern int		pr_argc;
+void ED_PrintEdicts(void);
+void ED_PrintNum(pr_t *pr, int ent);
 
-extern	bool	pr_trace;
-extern	dfunction_t	*pr_xfunction;
-
-#pragma varargck	argpos	PR_RunError	1
-void PR_RunError (char *error, ...);
-
-void ED_PrintEdicts (void);
-void ED_PrintNum (int ent);
-
-eval_t *GetEdictFieldValue(edict_t *ed, char *field);
-
-void M_Init (void);
-void M_Keydown (int key);
-void M_ToggleMenu_f (void);
-void M_Draw (void);
+eval_t *GetEdictFieldValue(pr_t *pr, edict_t *ed, char *field);
--- a/server.h
+++ b/server.h
@@ -31,11 +31,6 @@
 	struct model_s	*models[MAX_MODELS];
 	char		*sound_precache[MAX_SOUNDS];	// NULL terminated
 	char		*lightstyles[Nlights];
-	int			num_edicts;
-	int			max_edicts;
-	edict_t		*edicts;			// can NOT be array indexed, because
-									// edict_t is variable sized, but can
-									// be used to reference the world ent
 	server_state_t	state;			// some actions are only valid during load
 
 	sizebuf_t	datagram;
@@ -46,6 +41,8 @@
 
 	sizebuf_t	signon;
 	byte		signon_buf[NET_MAXMESSAGE];
+
+	pr_t *pr;
 } server_t;
 
 
@@ -197,8 +194,6 @@
 bool SV_movestep (edict_t *ent, vec3_t move, bool relink);
 
 void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg);
-
-void SV_MoveToGoal (void);
 
 void SV_CheckForNewClients (void);
 void SV_RunClients (void);
--- a/sv_main.c
+++ b/sv_main.c
@@ -151,7 +151,7 @@
 	volume = clamp(volume, 0, 255);
 	attenuation = clamp(attenuation, 0, 4);
 	channel = max(channel, 0);
-	ent = NUM_FOR_EDICT(entity);
+	ent = NUM_FOR_EDICT(sv.pr, entity);
 	if(ent >= sv.protocol->limit_entity || channel >= sv.protocol->limit_channel || sound_num >= sv.protocol->limit_sound)
 		return;
 
@@ -219,7 +219,7 @@
 
 	MSG_WriteByte (&client->message, (!coop.value && deathmatch.value) ? GAME_DEATHMATCH : GAME_COOP);
 
-	MSG_WriteString (&client->message, PR_Str(sv.edicts->v.message));
+	MSG_WriteString (&client->message, PR_Str(sv.pr, sv.pr->edicts->v.message));
 
 	for (n = 1, s = sv.model_precache+1 ; *s && n < sv.protocol->limit_model ; s++)
 		MSG_WriteString(&client->message, *s);
@@ -231,12 +231,12 @@
 
 	// send music
 	MSG_WriteByte (&client->message, svc_cdtrack);
-	MSG_WriteByte (&client->message, sv.edicts->v.sounds);
-	MSG_WriteByte (&client->message, sv.edicts->v.sounds);
+	MSG_WriteByte (&client->message, sv.pr->edicts->v.sounds);
+	MSG_WriteByte (&client->message, sv.pr->edicts->v.sounds);
 
 	// set view
 	MSG_WriteByte (&client->message, svc_setview);
-	MSG_WriteShort (&client->message, NUM_FOR_EDICT(client->edict));
+	MSG_WriteShort (&client->message, NUM_FOR_EDICT(sv.pr, client->edict));
 
 	MSG_WriteByte (&client->message, svc_signonnum);
 	MSG_WriteByte (&client->message, 1);
@@ -267,7 +267,7 @@
 
 	edictnum = clientnum+1;
 
-	ent = EDICT_NUM(edictnum);
+	ent = EDICT_NUM(sv.pr, edictnum);
 
 	// set up the client_t
 	netconnection = client->netconnection;
@@ -289,9 +289,9 @@
 		memcpy(client->spawn_parms, spawn_parms, sizeof spawn_parms);
 	else{
 		// call the progs to get default spawn parms for the new client
-		PR_ExecuteProgram(pr_global_struct->SetNewParms);
+		PR_ExecuteProgram(sv.pr, sv.pr->global_struct->SetNewParms);
 		for(i=0; i<Nparms; i++)
-			client->spawn_parms[i] = (&pr_global_struct->parm1)[i];
+			client->spawn_parms[i] = (&sv.pr->global_struct->parm1)[i];
 	}
 
 	SV_SendServerinfo(client);
@@ -445,13 +445,13 @@
 	pvs = SV_FatPVS (org, sv.worldmodel);
 
 	// send over all entities (excpet the client) that touch the pvs
-	ent = NEXT_EDICT(sv.edicts);
-	for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent))
+	ent = NEXT_EDICT(sv.pr, sv.pr->edicts);
+	for (e=1 ; e<sv.pr->num_edicts ; e++, ent = NEXT_EDICT(sv.pr, ent))
 	{
 		// ignore if not touching a PV leaf
 		model = ent->v.modelindex;
 
-		v = GetEdictFieldValue(ent, "alpha");
+		v = GetEdictFieldValue(sv.pr, ent, "alpha");
 		alpha = v ? f2alpha(v->_float) : DEFAULT_ALPHA;
 
 		if (ent != clent)	// clent is ALLWAYS sent
@@ -459,7 +459,7 @@
 			if((int)ent->v.effects == EF_NODRAW)
 				continue;
 			// ignore ents without visible models
-			if(!model || !*PR_Str(ent->v.model))
+			if(!model || !*PR_Str(sv.pr, ent->v.model))
 				continue;
 			if(model >= sv.protocol->limit_model)
 				continue;
@@ -581,8 +581,8 @@
 	int		e;
 	edict_t	*ent;
 
-	ent = NEXT_EDICT(sv.edicts);
-	for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent))
+	ent = NEXT_EDICT(sv.pr, sv.pr->edicts);
+	for (e=1 ; e<sv.pr->num_edicts ; e++, ent = NEXT_EDICT(sv.pr, ent))
 	{
 		ent->v.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH;
 	}
@@ -606,7 +606,7 @@
 	// send a damage message
 	if (ent->v.dmg_take || ent->v.dmg_save)
 	{
-		other = PROG_TO_EDICT(ent->v.dmg_inflictor);
+		other = PROG_TO_EDICT(sv.pr, ent->v.dmg_inflictor);
 		MSG_WriteByte (msg, svc_damage);
 		MSG_WriteByte (msg, ent->v.dmg_save);
 		MSG_WriteByte (msg, ent->v.dmg_take);
@@ -637,9 +637,9 @@
 
 	// stuff the sigil bits into the high bits of items for sbar, or else
 	// mix in items2
-	val = GetEdictFieldValue(ent, "items2");
-	items = (int)ent->v.items | (val ? ((int)val->_float << 23) : ((int)pr_global_struct->serverflags << 28));
-	weaponmodel = SV_ModelIndex(PR_Str(ent->v.weaponmodel));
+	val = GetEdictFieldValue(sv.pr, ent, "items2");
+	items = (int)ent->v.items | (val ? ((int)val->_float << 23) : ((int)sv.pr->global_struct->serverflags << 28));
+	weaponmodel = SV_ModelIndex(PR_Str(sv.pr, ent->v.weaponmodel));
 
 	if ((int)ent->v.flags & FL_ONGROUND)
 		bits |= SU_ONGROUND;
@@ -960,10 +960,10 @@
 	edict_t			*svent;
 	int				entnum;
 
-	for (entnum = 0; entnum < sv.num_edicts ; entnum++)
+	for (entnum = 0; entnum < sv.pr->num_edicts ; entnum++)
 	{
 		// get the current server version
-		svent = EDICT_NUM(entnum);
+		svent = EDICT_NUM(sv.pr, entnum);
 		if (svent->free)
 			continue;
 		if (entnum > svs.maxclients && !svent->v.modelindex)
@@ -983,7 +983,7 @@
 		else
 		{
 			svent->baseline.colormap = 0;
-			svent->baseline.modelindex = SV_ModelIndex(PR_Str(svent->v.model));
+			svent->baseline.modelindex = SV_ModelIndex(PR_Str(sv.pr, svent->v.model));
 			svent->baseline.alpha = svent->alpha;
 		}
 
@@ -1059,7 +1059,7 @@
 {
 	int		i, j;
 
-	svs.serverflags = pr_global_struct->serverflags;
+	svs.serverflags = sv.pr->global_struct->serverflags;
 
 	for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
 	{
@@ -1067,10 +1067,10 @@
 			continue;
 
 		// call the progs to get default spawn parms for the new client
-		pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
-		PR_ExecuteProgram (pr_global_struct->SetChangeParms);
+		sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, host_client->edict);
+		PR_ExecuteProgram(sv.pr, sv.pr->global_struct->SetChangeParms);
 		for (j=0 ; j<Nparms ; j++)
-			host_client->spawn_parms[j] = (&pr_global_struct->parm1)[j];
+			host_client->spawn_parms[j] = (&sv.pr->global_struct->parm1)[j];
 	}
 }
 
@@ -1124,13 +1124,10 @@
 	sv.protocol = sv_protocol;
 
 	// load progs to get entity field count
-	PR_LoadProgs ();
+	sv.pr = PR_LoadProgs("progs.dat");
 
 	// allocate server memory
-	sv.max_edicts = MAX_EDICTS;
 
-	sv.edicts = Hunk_Alloc(sv.max_edicts*pr_edict_size);
-
 	sv.datagram.maxsize = sizeof sv.datagram_buf;
 	sv.datagram.cursize = 0;
 	sv.datagram.data = sv.datagram_buf;
@@ -1147,10 +1144,10 @@
 	sv.signon.name = "sv.signon";
 
 	// leave slots at start for clients only
-	sv.num_edicts = svs.maxclients+1;
+	sv.pr->num_edicts = svs.maxclients+1;
 	for (i=0 ; i<svs.maxclients ; i++)
 	{
-		ent = EDICT_NUM(i+1);
+		ent = EDICT_NUM(sv.pr, i+1);
 		svs.clients[i].edict = ent;
 	}
 
@@ -1183,25 +1180,25 @@
 	}
 
 	// load the rest of the entities
-	ent = EDICT_NUM(0);
-	memset(&ent->v, 0, progs->entityfields * 4);
+	ent = EDICT_NUM(sv.pr, 0);
+	memset(&ent->v, 0, sv.pr->entityfields * 4);
 	ent->free = false;
-	ent->v.model = PR_SetStr(sv.worldmodel->name);
+	ent->v.model = PR_SetStr(sv.pr, sv.worldmodel->name);
 	ent->v.modelindex = 1;		// world model
 	ent->v.solid = SOLID_BSP;
 	ent->v.movetype = MOVETYPE_PUSH;
 
 	if (coop.value)
-		pr_global_struct->coop = coop.value;
+		sv.pr->global_struct->coop = coop.value;
 	else
-		pr_global_struct->deathmatch = deathmatch.value;
+		sv.pr->global_struct->deathmatch = deathmatch.value;
 
-	pr_global_struct->mapname = PR_SetStr(sv.name);
+	sv.pr->global_struct->mapname = PR_SetStr(sv.pr, sv.name);
 
 	// serverflags are for cross level information (sigils)
-	pr_global_struct->serverflags = svs.serverflags;
+	sv.pr->global_struct->serverflags = svs.serverflags;
 
-	ED_LoadFromFile (sv.worldmodel->entities);
+	ED_LoadFromFile(sv.pr, sv.worldmodel->entities);
 
 	sv.active = true;
 
--- a/sv_move.c
+++ b/sv_move.c
@@ -77,7 +77,7 @@
 Called by monster program code.
 The move will be adjusted for slopes and stairs, but if the move isn't
 possible, no move is done, false is returned, and
-pr_global_struct->trace_normal is set to the normal of the blocking wall
+sv.pr->global_struct->trace_normal is set to the normal of the blocking wall
 =============
 */
 bool SV_movestep (edict_t *ent, vec3_t move, bool relink)
@@ -99,13 +99,13 @@
 		for (i=0 ; i<2 ; i++)
 		{
 			VectorAdd (ent->v.origin, move, neworg);
-			enemy = PROG_TO_EDICT(ent->v.enemy);
-			if (i == 0 && enemy != sv.edicts)
+			enemy = PROG_TO_EDICT(sv.pr, ent->v.enemy);
+			if (i == 0 && enemy != sv.pr->edicts)
 			{
-				dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2];
+				dz = ent->v.origin[2] - PROG_TO_EDICT(sv.pr, ent->v.enemy)->v.origin[2];
 				if (dz > 40)
 					neworg[2] -= 8;
-				if (dz < 30)
+				else if (dz < 30)
 					neworg[2] += 8;
 			}
 			trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent);
@@ -121,7 +121,7 @@
 				return true;
 			}
 
-			if (enemy == sv.edicts)
+			if (enemy == sv.pr->edicts)
 				break;
 		}
 
@@ -182,7 +182,7 @@
 		//Con_Printf ("back on ground\n");
 		ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND;
 	}
-	ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+	ent->v.groundentity = EDICT_TO_PROG(sv.pr, trace.ent);
 
 	// the move is ok
 	if (relink)
@@ -202,7 +202,7 @@
 
 ======================
 */
-void PF_changeyaw (void);
+void PF_changeyaw (pr_t *pr);
 bool SV_StepDirection (edict_t *ent, float yaw, float dist)
 {
 	vec3_t		move, oldorigin;
@@ -209,7 +209,7 @@
 	float		delta;
 
 	ent->v.ideal_yaw = yaw;
-	PF_changeyaw();
+	PF_changeyaw(sv.pr);
 
 	yaw = yaw*M_PI*2 / 360;
 	move[0] = cos(yaw)*dist;
@@ -336,57 +336,3 @@
 		SV_FixCheckBottom (actor);
 
 }
-
-/*
-======================
-SV_CloseEnough
-
-======================
-*/
-bool SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
-{
-	int		i;
-
-	for (i=0 ; i<3 ; i++)
-	{
-		if (goal->v.absmin[i] > ent->v.absmax[i] + dist)
-			return false;
-		if (goal->v.absmax[i] < ent->v.absmin[i] - dist)
-			return false;
-	}
-	return true;
-}
-
-/*
-======================
-SV_MoveToGoal
-
-======================
-*/
-void SV_MoveToGoal (void)
-{
-	edict_t		*ent, *goal;
-	float		dist;
-
-	ent = PROG_TO_EDICT(pr_global_struct->self);
-	goal = PROG_TO_EDICT(ent->v.goalentity);
-	dist = G_FLOAT(OFS_PARM0);
-
-	if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
-	{
-		G_FLOAT(OFS_RETURN) = 0;
-		return;
-	}
-
-	// if the next step hits the enemy, return immediately
-	if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts &&  SV_CloseEnough (ent, goal, dist) )
-		return;
-
-	// bump around...
-	if ( (rand()&3)==1 ||
-	!SV_StepDirection (ent, ent->v.ideal_yaw, dist))
-	{
-		SV_NewChaseDir (ent, goal, dist);
-	}
-}
-
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -42,12 +42,12 @@
 	{
 		if (isnanf(ent->v.velocity[i]))
 		{
-			Con_Printf ("Got a NaN velocity on %s\n", PR_Str(ent->v.classname));
+			Con_Printf ("Got a NaN velocity on %s\n", PR_Str(sv.pr, ent->v.classname));
 			ent->v.velocity[i] = 0;
 		}
 		if (isnanf(ent->v.origin[i]))
 		{
-			Con_Printf ("Got a NaN origin on %s\n", PR_Str(ent->v.classname));
+			Con_Printf ("Got a NaN origin on %s\n", PR_Str(sv.pr, ent->v.classname));
 			ent->v.origin[i] = 0;
 		}
 		if (ent->v.velocity[i] > sv_maxvelocity.value)
@@ -80,10 +80,10 @@
 								// it is possible to start that way
 								// by a trigger with a local time.
 	ent->v.nextthink = 0;
-	pr_global_struct->time = thinktime;
-	pr_global_struct->self = EDICT_TO_PROG(ent);
-	pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
-	PR_ExecuteProgram (ent->v.think);
+	sv.pr->global_struct->time = thinktime;
+	sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, ent);
+	sv.pr->global_struct->other = EDICT_TO_PROG(sv.pr, sv.pr->edicts);
+	PR_ExecuteProgram(sv.pr, ent->v.think);
 	return !ent->free;
 }
 
@@ -98,26 +98,26 @@
 {
 	int		old_self, old_other;
 
-	old_self = pr_global_struct->self;
-	old_other = pr_global_struct->other;
+	old_self = sv.pr->global_struct->self;
+	old_other = sv.pr->global_struct->other;
 
-	pr_global_struct->time = sv.time;
+	sv.pr->global_struct->time = sv.time;
 	if (e1->v.touch && e1->v.solid != SOLID_NOT)
 	{
-		pr_global_struct->self = EDICT_TO_PROG(e1);
-		pr_global_struct->other = EDICT_TO_PROG(e2);
-		PR_ExecuteProgram (e1->v.touch);
+		sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, e1);
+		sv.pr->global_struct->other = EDICT_TO_PROG(sv.pr, e2);
+		PR_ExecuteProgram (sv.pr, e1->v.touch);
 	}
 
 	if (e2->v.touch && e2->v.solid != SOLID_NOT)
 	{
-		pr_global_struct->self = EDICT_TO_PROG(e2);
-		pr_global_struct->other = EDICT_TO_PROG(e1);
-		PR_ExecuteProgram (e2->v.touch);
+		sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, e2);
+		sv.pr->global_struct->other = EDICT_TO_PROG(sv.pr, e1);
+		PR_ExecuteProgram (sv.pr, e2->v.touch);
 	}
 
-	pr_global_struct->self = old_self;
-	pr_global_struct->other = old_other;
+	sv.pr->global_struct->self = old_self;
+	sv.pr->global_struct->other = old_other;
 }
 
 
@@ -228,7 +228,7 @@
 			if (trace.ent->v.solid == SOLID_BSP)
 			{
 				ent->v.flags =	(int)ent->v.flags | FL_ONGROUND;
-				ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+				ent->v.groundentity = EDICT_TO_PROG(sv.pr, trace.ent);
 			}
 		}
 		if (!trace.plane.normal[2])
@@ -312,7 +312,7 @@
 
 	eval_t	*val;
 
-	val = GetEdictFieldValue(ent, "gravity");
+	val = GetEdictFieldValue(sv.pr, ent, "gravity");
 	if (val && val->_float)
 		ent_gravity = val->_float;
 	else
@@ -401,8 +401,8 @@
 
 	// see if any solid entities are inside the final position
 	num_moved = 0;
-	check = NEXT_EDICT(sv.edicts);
-	for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
+	check = NEXT_EDICT(sv.pr, sv.pr->edicts);
+	for (e=1 ; e<sv.pr->num_edicts ; e++, check = NEXT_EDICT(sv.pr, check))
 	{
 		if (check->free)
 			continue;
@@ -413,7 +413,7 @@
 
 		// if the entity is standing on the pusher, it will definately be moved
 		if ( ! ( ((int)check->v.flags & FL_ONGROUND)
-		&& PROG_TO_EDICT(check->v.groundentity) == pusher) )
+		&& PROG_TO_EDICT(sv.pr, check->v.groundentity) == pusher) )
 		{
 			if ( check->v.absmin[0] >= maxs[0]
 			|| check->v.absmin[1] >= maxs[1]
@@ -466,9 +466,9 @@
 			// otherwise, just stay in place until the obstacle is gone
 			if (pusher->v.blocked)
 			{
-				pr_global_struct->self = EDICT_TO_PROG(pusher);
-				pr_global_struct->other = EDICT_TO_PROG(check);
-				PR_ExecuteProgram (pusher->v.blocked);
+				sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, pusher);
+				sv.pr->global_struct->other = EDICT_TO_PROG(sv.pr, check);
+				PR_ExecuteProgram (sv.pr, pusher->v.blocked);
 			}
 
 			// move back any entities we already moved
@@ -514,10 +514,10 @@
 	if (thinktime > oldltime && thinktime <= ent->v.ltime)
 	{
 		ent->v.nextthink = 0;
-		pr_global_struct->time = sv.time;
-		pr_global_struct->self = EDICT_TO_PROG(ent);
-		pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
-		PR_ExecuteProgram (ent->v.think);
+		sv.pr->global_struct->time = sv.time;
+		sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, ent);
+		sv.pr->global_struct->other = EDICT_TO_PROG(sv.pr, sv.pr->edicts);
+		PR_ExecuteProgram (sv.pr, ent->v.think);
 		if (ent->free)
 			return;
 	}
@@ -791,7 +791,7 @@
 		if (ent->v.solid == SOLID_BSP)
 		{
 			ent->v.flags =	(int)ent->v.flags | FL_ONGROUND;
-			ent->v.groundentity = EDICT_TO_PROG(downtrace.ent);
+			ent->v.groundentity = EDICT_TO_PROG(sv.pr, downtrace.ent);
 		}
 	}
 	else
@@ -818,9 +818,9 @@
 		return;		// unconnected slot
 
 	// call standard client pre-think
-	pr_global_struct->time = sv.time;
-	pr_global_struct->self = EDICT_TO_PROG(ent);
-	PR_ExecuteProgram (pr_global_struct->PlayerPreThink);
+	sv.pr->global_struct->time = sv.time;
+	sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, ent);
+	PR_ExecuteProgram (sv.pr, sv.pr->global_struct->PlayerPreThink);
 
 	// do a move
 	SV_CheckVelocity (ent);
@@ -866,9 +866,9 @@
 	// call standard player post-think
 	SV_LinkEdict (ent, (int)ent->v.movetype != MOVETYPE_NOCLIP);
 
-	pr_global_struct->time = sv.time;
-	pr_global_struct->self = EDICT_TO_PROG(ent);
-	PR_ExecuteProgram (pr_global_struct->PlayerPostThink);
+	sv.pr->global_struct->time = sv.time;
+	sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, ent);
+	PR_ExecuteProgram (sv.pr, sv.pr->global_struct->PlayerPostThink);
 }
 
 //============================================================================
@@ -996,7 +996,7 @@
 	// stop if on ground
 	if(trace.plane.normal[2] > 0.5 && DotProduct(trace.plane.normal, ent->v.velocity) < 15){
 		ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
-		ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+		ent->v.groundentity = EDICT_TO_PROG(sv.pr, trace.ent);
 		ClipVelocity(ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff + trace.plane.normal[2]);
 	}else{
 		ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
@@ -1069,19 +1069,19 @@
 	edict_t	*ent;
 
 	// let the progs know that a new frame has started
-	pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
-	pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
-	pr_global_struct->time = sv.time;
-	PR_ExecuteProgram (pr_global_struct->StartFrame);
+	sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, sv.pr->edicts);
+	sv.pr->global_struct->other = EDICT_TO_PROG(sv.pr, sv.pr->edicts);
+	sv.pr->global_struct->time = sv.time;
+	PR_ExecuteProgram (sv.pr, sv.pr->global_struct->StartFrame);
 
 	// treat each object in turn
-	ent = sv.edicts;
-	for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
+	ent = sv.pr->edicts;
+	for (i=0 ; i<sv.pr->num_edicts ; i++, ent = NEXT_EDICT(sv.pr, ent))
 	{
 		if (ent->free)
 			continue;
 
-		if (pr_global_struct->force_retouch)
+		if (sv.pr->global_struct->force_retouch)
 		{
 			SV_LinkEdict (ent, true);	// force retouch even for stationary
 		}
@@ -1105,8 +1105,8 @@
 			fatal ("SV_Physics: bad movetype %d", (int)ent->v.movetype);
 	}
 
-	if (pr_global_struct->force_retouch)
-		pr_global_struct->force_retouch--;
+	if (sv.pr->global_struct->force_retouch)
+		sv.pr->global_struct->force_retouch--;
 
 	sv.time += host_frametime;
 }
--- a/unix/u.h
+++ b/unix/u.h
@@ -18,6 +18,7 @@
 typedef long long vlong;
 typedef unsigned long long uvlong;
 typedef uint8_t u8int;
+typedef int16_t s16int;
 typedef uint16_t u16int;
 typedef int32_t s32int;
 typedef uint32_t u32int;
--- a/world.c
+++ b/world.c
@@ -275,16 +275,16 @@
 		|| ent->v.absmax[1] < touch->v.absmin[1]
 		|| ent->v.absmax[2] < touch->v.absmin[2] )
 			continue;
-		old_self = pr_global_struct->self;
-		old_other = pr_global_struct->other;
+		old_self = sv.pr->global_struct->self;
+		old_other = sv.pr->global_struct->other;
 
-		pr_global_struct->self = EDICT_TO_PROG(touch);
-		pr_global_struct->other = EDICT_TO_PROG(ent);
-		pr_global_struct->time = sv.time;
-		PR_ExecuteProgram (touch->v.touch);
+		sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, touch);
+		sv.pr->global_struct->other = EDICT_TO_PROG(sv.pr, ent);
+		sv.pr->global_struct->time = sv.time;
+		PR_ExecuteProgram (sv.pr, touch->v.touch);
 
-		pr_global_struct->self = old_self;
-		pr_global_struct->other = old_other;
+		sv.pr->global_struct->self = old_self;
+		sv.pr->global_struct->other = old_other;
 	}
 
 	// recurse down both sides
@@ -339,7 +339,7 @@
 	if (ent->area.prev)
 		SV_UnlinkEdict (ent);	// unlink from old position
 
-	if (ent == sv.edicts)
+	if (ent == sv.pr->edicts)
 		return;		// don't add the world
 
 	if (ent->free)
@@ -473,7 +473,7 @@
 	trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent);
 
 	if (trace.startsolid)
-		return sv.edicts;
+		return sv.pr->edicts;
 
 	return nil;
 }
@@ -711,9 +711,9 @@
 			return;
 		if (clip->passedict)
 		{
-		 	if (PROG_TO_EDICT(touch->v.owner) == clip->passedict)
+			if (PROG_TO_EDICT(sv.pr, touch->v.owner) == clip->passedict)
 				continue;	// don't clip against own missiles
-			if (PROG_TO_EDICT(clip->passedict->v.owner) == touch)
+			if (PROG_TO_EDICT(sv.pr, clip->passedict->v.owner) == touch)
 				continue;	// don't clip against owner
 		}
 
@@ -790,7 +790,7 @@
 	memset(&clip, 0, sizeof clip);
 
 	// clip to world
-	clip.trace = SV_ClipMoveToEntity ( sv.edicts, start, mins, maxs, end );
+	clip.trace = SV_ClipMoveToEntity ( sv.pr->edicts, start, mins, maxs, end );
 
 	clip.start = start;
 	clip.end = end;