--- /dev/null
+/* 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
--- /dev/null
+
+/* 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
--- /dev/null
+/*
+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
--- /dev/null
+// 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)
+{
+}
+
--- /dev/null
+/*
+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));
+}
+*/
+
--- /dev/null
+/*
+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"
+ }
+ }
+}
--- /dev/null
+
+// 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);
+ }
+ }
+