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

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

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

See the GNU General Public License for more details.

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

///////////////////////////////////////////////////////////////////////////
/// @file	sv_edict.c
/// @brief	PR_? Entity Dictionary
/// @defgroup edict_functions Entity Dictionary Functions
///////////////////////////////////////////////////////////////////////////

#include "quakedef.h"

dprograms_t		*progs;
dfunction_t		*pr_functions;
char			*pr_strings;
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

unsigned short		pr_crc;

int		type_size[8] = {1, sizeof (string_t) / 4, 1, 3, 1, 1, sizeof (func_t) / 4, sizeof (void *) / 4};

ddef_t *ED_FieldAtOfs (int ofs);
bool	ED_ParseEpair (void *base, ddef_t *key, char *s);

cvar_t	nomonsters = {"nomonsters", "0"};
cvar_t	gamecfg = {"gamecfg", "0"};
cvar_t	scratch1 = {"scratch1", "0"};
cvar_t	scratch2 = {"scratch2", "0"};
cvar_t	scratch3 = {"scratch3", "0"};
cvar_t	scratch4 = {"scratch4", "0"};
cvar_t	savedgamecfg = {"savedgamecfg", "0", true};
cvar_t	saved1 = {"saved1", "0", true};
cvar_t	saved2 = {"saved2", "0", true};
cvar_t	saved3 = {"saved3", "0", true};
cvar_t	saved4 = {"saved4", "0", true};

#define	MAX_FIELD_LEN	64
#define GEFV_CACHESIZE	2

typedef struct
{
	ddef_t	*pcache;
	char	field[MAX_FIELD_LEN];
} gefv_cache;

static gefv_cache	gefvCache[GEFV_CACHESIZE] = {{NULL, ""}, {NULL, ""}};

///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	Sets everything to NULL
///////////////////////////////////////////////////////////////////////////
void ED_ClearEdict (edict_t *e)
{
	memset (&e->v, 0, progs->entityfields * 4);
	e->free = false;
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	Either finds a free edict, or allocates a new one.
///			Try to avoid reusing an entity that was recently freed, because
///			it can cause the client to think the entity morphed into
///			something else instead of being removed and recreated, which
///			can cause interpolated angles and bad trails.
///////////////////////////////////////////////////////////////////////////
edict_t *ED_Alloc (void)
{
	int			i;
	edict_t		*e;
	
	for (i = svs.maxclients + 1 ; i < sv.num_edicts ; i++)
	{
		e = EDICT_NUM (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);
			return e;
		}
	}
	
	if (i == MAX_EDICTS)
		Sys_Error ("ED_Alloc: no free edicts");
		
	sv.num_edicts++;
	e = EDICT_NUM (i);
	ED_ClearEdict (e);
	
	return e;
}

///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	Marks the edict as free
/// @todo	FIXME: walk all entities and NULL out references to this entity
///////////////////////////////////////////////////////////////////////////
void ED_Free (edict_t *ed)
{
	SV_UnlinkEdict (ed);		// unlink from world bsp
	
	ed->free = true;
	ed->v.model = 0;
	ed->v.takedamage = 0;
	ed->v.modelindex = 0;
	ed->v.colormap = 0;
	ed->v.skin = 0;
	ed->v.frame = 0;
	VectorCopy (vec3_origin, ed->v.origin);
	VectorCopy (vec3_origin, ed->v.angles);
	ed->v.nextthink = -1;
	ed->v.solid = 0;
	
	ed->freetime = sv.time;
}

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

