]> de.git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
Adding the new vm
authorblack <black@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 6 Oct 2003 18:52:56 +0000 (18:52 +0000)
committerblack <black@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 6 Oct 2003 18:52:56 +0000 (18:52 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@3541 d7cf8633-e32d-0410-b094-e92efae38249

clprogdefs.h [new file with mode: 0644]
mprogdefs.h [new file with mode: 0644]
progsvm.h [new file with mode: 0644]
prvm_cmds.c [new file with mode: 0644]
prvm_edict.c [new file with mode: 0644]
prvm_exec.c [new file with mode: 0644]
prvm_execprogram.h [new file with mode: 0644]

diff --git a/clprogdefs.h b/clprogdefs.h
new file mode 100644 (file)
index 0000000..c84f263
--- /dev/null
@@ -0,0 +1,12 @@
+/* file generated by qcc, do not modify */
+
+typedef struct
+{
+       int     pad[28];
+} cl_globalvars_t;
+
+/*typedef struct
+{
+} cl_entvars_t;*/
+
+#define CL_PROGHEADER_CRC 12923
diff --git a/mprogdefs.h b/mprogdefs.h
new file mode 100644 (file)
index 0000000..d06e270
--- /dev/null
@@ -0,0 +1,15 @@
+
+/* file generated by qcc, do not modify */
+
+typedef struct
+{      
+       int     pad[28];
+       int     self;
+} m_globalvars_t;
+
+typedef struct
+{
+       string_t        classname;
+} m_entvars_t;
+
+#define M_PROGHEADER_CRC 40313
diff --git a/progsvm.h b/progsvm.h
new file mode 100644 (file)
index 0000000..54e8640
--- /dev/null
+++ b/progsvm.h
@@ -0,0 +1,462 @@
+/*
+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.
+
+*/
+/*
+This is a try to make the vm more generic, it is mainly based on the progs.h file.
+For the license refer to progs.h.
+
+Generic means, less as possible hard-coded links with the other parts of the engine.
+This means no edict_engineprivate struct usage, etc.
+The code uses void pointers instead.
+*/
+
+#ifndef PROGSVM_H
+#define PROGSVM_H
+
+#include "pr_comp.h"                   // defs shared with qcc
+//#include "progdefs.h"                        // generated by program cdefs
+
+/*
+typedef union vm_eval_s
+{
+       string_t                string;
+       float                   _float;
+       float                   vector[3];
+       func_t                  function;
+       int                             ivector[3];
+       int                             _int;
+       int                             edict;
+} vm_eval_t;
+
+typedef struct vm_link_s
+{
+       int entitynumber;
+       struct link_s   *prev, *next;
+} vm_link_t;
+
+#define ENTITYGRIDAREAS 16
+
+typedef struct vm_edict_engineprivate_s
+{
+       // true if this edict is unused
+       qboolean free;
+       // sv.time when the object was freed (to prevent early reuse which could
+       // mess up client interpolation or obscure severe QuakeC bugs)
+       float freetime;
+
+       // physics grid areas this edict is linked into
+       link_t areagrid[ENTITYGRIDAREAS];
+       // since the areagrid can have multiple references to one entity,
+       // we should avoid extensive checking on entities already encountered
+       int areagridmarknumber;
+
+       // old entity protocol, not used
+#ifdef QUAKEENTITIES
+       // baseline values
+       entity_state_t baseline;
+       // LordHavoc: previous frame
+       entity_state_t deltabaseline;
+#endif
+
+       // LordHavoc: gross hack to make floating items still work
+       int suspendedinairflag;
+       // used by PushMove to keep track of where objects were before they were
+       // moved, in case they need to be moved back
+       vec3_t moved_from;
+       vec3_t moved_fromangles;
+}
+vm_edict_engineprivate_t;
+
+// the entire server entity structure
+// NOTE: keep this small!  priv and v are dynamic but this struct is not!
+typedef struct vm_edict_s
+{
+       // engine-private fields (stored in dynamically resized array)
+       edict_engineprivate_t *e;
+       // QuakeC fields (stored in dynamically resized array)
+       entvars_t *v;
+}
+vm_edict_t;
+*/
+
+/*// LordHavoc: in an effort to eliminate time wasted on GetEdictFieldValue...  see pr_edict.c for the functions which use these.
+extern int eval_gravity;
+extern int eval_button3;
+extern int eval_button4;
+extern int eval_button5;
+extern int eval_button6;
+extern int eval_button7;
+extern int eval_button8;
+extern int eval_glow_size;
+extern int eval_glow_trail;
+extern int eval_glow_color;
+extern int eval_items2;
+extern int eval_scale;
+extern int eval_alpha;
+extern int eval_renderamt; // HalfLife support
+extern int eval_rendermode; // HalfLife support
+extern int eval_fullbright;
+extern int eval_ammo_shells1;
+extern int eval_ammo_nails1;
+extern int eval_ammo_lava_nails;
+extern int eval_ammo_rockets1;
+extern int eval_ammo_multi_rockets;
+extern int eval_ammo_cells1;
+extern int eval_ammo_plasma;
+extern int eval_idealpitch;
+extern int eval_pitch_speed;
+extern int eval_viewmodelforclient;
+extern int eval_nodrawtoclient;
+extern int eval_exteriormodeltoclient;
+extern int eval_drawonlytoclient;
+extern int eval_ping;
+extern int eval_movement;
+extern int eval_pmodel;
+extern int eval_punchvector;
+extern int eval_viewzoom;
+extern int eval_clientcolors;
+extern int eval_tag_entity;
+extern int eval_tag_index;*/
+
+typedef struct prvm_stack_s
+{
+       int                             s;
+       mfunction_t             *f;
+} prvm_stack_t;
+
+
+typedef union prvm_eval_s
+{
+       string_t                string;
+       float                   _float;
+       float                   vector[3];
+       func_t                  function;
+       int                             ivector[3];
+       int                             _int;
+       int                             edict;
+} prvm_eval_t;
+
+/*typedef struct prvm_link_s
+{
+       int entitynumber;
+       struct link_s   *prev, *next;
+} prvm_link_t;*/
+
+// AK: I dont call it engine private cause it doesnt really belongs to the engine
+//     it belongs to prvm.
+typedef struct prvm_edict_private_s
+{
+       qboolean free;
+       float freetime;
+} prvm_edict_private_t;
+
+typedef struct prvm_edict_s
+{
+       // engine-private fields (stored in dynamically resized array)
+       //edict_engineprivate_t *e;
+       prvm_edict_private_t *e;
+       // QuakeC fields (stored in dynamically resized array)
+       //entvars_t *v;
+       void *v;
+} prvm_edict_t;
+       
+#define PRVM_GETEDICTFIELDVALUE(ed, fieldoffset) (fieldoffset ? (prvm_eval_t *)((qbyte *)ed->v + fieldoffset) : NULL)
+
+/*// this struct is the basic requirement for a qc prog
+typedef struct prvm_pr_globalvars_s
+{
+       int pad[28];
+} prvm_pr_globalvars_t;
+*/
+/*
+extern mfunction_t *SV_PlayerPhysicsQC;
+extern mfunction_t *EndFrameQC;
+//KrimZon - SERVER COMMANDS IN QUAKEC
+extern mfunction_t *SV_ParseClientCommandQC;
+*/
+//============================================================================
+/*
+typedef struct prvm_builtin_mem_s
+{
+       void (*init)(void);
+       void (*deinit)(void);
+
+       void *mem;
+} prvm_builtin_mem_t;
+*/
+
+//============================================================================
+/*
+#define PRVM_FE_NEXTHINK       2
+#define PRVM_FE_THINK          4
+#define PRVM_FE_FRAME          8
+*/
+#define PRVM_FE_CLASSNAME   32
+#define PRVM_GE_TIME           16
+#define PRVM_OP_STATE          1
+
+#define        PRVM_MAX_STACK_DEPTH            256
+#define        PRVM_LOCALSTACK_SIZE            2048
+
+typedef void (*prvm_builtin_t) (void);
+
+// NOTE: external code has to create and free the mempools but everything else is done by prvm !
+typedef struct vm_prog_s
+{
+       dprograms_t                     *progs;
+       mfunction_t                     *functions;
+       char                            *strings;
+       ddef_t                          *fielddefs;
+       ddef_t                          *globaldefs;
+       dstatement_t            *statements;
+       //prvm_pr_globalvars_t*pr_global_struct; 
+       float                           *globals;                       // same as pr_global_struct
+       int                                     edict_size;                     // in bytes
+       int                                     edictareasize;          // LordHavoc: in bytes (for bound checking)
+       
+       mempool_t                       *progs_mempool;
+       mempool_t                       *edictstring_mempool;
+
+       prvm_builtin_t          *builtins;
+       int                                     numbuiltins;
+       
+       int                                     argc;
+       
+       int                                     trace;
+       mfunction_t                     *xfunction;
+       int                                     xstatement;
+
+       prvm_stack_t            stack[PRVM_MAX_STACK_DEPTH];
+       int                                     depth;
+       
+       int                                     localstack[PRVM_LOCALSTACK_SIZE];
+       int                                     localstack_used;
+
+       unsigned short          crc;    
+
+       //============================================================================
+       // until this point everything also exists (with the pr_ prefix) in the old vm
+
+       // copies of some vars that were former read from sv
+       int                                     num_edicts;
+       int                                     max_edicts;
+
+       prvm_edict_t            *edicts;
+       void                            *edictsfields;
+       void                            *edictprivate;
+
+       // size of the engine private struct
+       int                                     edictprivate_size;
+
+       // instead of sv_editcts_mempool
+       mempool_t                       *edicts_mempool;
+       
+       // has to be updated every frame - so the vm time is up-to-date
+       double                          time;
+
+       // name of the prog, e.g. "Server", "Client" or "Menu" (used in for text output)
+       char                            *name;
+
+       // flag - used to store general flags like PRVM_GE_SELF, etc.
+       int                                     flag;
+
+       char                            *extensionstring;
+
+       // used to indicate whether a prog is loaded
+       qboolean                        loaded;
+
+       // used instead of the constant MAX_EDICTS
+       int                                     limit_edicts;
+
+//     prvm_builtin_mem_t  *mem_list;
+
+// now passes as parameter of PRVM_LoadProgs
+//     char                            **required_func; 
+//     int                                     numrequiredfunc;
+
+       //============================================================================
+       
+       ddef_t                          *self; // if self != 0 then there is a global self
+       
+       //============================================================================
+       // function pointers
+       
+       void                            (*begin_increase_edicts)(void);  // used by PRVM_MEM_Increase_Edicts
+       void                            (*end_increase_edicts)(void); 
+       
+       void                            (*init_edict)(int num);         // used by PRVM_ED_ClearEdict
+       void                            (*free_edict)(prvm_edict_t *ed); // used by PRVM_ED_Free
+
+       void                            (*count_edicts)(void);  // used by PRVM_ED_Count_f
+
+       qboolean                        (*load_edict)(prvm_edict_t *ent); // used by PRVM_ED_LoadFromFile
+
+       void                            (*init_cmd)(void);      // used by PRVM_InitProg
+       void                            (*reset_cmd)(void); // used by PRVM_ResetProg
+
+       void                            (*error_cmd)(void);
+
+} prvm_prog_t;
+
+
+extern prvm_prog_t * prog;
+
+#define PRVM_MAXPROGS 3
+#define PRVM_SERVERPROG 0 // actually not used at the moment
+#define PRVM_CLIENTPROG 1
+#define PRVM_MENUPROG  2
+
+extern prvm_prog_t prvm_prog_list[PRVM_MAXPROGS];
+
+//============================================================================
+// prvm_cmds part
+
+extern prvm_builtin_t vm_sv_builtins[];  
+extern prvm_builtin_t vm_cl_builtins[];  
+extern prvm_builtin_t vm_m_builtins[];  
+
+extern const int vm_sv_numbuiltins;
+extern const int vm_cl_numbuiltins;
+extern const int vm_m_numbuiltins;
+
+extern char * vm_sv_extensions; 
+extern char * vm_cl_extensions; 
+extern char * vm_m_extensions; 
+
+void VM_SV_Cmd_Init(void); 
+void VM_SV_Cmd_Reset(void); 
+
+void VM_CL_Cmd_Init(void); 
+void VM_CL_Cmd_Reset(void); 
+
+void VM_M_Cmd_Init(void); 
+void VM_M_Cmd_Reset(void);
+
+void VM_Cmd_Init(void);
+void VM_Cmd_Reset(void);
+//============================================================================
+
+void PRVM_Init (void);
+
+void PRVM_ExecuteProgram (func_t fnum, const char *errormessage);
+void PRVM_LoadProgs (const char *filename, int numrequiredfunc, char **required_func);
+
+void PRVM_Profile_f (void);
+
+void PRVM_CrashAll (void);
+void PRVM_Crash (void);
+
+prvm_edict_t *PRVM_ED_Alloc (void);
+void PRVM_ED_Free (prvm_edict_t *ed);
+void PRVM_ED_ClearEdict (prvm_edict_t *e);
+
+char *PRVM_ED_NewString (const char *string);
+// returns a copy of the string allocated from the server's string heap
+
+void PRVM_ED_Print (prvm_edict_t *ed);
+void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed);
+const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent);
+
+void PRVM_ED_WriteGlobals (qfile_t *f);
+void PRVM_ED_ParseGlobals (const char *data);
+
+void PRVM_ED_LoadFromFile (const char *data);
+
+prvm_edict_t *PRVM_EDICT_NUM_ERROR(int n, char *filename, int fileline);
+#define        PRVM_EDICT_NUM(n) (((n) >= 0 && (n) < prog->max_edicts) ? prog->edicts + (n) : PRVM_EDICT_NUM_ERROR(n, __FILE__, __LINE__))
+
+//int NUM_FOR_EDICT_ERROR(edict_t *e);
+#define PRVM_NUM_FOR_EDICT(e) ((prvm_edict_t *)(e) - prog->edicts)
+//int NUM_FOR_EDICT(edict_t *e);
+
+#define        PRVM_NEXT_EDICT(e) ((e) + 1)
+
+#define PRVM_EDICT_TO_PROG(e) (PRVM_NUM_FOR_EDICT(e))
+//int PRVM_EDICT_TO_PROG(edict_t *e);
+#define PRVM_PROG_TO_EDICT(n) (PRVM_EDICT_NUM(n))
+//edict_t *PRVM_PROG_TO_EDICT(int n);
+
+//============================================================================
+
+#define        PRVM_G_FLOAT(o) (prog->globals[o])
+#define        PRVM_G_INT(o) (*(int *)&prog->globals[o])
+#define        PRVM_G_EDICT(o) (PRVM_PROG_TO_EDICT(*(int *)&prog->globals[o]))
+#define PRVM_G_EDICTNUM(o) PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(o))
+#define        PRVM_G_VECTOR(o) (&prog->globals[o])
+#define        PRVM_G_STRING(o) (PRVM_GetString(*(string_t *)&prog->globals[o]))
+//#define      PRVM_G_FUNCTION(o) (*(func_t *)&prog->globals[o])
+
+// FIXME: make these go away?
+#define        PRVM_E_FLOAT(e,o) (((float*)e->v)[o])
+//#define      PRVM_E_INT(e,o) (((int*)e->v)[o])
+//#define      PRVM_E_VECTOR(e,o) (&((float*)e->v)[o])
+#define        PRVM_E_STRING(e,o) (PRVM_GetString(*(string_t *)&((float*)e->v)[o]))
+
+extern int             prvm_type_size[8]; // for consistency : I think a goal of this sub-project is to
+// make the new vm mostly independent from the old one, thus if it's necessary, I copy everything
+
+void PRVM_Init_Exec(void);
+
+void PRVM_ED_PrintEdicts (void);
+void PRVM_ED_PrintNum (int ent);
+
+#define PRVM_GetString(num) (prog->strings + num)
+#define PRVM_SetString(s) ((int) (s - prog->strings))
+
+//============================================================================
+
+// used as replacement for a prog stack
+#if 1
+       #define PRVM_Begin  
+       #define PRVM_End        prog = 0;
+#else
+       #define PRVM_Begin      { prvm_prog_t *_oldprog_ = prog
+       #define PRVM_End        if(_oldprog_ != 0) Con_Print("Stack used !\n"); prog = _oldprog_;}
+#endif 
+
+#define PRVM_NAME      (prog->name ? prog->name : "Unknown prog name")
+
+// helper macro to make function pointer calls easier
+#define PRVM_GCALL(func)       if(prog->func) prog->func
+
+/*#define PRVM_ERROR   if(!prog->error_cmd)  \
+                                               Sys_Error("PRVM: No error_cmd specified !\n");   \
+                                       else \
+                                               prog->error_cmd*/
+
+#define PRVM_ERROR             PRVM_GCALL(error_cmd)(),Host_Error
+
+
+
+// other prog handling functions
+qboolean PRVM_SetProgFromString(const char *str);
+void    PRVM_SetProg(int prognr);
+
+void    PRVM_InitProg(int prognr);
+void    PRVM_ResetProg(void);
+
+qboolean PRVM_ProgLoaded(int prognr);
+
+int             PRVM_GetProgNr(void);
+       
+
+// TODO: fill in the params
+//void PRVM_Create();
+
+#endif
diff --git a/prvm_cmds.c b/prvm_cmds.c
new file mode 100644 (file)
index 0000000..cd780ce
--- /dev/null
@@ -0,0 +1,141 @@
+// AK
+// Basically every vm builtin cmd should be in here.
+// All 3 builtin list and extension lists can be found here
+
+#include "quakedef.h"
+#include "progdefs.h"
+#include "clprogdefs.h"
+#include "mprogdefs.h"
+
+//============================================================================
+// nice helper macros
+
+#define VM_SAFEPARMCOUNT(p,f)  if(prog->argc != p) PRVM_ERROR(#f "wrong parameter count (" #p "expected ) !\n") 
+
+#define e10 0,0,0,0,0,0,0,0,0,0
+#define e100 e10,e10,e10,e10,e10,e10,e10,e10,e10,e10
+#define e1000 e100,e100,e100,e100,e100,e100,e100,e100,e100,e100
+
+//============================================================================
+// Common 
+
+void VM_Cmd_Init(void)
+{
+}
+
+void VM_Cmd_Reset(void)
+{
+}
+
+//============================================================================
+// Server 
+
+char *vm_sv_extensions = 
+""; 
+
+prvm_builtin_t vm_sv_builtins[] = {
+0  // to be consistent with the old vm
+};
+
+const int vm_sv_numbuiltins = sizeof(vm_sv_builtins) / sizeof(prvm_builtin_t);
+
+void VM_SV_Cmd_Init(void)
+{
+}
+
+void VM_SV_Cmd_Reset(void)
+{
+}
+
+//============================================================================
+// Client 
+
+char *vm_cl_extensions = 
+"";
+
+prvm_builtin_t vm_cl_builtins[] = {
+0  // to be consistent with the old vm
+};
+
+const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t);
+
+void VM_CL_Cmd_Init(void)
+{
+}
+
+void VM_CL_Cmd_Reset(void)
+{
+}
+
+//============================================================================
+// Menu 
+
+char *vm_m_extensions = 
+"";
+
+// void setkeydest(float dest)
+void VM_M_SetKeyDest(void)
+{
+       VM_SAFEPARMCOUNT(1,VM_M_SetKeyDest);
+
+       switch((int)PRVM_G_FLOAT(OFS_PARM0))
+       {
+       case 0:
+               // key_game
+               key_dest = key_game;
+               break;
+       case 2:
+               // key_menu
+               key_dest = key_menu;
+               break;
+       case 1:
+               // key_message
+               // key_dest = key_message
+               // break;
+       default:
+               PRVM_ERROR("VM_M_SetKeyDest: wrong destination %i !\n",prog->globals[OFS_PARM0]);
+       }
+
+       return;
+}
+
+// float getkeydest(void)
+void VM_M_GetKeyDest(void)
+{
+       VM_SAFEPARMCOUNT(0,VM_M_GetKeyDest);
+
+       // key_game = 0, key_message = 1, key_menu = 2, unknown = 3
+       switch(key_dest)
+       {
+       case key_game:
+               PRVM_G_FLOAT(OFS_RETURN) = 0;
+               break;
+       case key_menu:
+               PRVM_G_FLOAT(OFS_RETURN) = 2;
+               break;
+       case key_message:
+               // not supported
+               // PRVM_G_FLOAT(OFS_RETURN) = 1;
+               // break;
+       default:
+               PRVM_G_FLOAT(OFS_RETURN) = 3;
+       }               
+}
+
+prvm_builtin_t vm_m_builtins[] = {
+0, // to be consistent with the old vm
+e1000,
+VM_M_SetKeyDest,
+VM_M_GetKeyDest
+};
+
+const int vm_m_numbuiltins = sizeof(vm_m_builtins) / sizeof(prvm_builtin_t);
+
+void VM_M_Cmd_Init(void)
+{
+}
+
+void VM_M_Cmd_Reset(void)
+{
+}
+
diff --git a/prvm_edict.c b/prvm_edict.c
new file mode 100644 (file)
index 0000000..ec2f2b1
--- /dev/null
@@ -0,0 +1,1700 @@
+/*
+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.
+
+*/
+// AK new vm 
+
+#include "quakedef.h"
+
+prvm_prog_t *prog;
+
+static prvm_prog_t prog_list[PRVM_MAXPROGS];
+
+int            prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
+
+ddef_t *PRVM_ED_FieldAtOfs(int ofs);
+qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s);
+
+// LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
+cvar_t prvm_boundscheck = {0, "prvm_boundscheck", "1"};
+// LordHavoc: prints every opcode as it executes - warning: this is significant spew
+cvar_t prvm_traceqc = {0, "prvm_traceqc", "0"};
+
+ddef_t *PRVM_ED_FindField (const char *name);
+mfunction_t *PRVM_ED_FindFunction (const char *name);
+
+//============================================================================
+// mempool handling
+
+/*
+===============
+PRVM_MEM_Alloc
+===============
+*/
+void PRVM_MEM_Alloc()
+{
+       int i;
+
+       // check bound of max_edicts
+       prog->max_edicts = min(prog->max_edicts,prog->limit_edicts);
+
+       // edictprivate_size has to be min as big prvm_edict_private_t
+       prog->edictprivate_size = max(prog->edictprivate_size,sizeof(prvm_edict_private_t)); 
+
+       // alloc edicts
+       prog->edicts = Mem_Alloc(prog->edicts_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
+       
+       // alloc edict private space
+       prog->edictprivate = Mem_Alloc(prog->edicts_mempool, prog->max_edicts * prog->edictprivate_size);
+       
+       // alloc edict fields
+       prog->edictsfields = Mem_Alloc(prog->edicts_mempool, prog->max_edicts * prog->edict_size);
+
+       // set edict pointers
+       for(i = 0; i < prog->max_edicts; i++)
+       {
+               prog->edicts[i].e = (prvm_edict_private_t *)((qbyte  *)prog->edictprivate + i * prog->edictprivate_size);
+               prog->edicts[i].v = (void*)((qbyte *)prog->edictsfields + i * prog->edict_size);
+       }
+}
+
+/*
+===============
+PRVM_MEM_IncreaseEdicts
+===============
+*/
+void PRVM_MEM_IncreaseEdicts()
+{
+       int             i;
+       int             oldmaxedicts = prog->max_edicts; 
+       void    *oldedictsfields = prog->edictsfields;
+       void    *oldedictprivate = prog->edictprivate;
+       
+       if(prog->max_edicts >= prog->limit_edicts)
+               return;
+       
+       PRVM_GCALL(begin_increase_edicts)();
+
+       // increase edicts
+       prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
+
+       prog->edictsfields = Mem_Alloc(prog->edicts_mempool, prog->max_edicts * prog->edict_size);
+       prog->edictprivate = Mem_Alloc(prog->edicts_mempool, prog->max_edicts * prog->edictprivate_size);
+
+       memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
+       memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
+
+       //set e and v pointers
+       for(i = 0; i < prog->max_edicts; i++)
+       {
+               prog->edicts[i].e = (prvm_edict_private_t *)((qbyte  *)prog->edictprivate + i * prog->edictprivate_size);
+               prog->edicts[i].v = (void*)((qbyte *)prog->edictsfields + i * prog->edict_size);
+       }
+
+       PRVM_GCALL(end_increase_edicts)();
+
+       Mem_Free(oldedictsfields);
+       Mem_Free(oldedictprivate);
+}
+
+//============================================================================
+// normal prvm
+
+int PRVM_ED_FindFieldOffset(const char *field)
+{
+       ddef_t *d;
+       d = PRVM_ED_FindField(field);
+       if (!d)
+               return 0;
+       return d->ofs*4;
+}
+
+qboolean PRVM_ProgLoaded(int prognr)
+{
+       if(prognr < 0 || prognr >= PRVM_MAXPROGS)
+               return FALSE;
+
+       return (prog_list[prognr].loaded ? TRUE : FALSE);
+}
+
+/*
+=================
+PRVM_SetProgFromString
+=================
+*/
+// perhaps add a return value when the str doesnt exist
+qboolean PRVM_SetProgFromString(const char *str)
+{
+       int i = 0;
+       for(; i < PRVM_MAXPROGS ; i++)
+               if(prog_list[i].name && !strcmp(prog_list[i].name,str))
+               {
+                       if(prog_list[i].loaded)
+                       {
+                               prog = &prog_list[i];
+                               return TRUE;
+                       }
+                       else
+                       {
+                               Con_Printf("%s not loaded !\n",PRVM_NAME);
+                               return FALSE;
+                       }
+               }
+
+       Con_Printf("Invalid program name %s !\n", str);
+       return FALSE;
+}
+
+/*
+=================
+PRVM_SetProg
+=================
+*/
+void PRVM_SetProg(int prognr)
+{
+       if(prognr && prognr < PRVM_MAXPROGS)
+       {       
+               if(prog_list[prognr].loaded)
+                       prog = &prog_list[prognr];
+               else
+                       PRVM_ERROR("%i(%s) not loaded !\n", prognr, PRVM_NAME);
+               return;
+       }
+       PRVM_ERROR("Invalid program number %i\n", prognr);
+}
+
+/*
+=================
+PRVM_ED_ClearEdict
+
+Sets everything to NULL
+=================
+*/
+void PRVM_ED_ClearEdict (prvm_edict_t *e)
+{
+       int num;
+       memset (e->v, 0, prog->progs->entityfields * 4);
+       e->e->free = false;
+       // LordHavoc: for consistency set these here
+       num = PRVM_NUM_FOR_EDICT(e) - 1;
+
+       // AK: Let the init_edict function determine if something needs to be initialized
+       PRVM_GCALL(init_edict)(num);    
+}
+
+/*
+=================
+PRVM_ED_Alloc
+
+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.
+=================
+*/
+prvm_edict_t *PRVM_ED_Alloc (void)
+{
+       int                     i;
+       prvm_edict_t            *e;
+
+       // the client qc dont need maxclients
+       // thus it doesnt need to use svs.maxclients
+       // AK: changed i=svs.maxclients+1
+       for (i = 0;i < prog->num_edicts;i++)
+       {
+               e = PRVM_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->e->free && ( e->e->freetime < 2 || prog->time - e->e->freetime > 0.5 ) )
+               {
+                       PRVM_ED_ClearEdict (e);
+                       return e;
+               }
+       }
+
+       if (i == MAX_EDICTS)
+               PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
+
+       prog->num_edicts++;
+       if (prog->num_edicts >= prog->max_edicts)
+               PRVM_MEM_IncreaseEdicts();
+
+       e = PRVM_EDICT_NUM(i);
+       PRVM_ED_ClearEdict (e);
+
+       return e;
+}
+
+/*
+=================
+PRVM_ED_Free
+
+Marks the edict as free
+FIXME: walk all entities and NULL out references to this entity
+=================
+*/
+void PRVM_ED_Free (prvm_edict_t *ed)
+{
+       PRVM_GCALL(free_edict)(ed);
+
+       ed->e->free = true;
+       ed->e->freetime = prog->time;
+}
+
+//===========================================================================
+
+/*
+============
+PRVM_ED_GlobalAtOfs
+============
+*/
+ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
+{
+       ddef_t          *def;
+       int                     i;
+
+       for (i=0 ; i<prog->progs->numglobaldefs ; i++)
+       {
+               def = &prog->globaldefs[i];
+               if (def->ofs == ofs)
+                       return def;
+       }
+       return NULL;
+}
+
+/*
+============
+PRVM_ED_FieldAtOfs
+============
+*/
+ddef_t *PRVM_ED_FieldAtOfs (int ofs)
+{
+       ddef_t          *def;
+       int                     i;
+
+       for (i=0 ; i<prog->progs->numfielddefs ; i++)
+       {
+               def = &prog->fielddefs[i];
+               if (def->ofs == ofs)
+                       return def;
+       }
+       return NULL;
+}
+
+/*
+============
+PRVM_ED_FindField
+============
+*/
+ddef_t *PRVM_ED_FindField (const char *name)
+{
+       ddef_t *def;
+       int i;
+
+       for (i=0 ; i<prog->progs->numfielddefs ; i++)
+       {
+               def = &prog->fielddefs[i];
+               if (!strcmp(PRVM_GetString(def->s_name), name))
+                       return def;
+       }
+       return NULL;
+}
+
+/*
+============
+PRVM_ED_FindGlobal
+============
+*/
+ddef_t *PRVM_ED_FindGlobal (const char *name)
+{
+       ddef_t *def;
+       int i;
+
+       for (i=0 ; i<prog->progs->numglobaldefs ; i++)
+       {
+               def = &prog->globaldefs[i];
+               if (!strcmp(PRVM_GetString(def->s_name), name))
+                       return def;
+       }
+       return NULL;
+}
+
+
+/*
+============
+PRVM_ED_FindFunction
+============
+*/
+mfunction_t *PRVM_ED_FindFunction (const char *name)
+{
+       mfunction_t             *func;
+       int                             i;
+
+       for (i=0 ; i<prog->progs->numfunctions ; i++)
+       {
+               func = &prog->functions[i];
+               if (!strcmp(PRVM_GetString(func->s_name), name))
+                       return func;
+       }
+       return NULL;
+}
+
+
+/*
+============
+PRVM_ValueString
+
+Returns a string describing *data in a type specific manner
+=============
+*/
+char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
+{
+       static char line[1024]; // LordHavoc: enlarged a bit (was 256)
+       ddef_t *def;
+       mfunction_t *f;
+       int n;
+
+       type &= ~DEF_SAVEGLOBAL;
+
+       switch (type)
+       {
+       case ev_string:
+               sprintf (line, "%s", PRVM_GetString(val->string));
+               break;
+       case ev_entity:
+               n = val->edict;
+               if (n < 0 || n >= MAX_EDICTS)
+                       sprintf (line, "entity %i (invalid!)", n);
+               else
+                       sprintf (line, "entity %i", n);
+               break;
+       case ev_function:
+               f = prog->functions + val->function;
+               sprintf (line, "%s()", PRVM_GetString(f->s_name));
+               break;
+       case ev_field:
+               def = PRVM_ED_FieldAtOfs ( val->_int );
+               sprintf (line, ".%s", PRVM_GetString(def->s_name));
+               break;
+       case ev_void:
+               sprintf (line, "void");
+               break;
+       case ev_float:
+               // LordHavoc: changed from %5.1f to %10.4f
+               sprintf (line, "%10.4f", val->_float);
+               break;
+       case ev_vector:
+               // LordHavoc: changed from %5.1f to %10.4f
+               sprintf (line, "'%10.4f %10.4f %10.4f'", 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;
+}
+
+/*
+============
+PRVM_UglyValueString
+
+Returns a string describing *data in a type specific manner
+Easier to parse than PR_ValueString
+=============
+*/
+char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
+{
+       static char line[4096];
+       int i;
+       char *s;
+       ddef_t *def;
+       mfunction_t *f;
+
+       type &= ~DEF_SAVEGLOBAL;
+
+       switch (type)
+       {
+       case ev_string:
+               // Parse the string a bit to turn special characters
+               // (like newline, specifically) into escape codes,
+               // this fixes saving games from various mods
+               sprintf (line, "%s", PRVM_GetString(val->string));
+               for (i = 0;i < (int)sizeof(line) - 2 && *s;)
+               {
+                       if (*s == '\n')
+                       {
+                               line[i++] = '\\';
+                               line[i++] = 'n';
+                       }
+                       else if (*s == '\r')
+                       {
+                               line[i++] = '\\';
+                               line[i++] = 'r';
+                       }
+                       else
+                               line[i++] = *s;
+                       s++;
+               }
+               line[i] = '\0';
+               
+               break;
+       case ev_entity:
+               sprintf (line, "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
+               break;
+       case ev_function:
+               f = pr_functions + val->function;
+               sprintf (line, "%s", PRVM_GetString(f->s_name));
+               break;
+       case ev_field:
+               def = PRVM_ED_FieldAtOfs ( val->_int );
+               s = PRVM_GetString(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;
+}
+
+/*
+============
+PRVM_GlobalString
+
+Returns a string with a description and the contents of a global,
+padded to 20 field width
+============
+*/
+char *PRVM_GlobalString (int ofs)
+{
+       char    *s;
+       int             i;
+       ddef_t  *def;
+       void    *val;
+       static char     line[128];
+
+       val = (void *)&prog->globals[ofs];
+       def = PRVM_ED_GlobalAtOfs(ofs);
+       if (!def)
+               sprintf (line,"%i(?)", ofs);
+       else
+       {
+               s = PRVM_ValueString (def->type, val);
+               sprintf (line,"%i(%s)%s", ofs, PRVM_GetString(def->s_name), s);
+       }
+
+       i = strlen(line);
+       for ( ; i<20 ; i++)
+               strcat (line," ");
+       strcat (line," ");
+
+       return line;
+}
+
+char *PRVM_GlobalStringNoContents (int ofs)
+{
+       int             i;
+       ddef_t  *def;
+       static char     line[128];
+
+       def = PRVM_ED_GlobalAtOfs(ofs);
+       if (!def)
+               sprintf (line,"%i(?)", ofs);
+       else
+               sprintf (line,"%i(%s)", ofs, PRVM_GetString(def->s_name));
+
+       i = strlen(line);
+       for ( ; i<20 ; i++)
+               strcat (line," ");
+       strcat (line," ");
+
+       return line;
+}
+
+
+/*
+=============
+PRVM_ED_Print
+
+For debugging
+=============
+*/
+// LordHavoc: optimized this to print out much more quickly (tempstring)
+// LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
+void PRVM_ED_Print (prvm_edict_t *ed)
+{
+       int             l;
+       ddef_t  *d;
+       int             *v;
+       int             i, j;
+       char    *name;
+       int             type;
+       char    tempstring[8192], tempstring2[260]; // temporary string buffers
+
+       if (ed->e->free)
+       {
+               Con_Printf ("%s: FREE\n",PRVM_NAME);
+               return;
+       }
+
+       tempstring[0] = 0;
+       sprintf(tempstring, "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
+       for (i=1 ; i<prog->progs->numfielddefs ; i++)
+       {
+               d = &prog->fielddefs[i];
+               name = PRVM_GetString(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<prvm_type_size[type] ; j++)
+                       if (v[j])
+                               break;
+               if (j == prvm_type_size[type])
+                       continue;
+
+               if (strlen(name) > 256)
+               {
+                       strncpy(tempstring2, name, 256);
+                       tempstring2[256] = tempstring2[257] = tempstring2[258] = '.';
+                       tempstring2[259] = 0;
+                       name = tempstring2;
+               }
+               strcat(tempstring, name);
+               for (l = strlen(name);l < 14;l++)
+                       strcat(tempstring, " ");
+               strcat(tempstring, " ");
+
+               name = PRVM_ValueString(d->type, (prvm_eval_t *)v);
+               if (strlen(name) > 256)
+               {
+                       strncpy(tempstring2, name, 256);
+                       tempstring2[256] = tempstring2[257] = tempstring2[258] = '.';
+                       tempstring2[259] = 0;
+                       name = tempstring2;
+               }
+               strcat(tempstring, name);
+               strcat(tempstring, "\n");
+               if (strlen(tempstring) >= 4096)
+               {
+                       Con_Printf("%s", tempstring);
+                       tempstring[0] = 0;
+               }
+       }
+       if (tempstring[0])
+               Con_Printf("%s", tempstring);
+}
+
+/*
+=============
+PRVM_ED_Write
+
+For savegames
+=============
+*/
+void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
+{
+       ddef_t  *d;
+       int             *v;
+       int             i, j;
+       char    *name;
+       int             type;
+
+       FS_Printf (f, "{\n");
+
+       if (ed->e->free)
+       {
+               FS_Printf (f, "}\n");
+               return;
+       }
+
+       for (i=1 ; i<prog->progs->numfielddefs ; i++)
+       {
+               d = &prog->fielddefs[i];
+               name = PRVM_GetString(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<prvm_type_size[type] ; j++)
+                       if (v[j])
+                               break;
+               if (j == prvm_type_size[type])
+                       continue;
+
+               FS_Printf (f,"\"%s\" ",name);
+               FS_Printf (f,"\"%s\"\n", PRVM_UglyValueString(d->type, (prvm_eval_t *)v));
+       }
+
+       FS_Printf (f, "}\n");
+}
+
+void PRVM_ED_PrintNum (int ent)
+{
+       PRVM_ED_Print (PRVM_EDICT_NUM(ent));
+}
+
+/*
+=============
+PRVM_ED_PrintEdicts_f
+
+For debugging, prints all the entities in the current server
+=============
+*/
+void PRVM_ED_PrintEdicts_f (void)
+{
+       int             i;
+
+       if(Cmd_Argc() != 2)
+       {
+               Con_Print("prvm_edicts <program name>\n");
+               return;
+       }
+       
+       PRVM_Begin;
+       if(!PRVM_SetProgFromString(Cmd_Argv(1)))
+               return;
+
+       Con_Printf ("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
+       for (i=0 ; i<prog->num_edicts ; i++)
+               PRVM_ED_PrintNum (i);
+
+       PRVM_End;
+}
+
+/*
+=============
+PRVM_ED_PrintEdict_f
+
+For debugging, prints a single edict
+=============
+*/
+void PRVM_ED_PrintEdict_f (void)
+{
+       int             i;
+
+       if(Cmd_Argc() != 3)
+       {
+               Con_Print("prvm_edict <program name> <edict number>\n");
+               return;
+       }
+
+       PRVM_Begin;
+       if(!PRVM_SetProgFromString(Cmd_Argv(1)))
+               return;
+
+       i = atoi (Cmd_Argv(2));
+       if (i >= prog->num_edicts)
+       {
+               Con_Printf("Bad edict number\n");
+               return;
+       }
+       PRVM_ED_PrintNum (i);
+
+       PRVM_End;
+}
+
+/*
+=============
+PRVM_ED_Count
+
+For debugging
+=============
+*/
+// 2 possibilities : 1. just displaying the active edict count
+//                                      2. making a function pointer [x]
+void PRVM_ED_Count_f (void)
+{
+       int             i;
+       prvm_edict_t    *ent;
+       int             active;
+
+       if(Cmd_Argc() != 2)
+       {
+               Con_Print("prvm_count <program name>\n");
+               return;
+       }
+
+       PRVM_Begin;
+       if(!PRVM_SetProgFromString(Cmd_Argv(1)))
+               return;
+
+       if(prog->count_edicts)
+               prog->count_edicts();
+       else
+       {
+               active = 0;
+               for (i=0 ; i<prog->num_edicts ; i++)
+               {
+                       ent = PRVM_EDICT_NUM(i);
+                       if (ent->e->free)
+                               continue;
+                       active++;
+               }
+               
+               Con_Printf ("num_edicts:%3i\n", sv.num_edicts);
+               Con_Printf ("active    :%3i\n", active);
+       }
+
+       PRVM_End;
+}
+
+/*
+==============================================================================
+
+                                       ARCHIVING GLOBALS
+
+FIXME: need to tag constants, doesn't really work
+==============================================================================
+*/
+
+/*
+=============
+PRVM_ED_WriteGlobals
+=============
+*/
+void PRVM_ED_WriteGlobals (qfile_t *f)
+{
+       ddef_t          *def;
+       int                     i;
+       char            *name;
+       int                     type;
+
+       FS_Printf (f,"{\n");
+       for (i=0 ; i<prog->progs->numglobaldefs ; i++)
+       {
+               def = &prog->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 = PRVM_GetString(def->s_name);
+               FS_Printf (f,"\"%s\" ", name);
+               FS_Printf (f,"\"%s\"\n", PRVM_UglyValueString(type, (prvm_eval_t *)&prog->globals[def->ofs]));
+       }
+       FS_Printf (f,"}\n");
+}
+
+/*
+=============
+PRVM_ED_ParseGlobals
+=============
+*/
+void PRVM_ED_ParseGlobals (const char *data)
+{
+       char keyname[1024]; // LordHavoc: good idea? bad idea?  was 64
+       ddef_t *key;
+
+       while (1)
+       {
+               // parse key
+               if (!COM_ParseToken(&data, false))
+                       PRVM_ERROR ("PRVM_ED_ParseEntity: EOF without closing brace");
+               if (com_token[0] == '}')
+                       break;
+
+               strcpy (keyname, com_token);
+
+               // parse value
+               if (!COM_ParseToken(&data, false))
+                       PRVM_ERROR ("PRVM_ED_ParseEntity: EOF without closing brace");
+
+               if (com_token[0] == '}')
+                       PRVM_ERROR ("PRVM_ED_ParseEntity: closing brace without data");
+
+               key = PRVM_ED_FindGlobal (keyname);
+               if (!key)
+               {
+                       Con_DPrintf ("'%s' is not a global on %s\n", keyname, PRVM_NAME);
+                       continue;
+               }
+
+               if (!PRVM_ED_ParseEpair(NULL, key, com_token))
+                       PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
+       }
+}
+
+//============================================================================
+
+
+/*
+=============
+PRVM_ED_NewString
+=============
+*/
+char *PRVM_ED_NewString (const char *string)
+{
+       char *new, *new_p;
+       int i,l;
+
+       l = strlen(string) + 1;
+       new = Mem_Alloc(prog->edictstring_mempool, 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;
+}
+
+
+/*
+=============
+PRVM_ED_ParseEval
+
+Can parse either fields or globals
+returns false if error
+=============
+*/
+qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s)
+{
+       int i;
+       ddef_t *def;
+       prvm_eval_t *val;
+       mfunction_t *func;
+
+       if (ent)
+               val = (prvm_eval_t *)((int *)ent->v + key->ofs);
+       else
+               val = (prvm_eval_t *)((int *)pr_globals + key->ofs);
+       switch (key->type & ~DEF_SAVEGLOBAL)
+       {
+       case ev_string:
+               val->string = PRVM_SetString(ED_NewString(s));
+               break;
+
+       case ev_float:
+               while (*s && *s <= ' ')
+                       s++;
+               val->_float = atof(s);
+               break;
+
+       case ev_vector:
+               for (i = 0;i < 3;i++)
+               {
+                       while (*s && *s <= ' ')
+                               s++;
+                       if (!*s)
+                               break;
+                       val->vector[i] = atof(s);
+                       while (*s > ' ')
+                               s++;
+                       if (!*s)
+                               break;
+               }
+               break;
+
+       case ev_entity:
+               while (*s && *s <= ' ')
+                       s++;
+               i = atoi(s);
+               if (i < 0 || i >= MAX_EDICTS)
+                       Con_Printf("PRVM_ED_ParseEpair: ev_entity reference too large (edict %i >= MAX_EDICTS %i) on %s\n", i, MAX_EDICTS, PRVM_NAME);
+               while (i >= prog->max_edicts)
+                       PRVM_MEM_IncreaseEdicts();
+                       //SV_IncreaseEdicts();
+               // if SV_IncreaseEdicts was called the base pointer needs to be updated
+               if (ent)
+                       val = (prvm_eval_t *)((int *)ent->v + key->ofs);
+               val->edict = PRVM_EDICT_TO_PROG(EDICT_NUM(i));
+               break;
+
+       case ev_field:
+               def = PRVM_ED_FindField(s);
+               if (!def)
+               {
+                       Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s on %s\n", s, PRVM_NAME);
+                       return false;
+               }
+               val->_int = PRVM_G_INT(def->ofs);
+               break;
+
+       case ev_function:
+               func = PRVM_ED_FindFunction(s);
+               if (!func)
+               {
+                       Con_Printf ("PRVM_ED_ParseEpair: Can't find function %s on %s\n", s, PRVM_NAME);
+                       return false;
+               }
+               val->function = func - prog->functions;
+               break;
+
+       default:
+               Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PR_GetString(key->s_name), PRVM_NAME);
+               return false;
+       }
+       return true;
+}
+
+/*
+====================
+PRVM_ED_ParseEdict
+
+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.
+====================
+*/
+const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
+{
+       ddef_t *key;
+       qboolean anglehack;
+       qboolean init;
+       char keyname[256];
+       int n;
+
+       init = false;
+
+// clear it
+       if (ent != prog->edicts)        // hack
+               memset (ent->v, 0, prog->progs->entityfields * 4);
+
+// go through all the dictionary pairs
+       while (1)
+       {
+       // parse key
+               if (!COM_ParseToken(&data, false))
+                       PRVM_ERROR ("PRVM_ED_ParseEntity: EOF without closing brace");
+               if (com_token[0] == '}')
+                       break;
+
+               // 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
+               if (!COM_ParseToken(&data, false))
+                       PRVM_ERROR ("PRVM_ED_ParseEntity: EOF without closing brace");
+
+               if (com_token[0] == '}')
+                       PRVM_ERROR ("PRVM_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 = PRVM_ED_FindField (keyname);
+               if (!key)
+               {
+                       Con_DPrintf ("%s: '%s' is not a field\n", PRVM_NAME, keyname);
+                       continue;
+               }
+
+               if (anglehack)
+               {
+                       char    temp[32];
+                       strcpy (temp, com_token);
+                       sprintf (com_token, "0 %s 0", temp);
+               }
+
+               if (!PRVM_ED_ParseEpair(ent, key, com_token))
+                       PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
+       }
+
+       if (!init)
+               ent->e->free = true;
+
+       return data;
+}
+
+
+/*
+================
+PRVM_ED_LoadFromFile
+
+The entities are directly placed in the array, rather than allocated with
+PRVM_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 PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
+================
+*/
+void PRVM_ED_LoadFromFile (const char *data)
+{
+       prvm_edict_t *ent;
+       int parsed, inhibited, spawned, died;
+       mfunction_t *func;
+
+       ent = NULL;
+       parsed = 0;
+       inhibited = 0;
+       spawned = 0;
+       died = 0;
+
+       // time defined ?
+       if(prog->flag & PRVM_GE_TIME)
+               PRVM_G_FLOAT(PRVM_ED_FindFieldOffset("time")) = prog->time;
+       
+// parse ents
+       while (1)
+       {
+// parse the opening brace
+               if (!COM_ParseToken(&data, false))
+                       break;
+               if (com_token[0] != '{')
+                       PRVM_ERROR ("PRVM_ED_LoadFromFile: found %s when expecting (%s) {",com_token, PRVM_NAME);
+
+               if (!ent)
+                       ent = PRVM_EDICT_NUM(0);
+               else
+                       ent = PRVM_ED_Alloc ();
+               data = PRVM_ED_ParseEdict (data, ent);
+               parsed++;
+
+               // remove the entity ?
+               if(prog->load_edict && !prog->load_edict(ent))
+               {
+                       PRVM_ED_Free(ent);
+                       inhibited++;
+                       continue;
+               }
+
+//
+// immediately call spawn function, but only if there is a self global
+//
+               if(prog->self && prog->flag & PRVM_FE_CLASSNAME)
+               {
+                       string_t handle =  *(string_t*)&((float*)ent->v)[PRVM_ED_FindFieldOffset("classname")];
+                       if (!handle)
+                       {
+                               Con_Printf ("No classname for:\n");
+                               PRVM_ED_Print (ent);
+                               PRVM_ED_Free (ent);
+                               continue;
+                       }
+                       
+                       // look for the spawn function
+                       func = PRVM_ED_FindFunction (PRVM_GetString(handle));
+                       
+                       if (!func)
+                       {
+                               if (developer.integer) // don't confuse non-developers with errors
+                               {
+                                       Con_Printf ("No spawn function for:\n");
+                                       PRVM_ED_Print (ent);
+                               }
+                               PRVM_ED_Free (ent);
+                               continue;
+                       }
+                       
+                       // self = ent
+                       PRVM_G_INT(prog->self->ofs) = PRVM_EDICT_TO_PROG(ent);
+                       PRVM_ExecuteProgram (func - prog->functions, "");
+               }
+       
+               spawned++;
+               if (ent->e->free)
+                       died++;
+       }
+
+       Con_DPrintf ("%s: %i entities parsed, %i inhibited, %i spawned (%i removed self, %i stayed)\n", PRVM_NAME, parsed, inhibited, spawned, died, spawned - died);
+}
+
+// not used
+/*
+typedef struct dpfield_s
+{
+       int type;
+       char *string;
+}
+dpfield_t;
+
+#define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
+
+dpfield_t dpfields[] =
+{
+};
+*/
+
+/*
+===============
+PRVM_ResetProg
+===============
+*/
+
+void PRVM_ResetProg()
+{
+       mempool_t *t1, *t2, *t3;
+
+       t1 = prog->progs_mempool;
+       t2 = prog->edictstring_mempool;
+       t3 = prog->edicts_mempool;
+       
+       Mem_EmptyPool(prog->progs_mempool);
+       Mem_EmptyPool(prog->edictstring_mempool);
+       Mem_EmptyPool(prog->edicts_mempool);
+       
+       memset(prog,0,sizeof(prvm_prog_t));
+       
+       
+       prog->progs_mempool = t1;
+       prog->edictstring_mempool = t2;
+       prog->edicts_mempool = t3;
+
+       PRVM_GCALL(reset_cmd);
+}
+
+/*
+===============
+PRVM_LoadProgs
+===============
+*/
+void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func)
+{
+       int i;
+       dstatement_t *st;
+       ddef_t *infielddefs;
+       void *temp;
+       dfunction_t *dfunctions;
+
+       Mem_EmptyPool(prog->progs_mempool);
+       Mem_EmptyPool(prog->edictstring_mempool);
+
+       temp = FS_LoadFile (filename, false);
+       if (temp == 0)
+               PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
+
+       prog->progs = (dprograms_t *)Mem_Alloc(prog->progs_mempool, fs_filesize);
+
+       memcpy(prog->progs, temp, fs_filesize);
+       Mem_Free(temp);
+
+       Con_DPrintf ("%s programs occupy %iK.\n", PRVM_NAME, fs_filesize/1024);
+
+       pr_crc = CRC_Block((qbyte *)prog->progs, fs_filesize);
+
+// byte swap the header
+       for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
+               ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
+
+       if (prog->progs->version != PROG_VERSION)
+               PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
+       if (prog->progs->crc != prog->crc)
+               PRVM_ERROR ("%s: %s system vars have been modified, progdefs.h is out of date", PRVM_NAME, filename);
+
+       //pr_functions = (dfunction_t *)((qbyte *)progs + progs->ofs_functions);
+       dfunctions = (dfunction_t *)((qbyte *)prog->progs + prog->progs->ofs_functions);
+       prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
+       prog->globaldefs = (ddef_t *)((qbyte *)prog->progs + prog->progs->ofs_globaldefs);
+
+       // we need to expand the fielddefs list to include all the engine fields,
+       // so allocate a new place for it
+       infielddefs = (ddef_t *)((qbyte *)prog->progs + prog->progs->ofs_fielddefs);
+       //                                                                                              ( + DPFIELDS                       )
+       prog->fielddefs = Mem_Alloc(prog->progs_mempool, prog->progs->numfielddefs * sizeof(ddef_t));
+
+       prog->statements = (dstatement_t *)((qbyte *)prog->progs + prog->progs->ofs_statements);
+
+       // moved edict_size calculation down below field adding code
+
+       //pr_global_struct = (globalvars_t *)((qbyte *)progs + progs->ofs_globals);
+       prog->globals = (float *)((qbyte *)prog->progs + prog->progs->ofs_globals);
+
+// byte swap the lumps
+       for (i=0 ; i<prog->progs->numstatements ; i++)
+       {
+               prog->statements[i].op = LittleShort(prog->statements[i].op);
+               prog->statements[i].a = LittleShort(prog->statements[i].a);
+               prog->statements[i].b = LittleShort(prog->statements[i].b);
+               prog->statements[i].c = LittleShort(prog->statements[i].c);
+       }
+
+       prog->functions = Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
+       for (i = 0;i < prog->progs->numfunctions;i++)
+       {
+               prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
+               prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
+               prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
+               prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
+               prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
+               prog->functions[i].locals = LittleLong (dfunctions[i].locals);
+               memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
+       }
+
+       for (i=0 ; i<prog->progs->numglobaldefs ; i++)
+       {
+               prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
+               prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
+               prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
+       }
+
+       // copy the progs fields to the new fields list
+       for (i = 0;i < prog->progs->numfielddefs;i++)
+       {
+               prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
+               if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
+                       PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
+               prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
+               prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
+       }
+
+/*     // append the darkplaces fields
+       for (i = 0;i < (int) DPFIELDS;i++)
+       {
+               pr_fielddefs[progs->numfielddefs].type = dpfields[i].type;
+               pr_fielddefs[progs->numfielddefs].ofs = progs->entityfields;
+               pr_fielddefs[progs->numfielddefs].s_name = PR_SetString(dpfields[i].string);
+               if (pr_fielddefs[progs->numfielddefs].type == ev_vector)
+                       progs->entityfields += 3;
+               else
+                       progs->entityfields++;
+               progs->numfielddefs++;
+       }*/
+
+       // check required functions
+       for(i=0 ; i < numrequiredfunc ; i++)
+               if(PRVM_ED_FindFunction(required_func[i]) == 0)
+                       PRVM_ERROR("%s: %s not found in %s\n",PRVM_NAME, required_func[i], filename);
+
+       for (i=0 ; i<prog->progs->numglobals ; i++)
+               ((int *)prog->globals)[i] = LittleLong (((int *)prog->globals)[i]);
+
+       // moved edict_size calculation down here, below field adding code
+       // LordHavoc: this no longer includes the edict_t header
+       prog->edict_size = prog->progs->entityfields * 4;
+       prog->edictareasize = prog->edict_size * MAX_EDICTS;
+
+       // LordHavoc: bounds check anything static
+       for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
+       {
+               switch (st->op)
+               {
+               case OP_IF:
+               case OP_IFNOT:
+                       if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
+                               PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s\n", i, PRVM_NAME);
+                       break;
+               case OP_GOTO:
+                       if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
+                               PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s\n", i, PRVM_NAME);
+                       break;
+               // global global global
+               case OP_ADD_F:
+               case OP_ADD_V:
+               case OP_SUB_F:
+               case OP_SUB_V:
+               case OP_MUL_F:
+               case OP_MUL_V:
+               case OP_MUL_FV:
+               case OP_MUL_VF:
+               case OP_DIV_F:
+               case OP_BITAND:
+               case OP_BITOR:
+               case OP_GE:
+               case OP_LE:
+               case OP_GT:
+               case OP_LT:
+               case OP_AND:
+               case OP_OR:
+               case OP_EQ_F:
+               case OP_EQ_V:
+               case OP_EQ_S:
+               case OP_EQ_E:
+               case OP_EQ_FNC:
+               case OP_NE_F:
+               case OP_NE_V:
+               case OP_NE_S:
+               case OP_NE_E:
+               case OP_NE_FNC:
+               case OP_ADDRESS:
+               case OP_LOAD_F:
+               case OP_LOAD_FLD:
+               case OP_LOAD_ENT:
+               case OP_LOAD_S:
+               case OP_LOAD_FNC:
+               case OP_LOAD_V:
+                       if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
+                               PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)\n", i);
+                       break;
+               // global none global
+               case OP_NOT_F:
+               case OP_NOT_V:
+               case OP_NOT_S:
+               case OP_NOT_FNC:
+               case OP_NOT_ENT:
+                       if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
+                               PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s\n", i, PRVM_NAME);
+                       break;
+               // 2 globals
+               case OP_STOREP_F:
+               case OP_STOREP_ENT:
+               case OP_STOREP_FLD:
+               case OP_STOREP_S:
+               case OP_STOREP_FNC:
+               case OP_STORE_F:
+               case OP_STORE_ENT:
+               case OP_STORE_FLD:
+               case OP_STORE_S:
+               case OP_STORE_FNC:
+               case OP_STATE:
+               case OP_STOREP_V:
+               case OP_STORE_V:
+                       if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
+                               PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)\n in %s", i, PRVM_NAME);
+                       break;
+               // 1 global
+               case OP_CALL0:
+               case OP_CALL1:
+               case OP_CALL2:
+               case OP_CALL3:
+               case OP_CALL4:
+               case OP_CALL5:
+               case OP_CALL6:
+               case OP_CALL7:
+               case OP_CALL8:
+               case OP_DONE:
+               case OP_RETURN:
+                       if ((unsigned short) st->a >= prog->progs->numglobals)
+                               PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s\n", i, PRVM_NAME);
+                       break;
+               default:
+                       PRVM_ERROR("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
+                       break;
+               }
+       }
+
+       PRVM_Init_Exec();
+
+       prog->loaded = TRUE;
+       
+       // set flags & ddef_ts in prog
+       
+       prog->flag = 0;
+       
+       prog->self = PRVM_ED_FindGlobal("self");
+
+       if(PRVM_ED_FindGlobal("time"))
+               prog->flag |= PRVM_GE_TIME;
+
+       if(PRVM_ED_FindFieldOffset ("classname"))
+               prog->flag |= PRVM_FE_CLASSNAME; 
+
+       if(PRVM_ED_FindFieldOffset ("nextthink") && PRVM_ED_FindFieldOffset("frame") && PRVM_ED_FindFieldOffset("think") 
+               && prog->flag &  PRVM_GE_TIME && prog->self) 
+               prog->flag |= PRVM_OP_STATE;
+       
+       PRVM_GCALL(reset_cmd)();
+
+       // init mempools
+       PRVM_MEM_Alloc();
+}
+
+
+void PRVM_Fields_f (void)
+{
+       int i, j, ednum, used, usedamount;
+       int *counts;
+       char tempstring[5000], tempstring2[260], *name;
+       prvm_edict_t *ed;
+       ddef_t *d;
+       int *v;
+
+       // TODO
+       /*
+       if (!sv.active)
+       {
+               Con_Printf("no progs loaded\n");
+               return;
+       }
+       */
+
+       if(Cmd_Argc() != 2)
+       {
+               Con_Print("prvm_fields <program name>\n");
+               return;
+       }
+
+       PRVM_Begin;
+       if(!PRVM_SetProgFromString(Cmd_Argv(1)))
+               return;
+
+       counts = Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
+       for (ednum = 0;ednum < prog->max_edicts;ednum++)
+       {
+               ed = PRVM_EDICT_NUM(ednum);
+               if (ed->e->free)
+                       continue;
+               for (i = 1;i < prog->progs->numfielddefs;i++)
+               {
+                       d = &prog->fielddefs[i];
+                       name = PRVM_GetString(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
+                       for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
+                       {
+                               if (v[j])
+                               {
+                                       counts[i]++;
+                                       break;
+                               }
+                       }
+               }
+       }
+       used = 0;
+       usedamount = 0;
+       tempstring[0] = 0;
+       for (i = 0;i < prog->progs->numfielddefs;i++)
+       {
+               d = &prog->fielddefs[i];
+               name = PRVM_GetString(d->s_name);
+               if (name[strlen(name)-2] == '_')
+                       continue;       // skip _x, _y, _z vars
+               switch(d->type & ~DEF_SAVEGLOBAL)
+               {
+               case ev_string:
+                       strcat(tempstring, "string   ");
+                       break;
+               case ev_entity:
+                       strcat(tempstring, "entity   ");
+                       break;
+               case ev_function:
+                       strcat(tempstring, "function ");
+                       break;
+               case ev_field:
+                       strcat(tempstring, "field    ");
+                       break;
+               case ev_void:
+                       strcat(tempstring, "void     ");
+                       break;
+               case ev_float:
+                       strcat(tempstring, "float    ");
+                       break;
+               case ev_vector:
+                       strcat(tempstring, "vector   ");
+                       break;
+               case ev_pointer:
+                       strcat(tempstring, "pointer  ");
+                       break;
+               default:
+                       sprintf (tempstring2, "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
+                       strcat(tempstring, tempstring2);
+                       break;
+               }
+               if (strlen(name) > 256)
+               {
+                       strncpy(tempstring2, name, 256);
+                       tempstring2[256] = tempstring2[257] = tempstring2[258] = '.';
+                       tempstring2[259] = 0;
+                       name = tempstring2;
+               }
+               strcat(tempstring, name);
+               for (j = strlen(name);j < 25;j++)
+                       strcat(tempstring, " ");
+               sprintf(tempstring2, "%5d", counts[i]);
+               strcat(tempstring, tempstring2);
+               strcat(tempstring, "\n");
+               if (strlen(tempstring) >= 4096)
+               {
+                       Con_Printf("%s", tempstring);
+                       tempstring[0] = 0;
+               }
+               if (counts[i])
+               {
+                       used++;
+                       usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
+               }
+       }
+       Mem_Free(counts);
+       Con_Printf("%s: %i entity fields (%i in use), totalling %i bytes per edict (%i in use), %i edicts allocated, %i bytes total spent on edict fields (%i needed)\n", PRVM_NAME, prog->progs->entityfields, used, prog->progs->entityfields * 4, usedamount * 4, prog->max_edicts, prog->progs->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts);
+
+       PRVM_End;
+}
+
+void PRVM_Globals_f (void)
+{
+       int i;
+       // TODO
+       /*if (!sv.active)
+       {
+               Con_Printf("no progs loaded\n");
+               return;
+       }*/
+       if(Cmd_Argc () != 2)
+       {
+               Con_Print ("prvm_globals <program name>\n");
+               return;
+       }
+
+       PRVM_Begin;
+       if(!PRVM_SetProgFromString (Cmd_Argv (1)))
+               return;
+
+       Con_Printf("%s :", PRVM_NAME);
+
+       for (i = 0;i < prog->progs->numglobaldefs;i++)
+               Con_Printf("%s\n", PRVM_GetString(pr_globaldefs[i].s_name));
+       Con_Printf("%i global variables, totalling %i bytes\n", prog->progs->numglobals, prog->progs->numglobals * 4);
+
+       PRVM_End;
+}
+
+/*
+===============
+PRVM_Init
+===============
+*/
+void PRVM_Init (void)
+{
+       Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f);
+       Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f);
+       Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f);
+       Cmd_AddCommand ("prvm_profile", PRVM_Profile_f);
+       Cmd_AddCommand ("prvm_fields", PRVM_Fields_f);
+       Cmd_AddCommand ("prvm_globals", PRVM_Globals_f);
+       // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
+       Cvar_RegisterVariable (&prvm_boundscheck);
+       Cvar_RegisterVariable (&prvm_traceqc);
+
+       VM_Cmd_Init();
+}
+
+/*
+===============
+PRVM_InitProg
+===============
+*/
+void PRVM_InitProg(int prognr)
+{
+       if(prognr < 0 || prognr >= PRVM_MAXPROGS)
+               Sys_Error("PRVM_InitProg: Invalid program number %i\n",prognr);
+
+       prog = &prog_list[prognr];
+
+       memset(prog, 0, sizeof(prvm_prog_t));
+
+       PRVM_GCALL(init_cmd)();
+}
+
+int PRVM_GetProgNr()
+{
+       return prog - prog_list;
+}
+
+// LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
+prvm_edict_t *PRVM_EDICT_NUM_ERROR(int n, char *filename, int fileline)
+{
+       PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
+       return NULL;
+}
+
+/*
+int NUM_FOR_EDICT_ERROR(edict_t *e)
+{
+       Host_Error ("NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, sv.edicts, e - sv.edicts);
+       return 0;
+}
+
+int NUM_FOR_EDICT(edict_t *e)
+{
+       int n;
+       n = e - sv.edicts;
+       if ((unsigned int)n >= MAX_EDICTS)
+               Host_Error ("NUM_FOR_EDICT: bad pointer");
+       return n;
+}
+
+//int NoCrash_NUM_FOR_EDICT(edict_t *e)
+//{
+//     return e - sv.edicts;
+//}
+
+//#define      EDICT_TO_PROG(e) ((qbyte *)(((edict_t *)e)->v) - (qbyte *)(sv.edictsfields))
+//#define PROG_TO_EDICT(e) (sv.edicts + ((e) / (progs->entityfields * 4)))
+int EDICT_TO_PROG(edict_t *e)
+{
+       int n;
+       n = e - sv.edicts;
+       if ((unsigned int)n >= (unsigned int)sv.max_edicts)
+               Host_Error("EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)\n", e, n, sv.edicts);
+       return n;// EXPERIMENTAL
+       //return (qbyte *)e->v - (qbyte *)sv.edictsfields;
+}
+edict_t *PROG_TO_EDICT(int n)
+{
+       if ((unsigned int)n >= (unsigned int)sv.max_edicts)
+               Host_Error("PROG_TO_EDICT: invalid edict number %i\n", n);
+       return sv.edicts + n; // EXPERIMENTAL
+       //return sv.edicts + ((n) / (progs->entityfields * 4));
+}
+*/
+
diff --git a/prvm_exec.c b/prvm_exec.c
new file mode 100644 (file)
index 0000000..2ea778e
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+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.
+
+*/
+
+#include "quakedef.h"
+
+char *prvm_opnames[] =
+{
+"DONE",
+
+"MUL_F",
+"MUL_V",
+"MUL_FV",
+"MUL_VF",
+
+"DIV",
+
+"ADD_F",
+"ADD_V",
+
+"SUB_F",
+"SUB_V",
+
+"EQ_F",
+"EQ_V",
+"EQ_S",
+"EQ_E",
+"EQ_FNC",
+
+"NE_F",
+"NE_V",
+"NE_S",
+"NE_E",
+"NE_FNC",
+
+"LE",
+"GE",
+"LT",
+"GT",
+
+"INDIRECT",
+"INDIRECT",
+"INDIRECT",
+"INDIRECT",
+"INDIRECT",
+"INDIRECT",
+
+"ADDRESS",
+
+"STORE_F",
+"STORE_V",
+"STORE_S",
+"STORE_ENT",
+"STORE_FLD",
+"STORE_FNC",
+
+"STOREP_F",
+"STOREP_V",
+"STOREP_S",
+"STOREP_ENT",
+"STOREP_FLD",
+"STOREP_FNC",
+
+"RETURN",
+
+"NOT_F",
+"NOT_V",
+"NOT_S",
+"NOT_ENT",
+"NOT_FNC",
+
+"IF",
+"IFNOT",
+
+"CALL0",
+"CALL1",
+"CALL2",
+"CALL3",
+"CALL4",
+"CALL5",
+"CALL6",
+"CALL7",
+"CALL8",
+
+"STATE",
+
+"GOTO",
+
+"AND",
+"OR",
+
+"BITAND",
+"BITOR"
+};
+
+char *PRVM_GlobalString (int ofs);
+char *PRVM_GlobalStringNoContents (int ofs);
+
+
+//=============================================================================
+
+/*
+=================
+PRVM_PrintStatement
+=================
+*/
+void PRVM_PrintStatement (dstatement_t *s)
+{
+       int             i;
+
+       if ( (unsigned)s->op < sizeof(prvm_opnames)/sizeof(prvm_opnames[0]))
+       {
+               Con_Printf ("%s ",  prvm_opnames[s->op]);
+               i = strlen(prvm_opnames[s->op]);
+               for ( ; i<10 ; i++)
+                       Con_Printf (" ");
+       }
+
+       if (s->op == OP_IF || s->op == OP_IFNOT)
+               Con_Printf ("%sbranch %i",PRVM_GlobalString((unsigned short) s->a),s->b);
+       else if (s->op == OP_GOTO)
+       {
+               Con_Printf ("branch %i",s->a);
+       }
+       else if ( (unsigned)(s->op - OP_STORE_F) < 6)
+       {
+               Con_Printf ("%s", PRVM_GlobalString((unsigned short) s->a));
+               Con_Printf ("%s", PRVM_GlobalStringNoContents((unsigned short) s->b));
+       }
+       else
+       {
+               if (s->a)
+                       Con_Printf ("%s", PRVM_GlobalString((unsigned short) s->a));
+               if (s->b)
+                       Con_Printf ("%s", PRVM_GlobalString((unsigned short) s->b));
+               if (s->c)
+                       Con_Printf ("%s", PRVM_GlobalStringNoContents((unsigned short) s->c));
+       }
+       Con_Printf ("\n");
+}
+
+/*
+============
+PRVM_StackTrace
+============
+*/
+void PRVM_StackTrace (void)
+{
+       mfunction_t     *f;
+       int                     i;
+
+       prog->stack[prog->depth].s = prog->xstatement;
+       prog->stack[prog->depth].f = prog->xfunction;
+       for (i = prog->depth;i > 0;i--)
+       {
+               f = prog->stack[i].f;
+
+               if (!f)
+                       Con_Printf ("<NULL FUNCTION>\n");
+               else
+                       Con_Printf ("%12s : %s : statement %i\n", PRVM_GetString(f->s_file), PRVM_GetString(f->s_name), prog->stack[i].s - f->first_statement);
+       }
+}
+
+
+/*
+============
+PRVM_Profile_f
+
+============
+*/
+void PRVM_Profile_f (void)
+{
+       mfunction_t *f, *best;
+       int i, num, max/*, howmany*/;
+
+       //howmany = 10;
+       //if (Cmd_Argc() == 2)
+       //      howmany = atoi(Cmd_Argv(1));
+       if(Cmd_Argc() != 2)
+       {
+               Con_Print("prvm_profile <program name>\n");
+               return;
+       }
+
+       PRVM_Begin;
+       if(!PRVM_SetProgFromString(Cmd_Argv(1)))
+               return;
+
+       num = 0;
+       do
+       {
+               max = 0;
+               best = NULL;
+               for (i=0 ; i<prog->progs->numfunctions ; i++)
+               {
+                       f = &prog->functions[i];
+                       if (f->profile > max)
+                       {
+                               max = f->profile;
+                               best = f;
+                       }
+               }
+               if (best)
+               {
+                       //if (num < howmany)
+                               Con_Printf ("%s: %7i %7i %s\n", PRVM_NAME, best->profile, best->builtinsprofile, PRVM_GetString(best->s_name));
+                       num++;
+                       best->profile = 0;
+                       best->builtinsprofile = 0;
+               }
+       } while (best);
+
+       PRVM_End;
+}
+
+void PRVM_CrashAll()
+{
+       int i;
+       PRVM_Begin; 
+       for(i = 0; i < PRVM_MAXPROGS; i++)
+       {
+               if(!PRVM_ProgLoaded(i))
+                       continue;
+               PRVM_SetProg(i);
+               PRVM_Crash();
+       }
+       PRVM_End;
+}
+
+void PRVM_Crash()
+{
+       int i;
+       
+       if (prog->depth < 1)
+       {
+               // kill the stack just to be sure
+               prog->depth = 0;
+               prog->localstack_used = 0;
+               return;
+       }
+
+       Con_Printf("QuakeC crash report for %s:\n", PRVM_NAME);
+       if (prog->xfunction)
+       {
+               for (i = -4;i <= 0;i++)
+                       if (prog->xstatement + i >= prog->xfunction->first_statement)
+                               PRVM_PrintStatement (prog->statements + prog->xstatement + i);
+       }
+       else
+               Con_Printf("null function executing??\n");
+       PRVM_StackTrace ();
+
+       // dump the stack so host_error can shutdown functions
+       prog->depth = 0;
+       prog->localstack_used = 0;
+
+}
+
+/*
+============================================================================
+PRVM_ExecuteProgram
+
+The interpretation main loop
+============================================================================
+*/
+
+/*
+====================
+PRVM_EnterFunction
+
+Returns the new program statement counter
+====================
+*/
+int PRVM_EnterFunction (mfunction_t *f)
+{
+       int             i, j, c, o;
+
+       if (!f)
+               PRVM_ERROR ("PRVM_EnterFunction: NULL function in %s\n", PRVM_NAME);
+
+       prog->stack[prog->depth].s = prog->xstatement;
+       prog->stack[prog->depth].f = prog->xfunction;
+       prog->depth++;
+       if (prog->depth >=PRVM_MAX_STACK_DEPTH)
+               PRVM_ERROR ("stack overflow");
+
+// save off any locals that the new function steps on
+       c = f->locals;
+       if (prog->localstack_used + c > PRVM_LOCALSTACK_SIZE)
+               PRVM_ERROR ("PRVM_ExecuteProgram: locals stack overflow in %s\n", PRVM_NAME);
+
+       for (i=0 ; i < c ; i++)
+               prog->localstack[prog->localstack_used+i] = ((int *)prog->globals)[f->parm_start + i];
+       prog->localstack_used += c;
+
+// copy parameters
+       o = f->parm_start;
+       for (i=0 ; i<f->numparms ; i++)
+       {
+               for (j=0 ; j<f->parm_size[i] ; j++)
+               {
+                       ((int *)prog->globals)[o] = ((int *)prog->globals)[OFS_PARM0+i*3+j];
+                       o++;
+               }
+       }
+
+       prog->xfunction = f;
+       return f->first_statement - 1;  // offset the s++
+}
+
+/*
+====================
+PRVM_LeaveFunction
+====================
+*/
+int PRVM_LeaveFunction (void)
+{
+       int             i, c;
+
+       if (prog->depth <= 0)
+               PRVM_ERROR ("prog stack underflow in %s", PRVM_NAME);
+
+       if (!prog->xfunction)
+               PRVM_ERROR ("PR_LeaveFunction: NULL function in %s\n", PRVM_NAME);
+// restore locals from the stack
+       c = prog->xfunction->locals;
+       prog->localstack_used -= c;
+       if (prog->localstack_used < 0)
+               PRVM_ERROR ("PRVM_ExecuteProgram: locals stack underflow in %s\n", PRVM_NAME);
+
+       for (i=0 ; i < c ; i++)
+               ((int *)prog->globals)[prog->xfunction->parm_start + i] = prog->localstack[prog->localstack_used+i];
+
+// up stack
+       prog->depth--;
+       prog->xfunction = prog->stack[prog->depth].f;
+       return prog->stack[prog->depth].s;
+}
+
+void PRVM_Init_Exec(void)
+{
+       // dump the stack
+       prog->depth = 0;
+       prog->localstack_used = 0;
+       // reset the string table
+       // nothing here yet
+}
+
+/*
+====================
+PRVM_ExecuteProgram
+====================
+*/
+// LordHavoc: optimized
+#define OPA ((eval_t *)&prog->globals[(unsigned short) st->a])
+#define OPB ((eval_t *)&prog->globals[(unsigned short) st->b])
+#define OPC ((eval_t *)&prog->globals[(unsigned short) st->c])
+extern cvar_t prvm_boundscheck;
+extern cvar_t prvm_traceqc;
+extern int             PRVM_ED_FindFieldOffset (const char *field);
+extern ddef_t* PRVM_ED_FindGlobal(const char *name); 
+void PRVM_ExecuteProgram (func_t fnum, const char *errormessage)
+{
+       dstatement_t    *st;
+       mfunction_t     *f, *newf;
+       prvm_edict_t    *ed;
+       prvm_eval_t     *ptr;
+       int             profile, startprofile, cachedpr_trace, exitdepth;
+
+       if (!fnum || fnum >= prog->progs->numfunctions)
+       {
+               if (prog->self && PRVM_G_INT(prog->self->ofs))
+                       PRVM_ED_Print (PRVM_PROG_TO_EDICT(PRVM_G_INT(prog->self->ofs)));
+               PRVM_ERROR ("PR_ExecuteProgram: %s", errormessage);
+       }
+
+       f = &prog->functions[fnum];
+
+       prog->trace = prvm_traceqc.integer;
+
+       // we know we're done when pr_depth drops to this
+       exitdepth = prog->depth;
+
+// make a stack frame
+       st = &prog->statements[PRVM_EnterFunction (f)];
+       startprofile = profile = 0;
+
+chooseexecprogram:
+       cachedpr_trace = prog->trace;
+       if (prvm_boundscheck.integer)
+       {
+#define PRVMBOUNDSCHECK 1
+               if (prog->trace)
+               {
+#define PRVMTRACE 1
+#include "prvm_execprogram.h"
+               }
+               else
+               {
+#undef PRVMTRACE
+#include "prvm_execprogram.h"
+               }
+       }
+       else
+       {
+#undef PRVMBOUNDSCHECK
+               if (prog->trace)
+               {
+#define PRVMTRACE 1
+#include "prvm_execprogram.h"
+               }
+               else
+               {
+#undef PRVMTRACE
+#include "prvm_execprogram.h"
+               }
+       }
+}
diff --git a/prvm_execprogram.h b/prvm_execprogram.h
new file mode 100644 (file)
index 0000000..3439aff
--- /dev/null
@@ -0,0 +1,614 @@
+
+// This code isn't #ifdef/#define protectable, don't try.
+
+               while (1)
+               {
+                       st++;
+                       if (++profile > 10000000) // LordHavoc: increased runaway loop limit 100x
+                       {
+                               // LordHavoc: update profile counter for debugging reasons
+                               // (identifying erroneous loops and recursion patterns)
+                               prog->xfunction->profile += profile - startprofile;
+                               startprofile = profile;
+                               // update the statement number before we error out
+                               prog->xstatement = st - prog->statements;
+                               PRVM_ERROR("runaway loop counter hit limit of %d opcodes\ntip: if having trouble identifying the problem, try typing profile now in %s", profile, PRVM_NAME);
+                       }
+
+#if PRVMTRACE
+                       prog->xfunction->profile += profile - startprofile;
+                       startprofile = profile;
+                       prog->xstatement = st - prog->statements;
+                       PRVM_PrintStatement(st);
+#endif
+
+                       switch (st->op)
+                       {
+                       case OP_ADD_F:
+                               OPC->_float = OPA->_float + OPB->_float;
+                               break;
+                       case OP_ADD_V:
+                               OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
+                               OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
+                               OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
+                               break;
+                       case OP_SUB_F:
+                               OPC->_float = OPA->_float - OPB->_float;
+                               break;
+                       case OP_SUB_V:
+                               OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
+                               OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
+                               OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
+                               break;
+                       case OP_MUL_F:
+                               OPC->_float = OPA->_float * OPB->_float;
+                               break;
+                       case OP_MUL_V:
+                               OPC->_float = OPA->vector[0]*OPB->vector[0] + OPA->vector[1]*OPB->vector[1] + OPA->vector[2]*OPB->vector[2];
+                               break;
+                       case OP_MUL_FV:
+                               OPC->vector[0] = OPA->_float * OPB->vector[0];
+                               OPC->vector[1] = OPA->_float * OPB->vector[1];
+                               OPC->vector[2] = OPA->_float * OPB->vector[2];
+                               break;
+                       case OP_MUL_VF:
+                               OPC->vector[0] = OPB->_float * OPA->vector[0];
+                               OPC->vector[1] = OPB->_float * OPA->vector[1];
+                               OPC->vector[2] = OPB->_float * OPA->vector[2];
+                               break;
+                       case OP_DIV_F:
+                               OPC->_float = OPA->_float / OPB->_float;
+                               break;
+                       case OP_BITAND:
+                               OPC->_float = (int)OPA->_float & (int)OPB->_float;
+                               break;
+                       case OP_BITOR:
+                               OPC->_float = (int)OPA->_float | (int)OPB->_float;
+                               break;
+                       case OP_GE:
+                               OPC->_float = OPA->_float >= OPB->_float;
+                               break;
+                       case OP_LE:
+                               OPC->_float = OPA->_float <= OPB->_float;
+                               break;
+                       case OP_GT:
+                               OPC->_float = OPA->_float > OPB->_float;
+                               break;
+                       case OP_LT:
+                               OPC->_float = OPA->_float < OPB->_float;
+                               break;
+                       case OP_AND:
+                               OPC->_float = OPA->_float && OPB->_float;
+                               break;
+                       case OP_OR:
+                               OPC->_float = OPA->_float || OPB->_float;
+                               break;
+                       case OP_NOT_F:
+                               OPC->_float = !OPA->_float;
+                               break;
+                       case OP_NOT_V:
+                               OPC->_float = !OPA->vector[0] && !OPA->vector[1] && !OPA->vector[2];
+                               break;
+                       case OP_NOT_S:
+                               OPC->_float = !OPA->string || !*PR_GetString(OPA->string);
+                               break;
+                       case OP_NOT_FNC:
+                               OPC->_float = !OPA->function;
+                               break;
+                       case OP_NOT_ENT:
+                               OPC->_float = (OPA->edict == 0);
+                               break;
+                       case OP_EQ_F:
+                               OPC->_float = OPA->_float == OPB->_float;
+                               break;
+                       case OP_EQ_V:
+                               OPC->_float = (OPA->vector[0] == OPB->vector[0]) && (OPA->vector[1] == OPB->vector[1]) && (OPA->vector[2] == OPB->vector[2]);
+                               break;
+                       case OP_EQ_S:
+                               OPC->_float = !strcmp(PRVM_GetString(OPA->string),PRVM_GetString(OPB->string));
+                               break;
+                       case OP_EQ_E:
+                               OPC->_float = OPA->_int == OPB->_int;
+                               break;
+                       case OP_EQ_FNC:
+                               OPC->_float = OPA->function == OPB->function;
+                               break;
+                       case OP_NE_F:
+                               OPC->_float = OPA->_float != OPB->_float;
+                               break;
+                       case OP_NE_V:
+                               OPC->_float = (OPA->vector[0] != OPB->vector[0]) || (OPA->vector[1] != OPB->vector[1]) || (OPA->vector[2] != OPB->vector[2]);
+                               break;
+                       case OP_NE_S:
+                               OPC->_float = strcmp(PRVM_GetString(OPA->string),PRVM_GetString(OPB->string));
+                               break;
+                       case OP_NE_E:
+                               OPC->_float = OPA->_int != OPB->_int;
+                               break;
+                       case OP_NE_FNC:
+                               OPC->_float = OPA->function != OPB->function;
+                               break;
+
+               //==================
+                       case OP_STORE_F:
+                       case OP_STORE_ENT:
+                       case OP_STORE_FLD:              // integers
+                       case OP_STORE_S:
+                       case OP_STORE_FNC:              // pointers
+                               OPB->_int = OPA->_int;
+                               break;
+                       case OP_STORE_V:
+                               OPB->ivector[0] = OPA->ivector[0];
+                               OPB->ivector[1] = OPA->ivector[1];
+                               OPB->ivector[2] = OPA->ivector[2];
+                               break;
+
+                       case OP_STOREP_F:
+                       case OP_STOREP_ENT:
+                       case OP_STOREP_FLD:             // integers
+                       case OP_STOREP_S:
+                       case OP_STOREP_FNC:             // pointers
+#if PRVMBOUNDSCHECK
+                               if (OPB->_int < 0 || OPB->_int + 4 > prog->edictareasize)
+                               {
+                                       prog->xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       prog->xstatement = st - prog->statements;
+                                       PRVM_ERROR("%s attempted to write to an out of bounds edict (%i)\n", PRVM_NAME, OPB->_int);
+                                       return;
+                               }
+#endif
+                               ptr = (prvm_eval_t *)((qbyte *)prog->edictsfields + OPB->_int);
+                               ptr->_int = OPA->_int;
+                               break;
+                       case OP_STOREP_V:
+#if PRVMBOUNDSCHECK
+                               if (OPB->_int < 0 || OPB->_int + 12 > prog->edictareasize)
+                               {
+                                       prog->xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       prog->xstatement = st - prog->statements;
+                                       PRVM_ERROR("%s attempted to write to an out of bounds edict (%i)\n", PRVM_NAME, OPB->_int);
+                                       return;
+                               }
+#endif
+                               ptr = (prvm_eval_t *)((qbyte *)prog->edictsfields + OPB->_int);
+                               ptr->vector[0] = OPA->vector[0];
+                               ptr->vector[1] = OPA->vector[1];
+                               ptr->vector[2] = OPA->vector[2];
+                               break;
+
+                       case OP_ADDRESS:
+#if PRVMBOUNDSCHECK
+                               if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->progs->entityfields))
+                               {
+                                       prog->xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       prog->xstatement = st - prog->statements;
+                                       PRVM_ERROR("%s attempted to address an invalid field (%i) in an edict\n", PRVM_NAME, OPB->_int);
+                                       return;
+                               }
+#endif
+                               /*if (OPA->edict == 0 && sv.state == ss_active)
+                               {
+                                       prog->xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       prog->xstatement = st - prog->statements;
+                                       Host_Error("assignment to world entity in %s", PRVM_NAME);
+                                       return;
+                               }*/
+                               ed = PRVM_PROG_TO_EDICT(OPA->edict);
+                               OPC->_int = (qbyte *)((int *)ed->v + OPB->_int) - (qbyte *)prog->edictsfields;
+                               break;
+
+                       case OP_LOAD_F:
+                       case OP_LOAD_FLD:
+                       case OP_LOAD_ENT:
+                       case OP_LOAD_S:
+                       case OP_LOAD_FNC:
+#if PRVMBOUNDSCHECK
+                               if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->progs->entityfields))
+                               {
+                                       prog->xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       prog->xstatement = st - prog->statements;
+                                       PRVM_ERROR("%s attempted to read an invalid field in an edict (%i)\n", PRVM_NAME, OPB->_int);
+                                       return;
+                               }
+#endif
+                               ed = PRVM_PROG_TO_EDICT(OPA->edict);
+                               OPC->_int = ((eval_t *)((int *)ed->v + OPB->_int))->_int;
+                               break;
+
+                       case OP_LOAD_V:
+#if PRVMBOUNDSCHECK
+                               if (OPB->_int < 0 || OPB->_int + 2 >= prog->progs->entityfields)
+                               {
+                                       prog->xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       prog->xstatement = st - prog->statements;
+                                       PRVM_ERROR("%s attempted to read an invalid field in an edict (%i)\n", PRVM_NAME, OPB->_int);
+                                       return;
+                               }
+#endif
+                               ed = PRVM_PROG_TO_EDICT(OPA->edict);
+                               OPC->vector[0] = ((eval_t *)((int *)ed->v + OPB->_int))->vector[0];
+                               OPC->vector[1] = ((eval_t *)((int *)ed->v + OPB->_int))->vector[1];
+                               OPC->vector[2] = ((eval_t *)((int *)ed->v + OPB->_int))->vector[2];
+                               break;
+
+               //==================
+
+                       case OP_IFNOT:
+                               if (!OPA->_int)
+                                       st += st->b - 1;        // offset the s++
+                               break;
+
+                       case OP_IF:
+                               if (OPA->_int)
+                                       st += st->b - 1;        // offset the s++
+                               break;
+
+                       case OP_GOTO:
+                               st += st->a - 1;        // offset the s++
+                               break;
+
+                       case OP_CALL0:
+                       case OP_CALL1:
+                       case OP_CALL2:
+                       case OP_CALL3:
+                       case OP_CALL4:
+                       case OP_CALL5:
+                       case OP_CALL6:
+                       case OP_CALL7:
+                       case OP_CALL8:
+                               prog->xfunction->profile += profile - startprofile;
+                               startprofile = profile;
+                               prog->xstatement = st - prog->statements;
+                               prog->argc = st->op - OP_CALL0;
+                               if (!OPA->function)
+                                       PRVM_ERROR("NULL function in %s", PRVM_NAME);
+
+                               newf = &prog->functions[OPA->function];
+
+                               if (newf->first_statement < 0)
+                               {
+                                       // negative statements are built in functions
+                                       int builtinnumber = -newf->first_statement;
+                                       prog->xfunction->builtinsprofile++;
+                                       if (builtinnumber < prog->numbuiltins && prog->builtins[builtinnumber])
+                                               prog->builtins[builtinnumber]();
+                                       else
+                                               PRVM_ERROR("No such builtin #%i in %s", builtinnumber, PRVM_NAME);
+                               }
+                               else
+                                       st = prog->statements + PRVM_EnterFunction(newf);
+                               break;
+
+                       case OP_DONE:
+                       case OP_RETURN:
+                               prog->xfunction->profile += profile - startprofile;
+                               startprofile = profile;
+                               prog->xstatement = st - prog->statements;
+
+                               prog->globals[OFS_RETURN] = prog->globals[(unsigned short) st->a];
+                               prog->globals[OFS_RETURN+1] = prog->globals[(unsigned short) st->a+1];
+                               prog->globals[OFS_RETURN+2] = prog->globals[(unsigned short) st->a+2];
+
+                               st = prog->statements + PRVM_LeaveFunction();
+                               if (prog->depth <= exitdepth)
+                                       return;         // all done
+                               if (prog->trace != cachedpr_trace)
+                                       goto chooseexecprogram;
+                               break;
+
+                       case OP_STATE:
+                               if(prog->flag & PRVM_OP_STATE) 
+                               {
+                                       prog->xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       prog->xstatement = st - prog->statements;
+                                       ed = PRVM_PROG_TO_EDICT(PRVM_G_INT(prog->self->ofs));
+                                       PRVM_E_FLOAT(ed,PRVM_ED_FindFieldOffset ("nextthink")) = PRVM_G_FLOAT(PRVM_ED_FindGlobal("time")->ofs*4) + 0.1;
+                                       PRVM_E_FLOAT(ed,PRVM_ED_FindFieldOffset ("frame")) = OPA->_float;
+                                       *(func_t *)((qbyte*)ed->v + PRVM_ED_FindFieldOffset ("think")) = OPB->function;
+                               }
+                               else
+                                       PRVM_ERROR("OP_STATE not supported by %s\n", PRVM_NAME);
+                               break;
+                               
+// LordHavoc: to be enabled when Progs version 7 (or whatever it will be numbered) is finalized
+/*
+                       case OP_ADD_I:
+                               OPC->_int = OPA->_int + OPB->_int;
+                               break;
+                       case OP_ADD_IF:
+                               OPC->_int = OPA->_int + (int) OPB->_float;
+                               break;
+                       case OP_ADD_FI:
+                               OPC->_float = OPA->_float + (float) OPB->_int;
+                               break;
+                       case OP_SUB_I:
+                               OPC->_int = OPA->_int - OPB->_int;
+                               break;
+                       case OP_SUB_IF:
+                               OPC->_int = OPA->_int - (int) OPB->_float;
+                               break;
+                       case OP_SUB_FI:
+                               OPC->_float = OPA->_float - (float) OPB->_int;
+                               break;
+                       case OP_MUL_I:
+                               OPC->_int = OPA->_int * OPB->_int;
+                               break;
+                       case OP_MUL_IF:
+                               OPC->_int = OPA->_int * (int) OPB->_float;
+                               break;
+                       case OP_MUL_FI:
+                               OPC->_float = OPA->_float * (float) OPB->_int;
+                               break;
+                       case OP_MUL_VI:
+                               OPC->vector[0] = (float) OPB->_int * OPA->vector[0];
+                               OPC->vector[1] = (float) OPB->_int * OPA->vector[1];
+                               OPC->vector[2] = (float) OPB->_int * OPA->vector[2];
+                               break;
+                       case OP_DIV_VF:
+                               {
+                                       float temp = 1.0f / OPB->_float;
+                                       OPC->vector[0] = temp * OPA->vector[0];
+                                       OPC->vector[1] = temp * OPA->vector[1];
+                                       OPC->vector[2] = temp * OPA->vector[2];
+                               }
+                               break;
+                       case OP_DIV_I:
+                               OPC->_int = OPA->_int / OPB->_int;
+                               break;
+                       case OP_DIV_IF:
+                               OPC->_int = OPA->_int / (int) OPB->_float;
+                               break;
+                       case OP_DIV_FI:
+                               OPC->_float = OPA->_float / (float) OPB->_int;
+                               break;
+                       case OP_CONV_IF:
+                               OPC->_float = OPA->_int;
+                               break;
+                       case OP_CONV_FI:
+                               OPC->_int = OPA->_float;
+                               break;
+                       case OP_BITAND_I:
+                               OPC->_int = OPA->_int & OPB->_int;
+                               break;
+                       case OP_BITOR_I:
+                               OPC->_int = OPA->_int | OPB->_int;
+                               break;
+                       case OP_BITAND_IF:
+                               OPC->_int = OPA->_int & (int)OPB->_float;
+                               break;
+                       case OP_BITOR_IF:
+                               OPC->_int = OPA->_int | (int)OPB->_float;
+                               break;
+                       case OP_BITAND_FI:
+                               OPC->_float = (int)OPA->_float & OPB->_int;
+                               break;
+                       case OP_BITOR_FI:
+                               OPC->_float = (int)OPA->_float | OPB->_int;
+                               break;
+                       case OP_GE_I:
+                               OPC->_float = OPA->_int >= OPB->_int;
+                               break;
+                       case OP_LE_I:
+                               OPC->_float = OPA->_int <= OPB->_int;
+                               break;
+                       case OP_GT_I:
+                               OPC->_float = OPA->_int > OPB->_int;
+                               break;
+                       case OP_LT_I:
+                               OPC->_float = OPA->_int < OPB->_int;
+                               break;
+                       case OP_AND_I:
+                               OPC->_float = OPA->_int && OPB->_int;
+                               break;
+                       case OP_OR_I:
+                               OPC->_float = OPA->_int || OPB->_int;
+                               break;
+                       case OP_GE_IF:
+                               OPC->_float = (float)OPA->_int >= OPB->_float;
+                               break;
+                       case OP_LE_IF:
+                               OPC->_float = (float)OPA->_int <= OPB->_float;
+                               break;
+                       case OP_GT_IF:
+                               OPC->_float = (float)OPA->_int > OPB->_float;
+                               break;
+                       case OP_LT_IF:
+                               OPC->_float = (float)OPA->_int < OPB->_float;
+                               break;
+                       case OP_AND_IF:
+                               OPC->_float = (float)OPA->_int && OPB->_float;
+                               break;
+                       case OP_OR_IF:
+                               OPC->_float = (float)OPA->_int || OPB->_float;
+                               break;
+                       case OP_GE_FI:
+                               OPC->_float = OPA->_float >= (float)OPB->_int;
+                               break;
+                       case OP_LE_FI:
+                               OPC->_float = OPA->_float <= (float)OPB->_int;
+                               break;
+                       case OP_GT_FI:
+                               OPC->_float = OPA->_float > (float)OPB->_int;
+                               break;
+                       case OP_LT_FI:
+                               OPC->_float = OPA->_float < (float)OPB->_int;
+                               break;
+                       case OP_AND_FI:
+                               OPC->_float = OPA->_float && (float)OPB->_int;
+                               break;
+                       case OP_OR_FI:
+                               OPC->_float = OPA->_float || (float)OPB->_int;
+                               break;
+                       case OP_NOT_I:
+                               OPC->_float = !OPA->_int;
+                               break;
+                       case OP_EQ_I:
+                               OPC->_float = OPA->_int == OPB->_int;
+                               break;
+                       case OP_EQ_IF:
+                               OPC->_float = (float)OPA->_int == OPB->_float;
+                               break;
+                       case OP_EQ_FI:
+                               OPC->_float = OPA->_float == (float)OPB->_int;
+                               break;
+                       case OP_NE_I:
+                               OPC->_float = OPA->_int != OPB->_int;
+                               break;
+                       case OP_NE_IF:
+                               OPC->_float = (float)OPA->_int != OPB->_float;
+                               break;
+                       case OP_NE_FI:
+                               OPC->_float = OPA->_float != (float)OPB->_int;
+                               break;
+                       case OP_STORE_I:
+                               OPB->_int = OPA->_int;
+                               break;
+                       case OP_STOREP_I:
+#if PRBOUNDSCHECK
+                               if (OPB->_int < 0 || OPB->_int + 4 > pr_edictareasize)
+                               {
+                                       pr_xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       pr_xstatement = st - pr_statements;
+                                       Host_Error("Progs attempted to write to an out of bounds edict\n");
+                                       return;
+                               }
+#endif
+                               ptr = (eval_t *)((qbyte *)sv.edictsfields + OPB->_int);
+                               ptr->_int = OPA->_int;
+                               break;
+                       case OP_LOAD_I:
+#if PRBOUNDSCHECK
+                               if (OPA->edict < 0 || OPA->edict >= pr_edictareasize)
+                               {
+                                       pr_xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       pr_xstatement = st - pr_statements;
+                                       Host_Error("Progs attempted to read an out of bounds edict number\n");
+                                       return;
+                               }
+                               if (OPB->_int < 0 || OPB->_int >= progs->entityfields)
+                               {
+                                       pr_xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       pr_xstatement = st - pr_statements;
+                                       Host_Error("Progs attempted to read an invalid field in an edict\n");
+                                       return;
+                               }
+#endif
+                               ed = PROG_TO_EDICT(OPA->edict);
+                               OPC->_int = ((eval_t *)((int *)ed->v + OPB->_int))->_int;
+                               break;
+
+                       case OP_GSTOREP_I:
+                       case OP_GSTOREP_F:
+                       case OP_GSTOREP_ENT:
+                       case OP_GSTOREP_FLD:            // integers
+                       case OP_GSTOREP_S:
+                       case OP_GSTOREP_FNC:            // pointers
+#if PRBOUNDSCHECK
+                               if (OPB->_int < 0 || OPB->_int >= pr_globaldefs)
+                               {
+                                       pr_xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       pr_xstatement = st - pr_statements;
+                                       Host_Error("Progs attempted to write to an invalid indexed global\n");
+                                       return;
+                               }
+#endif
+                               pr_globals[OPB->_int] = OPA->_float;
+                               break;
+                       case OP_GSTOREP_V:
+#if PRBOUNDSCHECK
+                               if (OPB->_int < 0 || OPB->_int + 2 >= pr_globaldefs)
+                               {
+                                       pr_xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       pr_xstatement = st - pr_statements;
+                                       Host_Error("Progs attempted to write to an invalid indexed global\n");
+                                       return;
+                               }
+#endif
+                               pr_globals[OPB->_int  ] = OPA->vector[0];
+                               pr_globals[OPB->_int+1] = OPA->vector[1];
+                               pr_globals[OPB->_int+2] = OPA->vector[2];
+                               break;
+
+                       case OP_GADDRESS:
+                               i = OPA->_int + (int) OPB->_float;
+#if PRBOUNDSCHECK
+                               if (i < 0 || i >= pr_globaldefs)
+                               {
+                                       pr_xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       pr_xstatement = st - pr_statements;
+                                       Host_Error("Progs attempted to address an out of bounds global\n");
+                                       return;
+                               }
+#endif
+                               OPC->_float = pr_globals[i];
+                               break;
+
+                       case OP_GLOAD_I:
+                       case OP_GLOAD_F:
+                       case OP_GLOAD_FLD:
+                       case OP_GLOAD_ENT:
+                       case OP_GLOAD_S:
+                       case OP_GLOAD_FNC:
+#if PRBOUNDSCHECK
+                               if (OPA->_int < 0 || OPA->_int >= pr_globaldefs)
+                               {
+                                       pr_xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       pr_xstatement = st - pr_statements;
+                                       Host_Error("Progs attempted to read an invalid indexed global\n");
+                                       return;
+                               }
+#endif
+                               OPC->_float = pr_globals[OPA->_int];
+                               break;
+
+                       case OP_GLOAD_V:
+#if PRBOUNDSCHECK
+                               if (OPA->_int < 0 || OPA->_int + 2 >= pr_globaldefs)
+                               {
+                                       pr_xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       pr_xstatement = st - pr_statements;
+                                       Host_Error("Progs attempted to read an invalid indexed global\n");
+                                       return;
+                               }
+#endif
+                               OPC->vector[0] = pr_globals[OPA->_int  ];
+                               OPC->vector[1] = pr_globals[OPA->_int+1];
+                               OPC->vector[2] = pr_globals[OPA->_int+2];
+                               break;
+
+                       case OP_BOUNDCHECK:
+                               if (OPA->_int < 0 || OPA->_int >= st->b)
+                               {
+                                       pr_xfunction->profile += profile - startprofile;
+                                       startprofile = profile;
+                                       pr_xstatement = st - pr_statements;
+                                       Host_Error("Progs boundcheck failed at line number %d, value is < 0 or >= %d\n", st->b, st->c);
+                                       return;
+                               }
+                               break;
+
+*/
+
+                       default:
+                               prog->xfunction->profile += profile - startprofile;
+                               startprofile = profile;
+                               prog->xstatement = st - prog->statements;
+                               PRVM_ERROR ("Bad opcode %i in %s", st->op, PRVM_NAME);
+                       }
+               }
+