From 29de7088f191d21a0c7dd5396cb5cead032c2ac2 Mon Sep 17 00:00:00 2001 From: black Date: Mon, 6 Oct 2003 18:52:56 +0000 Subject: [PATCH] Adding the new vm git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@3541 d7cf8633-e32d-0410-b094-e92efae38249 --- clprogdefs.h | 12 + mprogdefs.h | 15 + progsvm.h | 462 ++++++++++++ prvm_cmds.c | 141 ++++ prvm_edict.c | 1700 ++++++++++++++++++++++++++++++++++++++++++++ prvm_exec.c | 435 ++++++++++++ prvm_execprogram.h | 614 ++++++++++++++++ 7 files changed, 3379 insertions(+) create mode 100644 clprogdefs.h create mode 100644 mprogdefs.h create mode 100644 progsvm.h create mode 100644 prvm_cmds.c create mode 100644 prvm_edict.c create mode 100644 prvm_exec.c create mode 100644 prvm_execprogram.h diff --git a/clprogdefs.h b/clprogdefs.h new file mode 100644 index 00000000..c84f2636 --- /dev/null +++ b/clprogdefs.h @@ -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 index 00000000..d06e2700 --- /dev/null +++ b/mprogdefs.h @@ -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 index 00000000..54e86409 --- /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 index 00000000..cd780ce7 --- /dev/null +++ b/prvm_cmds.c @@ -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 index 00000000..ec2f2b18 --- /dev/null +++ b/prvm_edict.c @@ -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 ; iprogs->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 ; iprogs->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 ; iprogs->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 ; iprogs->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 ; iprogs->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 ; iprogs->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 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 ; iprogs->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 ; jtype, (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 \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 ; inum_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 \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 \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString(Cmd_Argv(1))) + return; + + if(prog->count_edicts) + prog->count_edicts(); + else + { + active = 0; + for (i=0 ; inum_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 ; iprogs->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 ; iprogs->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 ; iprogs->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 ; iprogs->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 \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 \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 index 00000000..2ea778eb --- /dev/null +++ b/prvm_exec.c @@ -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 ("\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 \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString(Cmd_Argv(1))) + return; + + num = 0; + do + { + max = 0; + best = NULL; + for (i=0 ; iprogs->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 ; inumparms ; i++) + { + for (j=0 ; jparm_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 index 00000000..3439affa --- /dev/null +++ b/prvm_execprogram.h @@ -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); + } + } + -- 2.39.2