///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	
/// @param	ofs
///////////////////////////////////////////////////////////////////////////
ddef_t *ED_GlobalAtOfs (int ofs)
{
	ddef_t	*def;
	int		i;
	
	for (i = 0 ; i < progs->numglobaldefs ; i++)
	{
		def = &pr_globaldefs[i];
		if (def->ofs == ofs)
			return def;
	}
	return NULL;
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	
/// @param	ofs
/// @return	
///////////////////////////////////////////////////////////////////////////
ddef_t *ED_FieldAtOfs (int ofs)
{
	ddef_t		*def;
	int			i;
	
	for (i = 0 ; i < progs->numfielddefs ; i++)
	{
		def = &pr_fielddefs[i];
		if (def->ofs == ofs)
			return def;
	}
	return NULL;
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	
/// @param	name
/// @return
///////////////////////////////////////////////////////////////////////////
ddef_t *ED_FindField (char *name)
{
	ddef_t		*def;
	int			i;
	
	for (i = 0 ; i < progs->numfielddefs ; i++)
	{
		def = &pr_fielddefs[i];
		if (!strcmp (pr_strings + def->s_name, name))
			return def;
	}
	return NULL;
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	
/// @param	name
/// @return	
///////////////////////////////////////////////////////////////////////////
ddef_t *ED_FindGlobal (char *name)
{
	ddef_t		*def;
	int			i;
	
	for (i = 0 ; i < progs->numglobaldefs ; i++)
	{
		def = &pr_globaldefs[i];
		if (!strcmp (pr_strings + def->s_name, name))
			return def;
	}
	return NULL;
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	
/// @param	name
/// @return	
///////////////////////////////////////////////////////////////////////////
dfunction_t *ED_FindFunction (char *name)
{
	dfunction_t		*func;
	int				i;
	
	for (i = 0 ; i < progs->numfunctions ; i++)
	{
		func = &pr_functions[i];
		if (!strcmp (pr_strings + func->s_name, name))
			return func;
	}
	return NULL;
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	
/// @param	ed		
/// @param	field	
/// @return	
///////////////////////////////////////////////////////////////////////////
eval_t *GetEdictFieldValue (edict_t *ed, char *field)
{
	ddef_t			*def = NULL;
	int				i;
	static int		rep = 0;
	
	for (i = 0 ; i < GEFV_CACHESIZE ; i++)
	{
		if (!strcmp (field, gefvCache[i].field))
		{
			def = gefvCache[i].pcache;
			goto Done;
		}
	}
	
	def = ED_FindField (field);
	
	if (strlen (field) < MAX_FIELD_LEN)
	{
		gefvCache[rep].pcache = def;
		strcpy (gefvCache[rep].field, field);
		rep ^= 1;
	}
	
Done:
	if (!def)
		return NULL;
		
	return (eval_t *) ( (char *) &ed->v + def->ofs * 4);
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	Returns a string describing *data in a type specific manner
/// @param	type	
/// @param	val		
/// @return	
///////////////////////////////////////////////////////////////////////////
char *PR_ValueString (etype_t type, eval_t *val)
{
	static char	line[256];
	ddef_t		*def;
	dfunction_t	*f;
	
	type &= ~DEF_SAVEGLOBAL;
	
	switch (type)
	{
		case ev_string:
			sprintf (line, "%s", pr_strings + val->string);
			break;
		case ev_entity:
			sprintf (line, "entity %i", NUM_FOR_EDICT (PROG_TO_EDICT (val->edict)));
			break;
		case ev_function:
			f = pr_functions + val->function;
			sprintf (line, "%s()", pr_strings + f->s_name);
			break;
		case ev_field:
			def = ED_FieldAtOfs (val->_int);
			sprintf (line, ".%s", pr_strings + def->s_name);
			break;
		case ev_void:
			sprintf (line, "void");
			break;
		case ev_float:
			sprintf (line, "%5.1f", val->_float);
			break;
		case ev_vector:
			sprintf (line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]);
			break;
		case ev_pointer:
			sprintf (line, "pointer");
			break;
		default:
			sprintf (line, "bad type %i", type);
			break;
	}
	
	return line;
}


///////////////////////////////////////////////////////////////////////////
/// @brief	Returns a string describing *data in a type specific manner
///			Easier to parse than PR_ValueString
///////////////////////////////////////////////////////////////////////////
char *PR_UglyValueString (etype_t type, eval_t *val)
{
	static char	line[256];
	ddef_t		*def;
	dfunction_t	*f;
	
	type &= ~DEF_SAVEGLOBAL;
	
	switch (type)
	{
		case ev_string:
			sprintf (line, "%s", pr_strings + val->string);
			break;
		case ev_entity:
			sprintf (line, "%i", NUM_FOR_EDICT (PROG_TO_EDICT (val->edict)));
			break;
		case ev_function:
			f = pr_functions + val->function;
			sprintf (line, "%s", pr_strings + f->s_name);
			break;
		case ev_field:
			def = ED_FieldAtOfs (val->_int);
			sprintf (line, "%s", pr_strings + def->s_name);
			break;
		case ev_void:
			sprintf (line, "void");
			break;
		case ev_float:
			sprintf (line, "%f", val->_float);
			break;
		case ev_vector:
			sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
			break;
		default:
			sprintf (line, "bad type %i", type);
			break;
	}
	
	return line;
}


///////////////////////////////////////////////////////////////////////////
/// @brief	Returns a string with a description and the contents of a global,
///			padded to 20 field width
///////////////////////////////////////////////////////////////////////////
char *PR_GlobalString (int ofs)
{
	char	*s;
	int		i;
	ddef_t	*def;
	void	*val;
	static char	line[128];
	
	val = (void *) &pr_globals[ofs];
	def = ED_GlobalAtOfs (ofs);
	if (!def)
		sprintf (line, "%i(???)", ofs);
	else
	{
		s = PR_ValueString (def->type, val);
		sprintf (line, "%i(%s)%s", ofs, pr_strings + def->s_name, s);
	}
	
	i = strlen (line);
	for (; i < 20 ; i++)
		strcat (line, " ");
	strcat (line, " ");
	
	return line;
}


///////////////////////////////////////////////////////////////////////////
/// @brief	
///////////////////////////////////////////////////////////////////////////
char *PR_GlobalStringNoContents (int ofs)
{
	int		i;
	ddef_t	*def;
	static char	line[128];
	
	def = ED_GlobalAtOfs (ofs);
	if (!def)
		sprintf (line, "%i(???)", ofs);
	else
		sprintf (line, "%i(%s)", ofs, pr_strings + def->s_name);
		
	i = strlen (line);
	for (; i < 20 ; i++)
		strcat (line, " ");
	strcat (line, " ");
	
	return line;
}


///////////////////////////////////////////////////////////////////////////
/// @brief	For debugging.
///////////////////////////////////////////////////////////////////////////
void ED_Print (edict_t *ed)
{
	int		l;
	ddef_t	*d;
	int		*v;
	int		i, j;
	char	*name;
	int		type;
	
	if (ed->free)
	{
		Con_Printf ("FREE\n");
		return;
	}
	
	Con_Printf ("\nEDICT %i:\n", NUM_FOR_EDICT (ed));
	for (i = 1 ; i < progs->numfielddefs ; i++)
	{
		d = &pr_fielddefs[i];
		name = pr_strings + d->s_name;
		if (name[strlen (name)-2] == '_')
			continue;	// skip _x, _y, _z vars
			
		v = (int *) ( (char *) &ed->v + d->ofs * 4);
		
		// if the value is still all 0, skip the field
		type = d->type & ~DEF_SAVEGLOBAL;
		
		for (j = 0 ; j < type_size[type] ; j++)
			if (v[j])
				break;
		if (j == type_size[type])
			continue;
			
		Con_Printf ("%s", name);
		l = strlen (name);
		while (l++ < 15)
			Con_Printf (" ");
			
		Con_Printf ("%s\n", PR_ValueString (d->type, (eval_t *) v));
	}
}

/*
=============
ED_Write

For savegames
=============
*/
void ED_Write (FILE *f, edict_t *ed)
{
	ddef_t	*d;
	int		*v;
	int		i, j;
	char	*name;
	int		type;
	
	fprintf (f, "{\n");
	
	if (ed->free)
	{
		fprintf (f, "}\n");
		return;
	}
	
	for (i = 1 ; i < progs->numfielddefs ; i++)
	{
		d = &pr_fielddefs[i];
		name = pr_strings + d->s_name;
		if (name[strlen (name)-2] == '_')
			continue;	// skip _x, _y, _z vars
			
		v = (int *) ( (char *) &ed->v + d->ofs * 4);
		
		// if the value is still all 0, skip the field
		type = d->type & ~DEF_SAVEGLOBAL;
		for (j = 0 ; j < type_size[type] ; j++)
			if (v[j])
				break;
		if (j == type_size[type])
			continue;
			
		fprintf (f, "\"%s\" ", name);
		fprintf (f, "\"%s\"\n", PR_UglyValueString (d->type, (eval_t *) v));
	}
	
	fprintf (f, "}\n");
}


///////////////////////////////////////////////////////////////////////////
/// @brief	
/// @param	
///////////////////////////////////////////////////////////////////////////
void ED_PrintNum (int ent)
{
	ED_Print (EDICT_NUM (ent));
}

/*
=============
ED_PrintEdicts

For debugging, prints all the entities in the current server
=============
*/
void ED_PrintEdicts (void)
{
	int		i;
	
	Con_Printf ("%i entities\n", sv.num_edicts);
	for (i = 0 ; i < sv.num_edicts ; i++)
		ED_PrintNum (i);
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	For debugging, prints a single edict.
///////////////////////////////////////////////////////////////////////////
void ED_PrintEdict_f (void)
{
	int		i;
	
	i = Q_atoi (Cmd_Argv (1));
	if (i >= sv.num_edicts)
	{
		Con_Printf ("Bad edict number\n");
		return;
	}
	ED_PrintNum (i);
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	For debugging, prints a single edict.
///////////////////////////////////////////////////////////////////////////
void ED_Count (void)
{
	int		i;
	edict_t	*ent;
	int		active, models, solid, step;
	
	active = models = solid = step = 0;
	for (i = 0 ; i < sv.num_edicts ; i++)
	{
		ent = EDICT_NUM (i);
		if (ent->free)
			continue;
		active++;
		if (ent->v.solid)
			solid++;
		if (ent->v.model)
			models++;
		if (ent->v.movetype == MOVETYPE_STEP)
			step++;
	}
	
	Con_Printf ("num_edicts:%3i\n", sv.num_edicts);
	Con_Printf ("active    :%3i\n", active);
	Con_Printf ("view      :%3i\n", models);
	Con_Printf ("touch     :%3i\n", solid);
	Con_Printf ("step      :%3i\n", step);
	
}

///////////////////////////////////////////////////////////////////////////
/// 
/// 				ARCHIVING GLOBALS
/// 
/// FIXME: need to tag constants, doesn't really work
///////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	
///////////////////////////////////////////////////////////////////////////
void ED_WriteGlobals (FILE *f)
{
	ddef_t		*def;
	int			i;
	char		*name;
	int			type;
	
	fprintf (f, "{\n");
	for (i = 0 ; i < progs->numglobaldefs ; i++)
	{
		def = &pr_globaldefs[i];
		type = def->type;
		if (! (def->type & DEF_SAVEGLOBAL))
			continue;
		type &= ~DEF_SAVEGLOBAL;
		
		if (type != ev_string
				&& type != ev_float
				&& type != ev_entity)
			continue;
			
		name = pr_strings + def->s_name;
		fprintf (f, "\"%s\" ", name);
		fprintf (f, "\"%s\"\n", PR_UglyValueString (type, (eval_t *) &pr_globals[def->ofs]));
	}
	fprintf (f, "}\n");
}

///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	
///////////////////////////////////////////////////////////////////////////
void ED_ParseGlobals (char *data)
{
	char	keyname[64];
	ddef_t	*key;
	
	while (1)
	{
		// parse key
		data = COM_Parse (data);
		if (com_token[0] == '}')
			break;
		if (!data)
			Sys_Error ("ED_ParseEntity: EOF without closing brace");
			
		strcpy (keyname, com_token);
		
		// parse value
		data = COM_Parse (data);
		if (!data)
			Sys_Error ("ED_ParseEntity: EOF without closing brace");
			
		if (com_token[0] == '}')
			Sys_Error ("ED_ParseEntity: closing brace without data");
			
		key = ED_FindGlobal (keyname);
		if (!key)
		{
			Con_Printf ("'%s' is not a global\n", keyname);
			continue;
		}
		
		if (!ED_ParseEpair ( (void *) pr_globals, key, com_token))
			Host_Error ("ED_ParseGlobals: parse error");
	}
}

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


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	
///////////////////////////////////////////////////////////////////////////
char *ED_NewString (char *string)
{
	char	*_new, *new_p;
	int		i, l;
	
	l = strlen (string) + 1;
	_new = Hunk_Alloc (l);
	new_p = _new;
	
	for (i = 0 ; i < l ; i++)
	{
		if (string[i] == '\\' && i < l - 1)
		{
			i++;
			if (string[i] == 'n')
				*new_p++ = '\n';
			else
				*new_p++ = '\\';
		}
		else
			*new_p++ = string[i];
	}
	
	return _new;
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	Can parse either fields or globals
///	@return	false if error
///////////////////////////////////////////////////////////////////////////
bool	ED_ParseEpair (void *base, ddef_t *key, char *s)
{
	int		i;
	char	string[128];
	ddef_t	*def;
	char	*v, *w;
	void	*d;
	dfunction_t	*func;
	
	d = (void *) ( (int *) base + key->ofs);
	
	switch (key->type & ~DEF_SAVEGLOBAL)
	{
		case ev_string:
			* (string_t *) d = ED_NewString (s) - pr_strings;
			break;
			
		case ev_float:
			* (float *) d = atof (s);
			break;
			
		case ev_vector:
			strcpy (string, s);
			v = string;
			w = string;
			for (i = 0 ; i < 3 ; i++)
			{
				while (*v && *v != ' ')
					v++;
				*v = 0;
				( (float *) d) [i] = atof (w);
				w = v = v + 1;
			}
			break;
			
		case ev_entity:
			* (int *) d = EDICT_TO_PROG (EDICT_NUM (atoi (s)));
			break;
			
		case ev_field:
			def = ED_FindField (s);
			if (!def)
			{
				Con_Printf ("Can't find field %s\n", s);
				return false;
			}
			* (int *) d = G_INT (def->ofs);
			break;
			
		case ev_function:
			func = ED_FindFunction (s);
			if (!func)
			{
				Con_Printf ("Can't find function %s\n", s);
				return false;
			}
			* (func_t *) d = func - pr_functions;
			break;
			
		default:
			break;
	}
	return true;
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	Parses an edict out of the given string, returning the new
///			position ed should be a properly initialized empty edict.
///			Used for initial level load and for savegames.
///	@param	data	
///	@param	ent		edict to parse
///////////////////////////////////////////////////////////////////////////
char *ED_ParseEdict (char *data, edict_t *ent)
{
	ddef_t	*key;
	bool	anglehack;
	bool	init;
	char	keyname[256];
	int		n;
	
	init = false;
	
	// clear it
	if (ent != sv.edicts)	// hack
		memset (&ent->v, 0, progs->entityfields * 4);
		
	// go through all the dictionary pairs
	while (1)
	{
		// parse key
		data = COM_Parse (data);
		if (com_token[0] == '}')
			break;
		if (!data)
			Sys_Error ("ED_ParseEntity: EOF without closing brace");
			
		// anglehack is to allow QuakeEd to write single scalar angles
		// and allow them to be turned into vectors. (FIXME...)
		if (!strcmp (com_token, "angle"))
		{
			strcpy (com_token, "angles");
			anglehack = true;
		}
		else
			anglehack = false;
			
		// FIXME: change light to _light to get rid of this hack
		if (!strcmp (com_token, "light"))
			strcpy (com_token, "light_lev");	// hack for single light def
			
		strcpy (keyname, com_token);
		
		// another hack to fix heynames with trailing spaces
		n = strlen (keyname);
		while (n && keyname[n-1] == ' ')
		{
			keyname[n-1] = 0;
			n--;
		}
		
		// parse value
		data = COM_Parse (data);
		if (!data)
			Sys_Error ("ED_ParseEntity: EOF without closing brace");
			
		if (com_token[0] == '}')
			Sys_Error ("ED_ParseEntity: closing brace without data");
			
		init = true;
		
		// keynames with a leading underscore are used for utility comments,
		// and are immediately discarded by quake
		if (keyname[0] == '_')
			continue;
			
		key = ED_FindField (keyname);
		if (!key)
		{
			Con_Printf ("'%s' is not a field\n", keyname);
			continue;
		}
		
		if (anglehack)
		{
			char	temp[32];
			strcpy (temp, com_token);
			sprintf (com_token, "0 %s 0", temp);
		}
		
		if (!ED_ParseEpair ( (void *) &ent->v, key, com_token))
			Host_Error ("ED_ParseEdict: parse error");
	}
	
	if (!init)
		ent->free = true;
		
	return data;
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	The entities are directly placed in the array, rather than
///			allocated with ED_Alloc, because otherwise an error loading
///			the map would have entity number references out of order.
/// 
/// Creates a server's entity / program execution context by
/// parsing textual entity definitions out of an ent file.
/// 
/// Used for both fresh maps and savegame loads.  A fresh map would also need
/// to call ED_CallSpawnFunctions () to let the objects initialize themselves.
///////////////////////////////////////////////////////////////////////////
void ED_LoadFromFile (char *data)
{
	edict_t		*ent;
	int			inhibit;
	dfunction_t	*func;
	
	ent = NULL;
	inhibit = 0;
	pr_global_struct->time = sv.time;
	
	// parse ents
	while (1)
	{
		// parse the opening brace
		data = COM_Parse (data);
		if (!data)
			break;
		if (com_token[0] != '{')
			Sys_Error ("ED_LoadFromFile: found %s when expecting {", com_token);
			
		if (!ent)
			ent = EDICT_NUM (0);
		else
			ent = ED_Alloc ();

		data = ED_ParseEdict (data, ent);
		
		// remove things from different skill levels or deathmatch
		if (deathmatch.value)
		{
			if (((int) ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH))
			{
				ED_Free (ent);
				inhibit++;
				continue;
			}
		}
		else if ( (current_skill == 0 && ( (int) ent->v.spawnflags & SPAWNFLAG_NOT_EASY))
				  || (current_skill == 1 && ( (int) ent->v.spawnflags & SPAWNFLAG_NOT_MEDIUM))
				  || (current_skill >= 2 && ( (int) ent->v.spawnflags & SPAWNFLAG_NOT_HARD)))
		{
			ED_Free (ent);
			inhibit++;
			continue;
		}
		
		//
		// immediately call spawn function
		//
		if (!ent->v.classname)
		{
			Con_Printf ("No classname for:\n");
			ED_Print (ent);
			ED_Free (ent);
			continue;
		}
		
		// look for the spawn function
		func = ED_FindFunction (pr_strings + ent->v.classname);
		
		if (!func)
		{
			Con_Printf ("No spawn function for:\n");
			ED_Print (ent);
			ED_Free (ent);
			continue;
		}
		
		pr_global_struct->self = EDICT_TO_PROG (ent);
		PR_ExecuteProgram (func - pr_functions);
	}
	
	Con_DPrintf ("%i entities inhibited\n", inhibit);
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	Load progs.dat
///////////////////////////////////////////////////////////////////////////
void PR_LoadProgs (void)
{
	int		i;
	
	// flush the non-C variable lookup cache
	for (i = 0 ; i < GEFV_CACHESIZE ; i++)
		gefvCache[i].field[0] = 0;
		
	CRC_Init (&pr_crc);
	
	progs = (dprograms_t *) COM_LoadHunkFile ("progs.dat");
	if (!progs)
		Sys_Error ("PR_LoadProgs: couldn't load progs.dat");
	Con_DPrintf ("Programs occupy %iK.\n", com_filesize / 1024);
	
	for (i = 0 ; i < com_filesize ; i++)
		CRC_ProcessByte (&pr_crc, ( (byte *) progs) [i]);
		
	// byte swap the header
	for (i = 0 ; i < sizeof (*progs) / 4 ; i++)
		( (int *) progs) [i] = LittleLong ( ( (int *) progs) [i]);
		
	if (progs->version != PROG_VERSION)
		Sys_Error ("progs.dat has wrong version number (%i should be %i)", progs->version, PROG_VERSION);
	if (progs->crc != PROGHEADER_CRC)
		Sys_Error ("progs.dat system vars have been modified, progdefs.h is out of date");
		
	pr_functions = (dfunction_t *) ( (byte *) progs + progs->ofs_functions);
	pr_strings = (char *) progs + progs->ofs_strings;
	pr_globaldefs = (ddef_t *) ( (byte *) progs + progs->ofs_globaldefs);
	pr_fielddefs = (ddef_t *) ( (byte *) progs + progs->ofs_fielddefs);
	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;
	
	pr_edict_size = progs->entityfields * 4 + sizeof (edict_t) - sizeof (entvars_t);
	
	// 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);
	}
	
	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);
	}
	
	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);
	}
	
	for (i = 0 ; i < progs->numfielddefs ; i++)
	{
		pr_fielddefs[i].type = LittleShort (pr_fielddefs[i].type);
		if (pr_fielddefs[i].type & DEF_SAVEGLOBAL)
			Sys_Error ("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL");
		pr_fielddefs[i].ofs = LittleShort (pr_fielddefs[i].ofs);
		pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name);
	}
	
	for (i = 0 ; i < progs->numglobals ; i++)
		( (int *) pr_globals) [i] = LittleLong ( ( (int *) pr_globals) [i]);
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	Program (?) Initialization
///////////////////////////////////////////////////////////////////////////
void PR_Init (void)
{
	Cmd_AddCommand ("edict", ED_PrintEdict_f);
	Cmd_AddCommand ("edicts", ED_PrintEdicts);
	Cmd_AddCommand ("edictcount", ED_Count);
	Cmd_AddCommand ("profile", PR_Profile_f);
	Cvar_RegisterVariable (&nomonsters);
	Cvar_RegisterVariable (&gamecfg);
	Cvar_RegisterVariable (&scratch1);
	Cvar_RegisterVariable (&scratch2);
	Cvar_RegisterVariable (&scratch3);
	Cvar_RegisterVariable (&scratch4);
	Cvar_RegisterVariable (&savedgamecfg);
	Cvar_RegisterVariable (&saved1);
	Cvar_RegisterVariable (&saved2);
	Cvar_RegisterVariable (&saved3);
	Cvar_RegisterVariable (&saved4);
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	
/// @param	n
/// @return	
///////////////////////////////////////////////////////////////////////////
edict_t *EDICT_NUM (int n)
{
	if (n < 0 || n >= sv.max_edicts)
		Sys_Error ("EDICT_NUM: bad number %i", n);
	return (edict_t *) ( (byte *) sv.edicts + (n) * pr_edict_size);
}


///////////////////////////////////////////////////////////////////////////
/// @ingroup edict_functions
/// @brief	
///////////////////////////////////////////////////////////////////////////
int NUM_FOR_EDICT (edict_t *e)
{
	int		b;
	
	b = (byte *) e - (byte *) sv.edicts;
	b = b / pr_edict_size;
	
	if (b < 0 || b >= sv.num_edicts)
		Sys_Error ("NUM_FOR_EDICT: bad pointer");
	return b;
}
