/* 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 "engine.h" #include "progsvm.h" char *prvm_opnames[] = { "^5DONE", "MUL_F", "MUL_V", "MUL_FV", "MUL_VF", "DIV", "ADD_F", "ADD_V", "SUB_F", "SUB_V", "^2EQ_F", "^2EQ_V", "^2EQ_S", "^2EQ_E", "^2EQ_FNC", "^2NE_F", "^2NE_V", "^2NE_S", "^2NE_E", "^2NE_FNC", "^2LE", "^2GE", "^2LT", "^2GT", "^6FIELD_F", "^6FIELD_V", "^6FIELD_S", "^6FIELD_ENT", "^6FIELD_FLD", "^6FIELD_FNC", "^1ADDRESS", "STORE_F", "STORE_V", "STORE_S", "STORE_ENT", "STORE_FLD", "STORE_FNC", "^1STOREP_F", "^1STOREP_V", "^1STOREP_S", "^1STOREP_ENT", "^1STOREP_FLD", "^1STOREP_FNC", "^5RETURN", "^2NOT_F", "^2NOT_V", "^2NOT_S", "^2NOT_ENT", "^2NOT_FNC", "^5IF", "^5IFNOT", "^3CALL0", "^3CALL1", "^3CALL2", "^3CALL3", "^3CALL4", "^3CALL5", "^3CALL6", "^3CALL7", "^3CALL8", "^1STATE", "^5GOTO", "^2AND", "^2OR", "BITAND", "BITOR" }; char *PRVM_GlobalString (int ofs); char *PRVM_GlobalStringNoContents (int ofs); //============================================================================= /* ================= PRVM_PrintStatement ================= */ extern cvar_t *prvm_statementprofiling; void PRVM_PrintStatement (dstatement_t *s) { size_t i; int opnum = (int)(s - prog->statements); Msg("s%i: ", opnum); if( prog->statement_linenums ) Msg( "%s:%i: ", PRVM_GetString( prog->xfunction->s_file ), prog->statement_linenums[ opnum ] ); if (prvm_statementprofiling->value) Msg("%7.0f ", prog->statement_profile[s - prog->statements]); if ( (unsigned)s->op < sizeof(prvm_opnames)/sizeof(prvm_opnames[0])) { Msg("%s ", prvm_opnames[s->op]); i = strlen(prvm_opnames[s->op]); // don't count a preceding color tag when padding the name if (prvm_opnames[s->op][0] == STRING_COLOR_TAG) i -= 2; for ( ; i<10 ; i++) Msg(" "); } if (s->op == OP_IF || s->op == OP_IFNOT) Msg("%s, s%i",PRVM_GlobalString((unsigned short) s->a),(signed short)s->b + opnum); else if (s->op == OP_GOTO) Msg("s%i",(signed short)s->a + opnum); else if ( (unsigned)(s->op - OP_STORE_F) < 6) { Msg(PRVM_GlobalString((unsigned short) s->a)); Msg(", "); Msg(PRVM_GlobalStringNoContents((unsigned short) s->b)); } else if (s->op == OP_ADDRESS || (unsigned)(s->op - OP_LOAD_F) < 6) { if (s->a) Msg(PRVM_GlobalString((unsigned short) s->a)); if (s->b) { Msg(", "); Msg(PRVM_GlobalStringNoContents((unsigned short) s->b)); } if (s->c) { Msg(", "); Msg(PRVM_GlobalStringNoContents((unsigned short) s->c)); } } else { if (s->a) Msg(PRVM_GlobalString((unsigned short) s->a)); if (s->b) { Msg(", "); Msg(PRVM_GlobalString((unsigned short) s->b)); } if (s->c) { Msg(", "); Msg(PRVM_GlobalStringNoContents((unsigned short) s->c)); } } Msg("\n"); } void PRVM_PrintFunctionStatements (const char *name) { int i, firststatement, endstatement; mfunction_t *func; func = PRVM_ED_FindFunction (name); if (!func) { Msg("%s progs: no function named %s\n", PRVM_NAME, name); return; } firststatement = func->first_statement; if (firststatement < 0) { Msg("%s progs: function %s is builtin #%i\n", PRVM_NAME, name, -firststatement); return; } // find the end statement endstatement = prog->progs->numstatements; for (i = 0;i < prog->progs->numfunctions;i++) if (endstatement > prog->functions[i].first_statement && firststatement < prog->functions[i].first_statement) endstatement = prog->functions[i].first_statement; // now print the range of statements Msg("%s progs: disassembly of function %s (statements %i-%i):\n", PRVM_NAME, name, firststatement, endstatement); for (i = firststatement;i < endstatement;i++) { PRVM_PrintStatement(prog->statements + i); prog->statement_profile[i] = 0; } } /* ============ PRVM_PrintFunction_f ============ */ void PRVM_PrintFunction_f (void) { if (Cmd_Argc() != 3) { Msg("usage: prvm_printfunction \n"); return; } PRVM_Begin; if(!PRVM_SetProgFromString(Cmd_Argv(1))) return; PRVM_PrintFunctionStatements(Cmd_Argv(2)); PRVM_End; } /* ============ 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) Msg("\n"); else Msg("%12s : %s : statement %i\n", PRVM_GetString(f->s_file), PRVM_GetString(f->s_name), prog->stack[i].s - f->first_statement); } } void PRVM_Profile (int maxfunctions, int mininstructions) { mfunction_t *f, *best; int i, num; double max; Msg( "%s Profile:\n[CallCount] [Statements] [BuiltinCost]\n", PRVM_NAME ); num = 0; do { max = 0; best = NULL; for (i=0 ; iprogs->numfunctions ; i++) { f = &prog->functions[i]; if (max < f->profile + f->builtinsprofile + f->callcount) { max = f->profile + f->builtinsprofile + f->callcount; best = f; } } if (best) { if (num < maxfunctions && max >= mininstructions) { if (best->first_statement < 0) Msg("%9.0f ----- builtin ----- %s\n", best->callcount, PRVM_GetString(best->s_name)); else Msg("%9.0f %9.0f %9.0f %s\n", best->callcount, best->profile, best->builtinsprofile, PRVM_GetString(best->s_name)); } num++; best->profile = 0; best->builtinsprofile = 0; best->callcount = 0; } } while (best); } /* ============ PRVM_Profile_f ============ */ void PRVM_Profile_f (void) { int howmany; howmany = 1<<30; if (Cmd_Argc() == 3) howmany = atoi(Cmd_Argv(2)); else if (Cmd_Argc() != 2) { Msg("prvm_profile \n"); return; } PRVM_Begin; if(!PRVM_SetProgFromString(Cmd_Argv(1))) return; PRVM_Profile(howmany, 1); PRVM_End; } void PRVM_CrashAll() { int i; prvm_prog_t *oldprog = prog; for(i = 0; i < PRVM_MAXPROGS; i++) { if(!PRVM_ProgLoaded(i)) continue; PRVM_SetProg(i); PRVM_Crash(); } prog = oldprog; } void PRVM_PrintState(void) { int i; if (prog->xfunction) { for (i = -7; i <= 0;i++) if (prog->xstatement + i >= prog->xfunction->first_statement) PRVM_PrintStatement (prog->statements + prog->xstatement + i); } else Msg("null function executing??\n"); PRVM_StackTrace (); } void PRVM_Crash() { if (prog == NULL) return; if( prog->depth > 0 ) { Msg("QuakeC crash report for %s:\n", PRVM_NAME); PRVM_PrintState(); } // dump the stack so host_error can shutdown functions prog->depth = 0; prog->localstack_used = 0; // reset the prog pointer prog = NULL; } /* ============================================================================ 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", 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", PRVM_NAME); for (i=0 ; i < c ; i++) prog->localstack[prog->localstack_used+i] = ((int *)prog->globals.gp)[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.gp)[o] = ((int *)prog->globals.gp)[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", 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", PRVM_NAME); for (i=0 ; i < c ; i++) ((int *)prog->globals.gp)[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 ((prvm_eval_t *)&prog->globals.gp[(word) st->a]) #define OPB ((prvm_eval_t *)&prog->globals.gp[(word) st->b]) #define OPC ((prvm_eval_t *)&prog->globals.gp[(word) st->c]) extern cvar_t *prvm_boundscheck; extern cvar_t *prvm_traceqc; extern cvar_t *prvm_statementprofiling; 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, *startst; mfunction_t *f, *newf; edict_t *ed; prvm_eval_t *ptr, *_switch; int switchtype, exitdepth; int i, jumpcount, cachedpr_trace; if (!fnum || fnum >= (unsigned int)prog->progs->numfunctions) { if (prog->pev && PRVM_G_INT(prog->pev->ofs)) PRVM_ED_Print(PRVM_PROG_TO_EDICT(PRVM_G_INT(prog->pev->ofs))); PRVM_ERROR ("PRVM_ExecuteProgram: %s", errormessage); return; } f = &prog->functions[fnum]; prog->trace = prvm_traceqc->value; // we know we're done when pr_depth drops to this exitdepth = prog->depth; // make a stack frame st = &prog->statements[PRVM_EnterFunction (f)]; // save the starting statement pointer for profiling // (when the function exits or jumps, the (st - startst) integer value is // added to the function's profile counter) startst = st; // instead of counting instructions, we count jumps jumpcount = 0; // add one to the callcount of this function because otherwise engine-called functions aren't counted prog->xfunction->callcount++; chooseexecprogram: cachedpr_trace = prog->trace; while (1) { st++; if (prog->trace) PRVM_PrintStatement(st); if (prvm_statementprofiling->value) prog->statement_profile[st - prog->statements]++; 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: // don't divide by zero if( OPB->_float == 0.0f ) OPC->_float = 0.0f; else OPC->_float = OPA->_float / OPB->_float; break; case OP_DIV_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_BITAND: OPC->_float = (float)((int)OPA->_float & (int)OPB->_float); break; case OP_BITOR: OPC->_float = (float)((int)OPA->_float | (int)OPB->_float); break; case OP_GE: OPC->_float = (float)OPA->_float >= OPB->_float; break; case OP_GE_I: OPC->_int = (int)(OPA->_int >= OPB->_int); break; case OP_GE_IF: OPC->_float = (float)(OPA->_int >= OPB->_float); break; case OP_GE_FI: OPC->_float = (float)(OPA->_float >= OPB->_int); break; case OP_LE: OPC->_float = OPA->_float <= OPB->_float; break; case OP_LE_I: OPC->_int = (int)(OPA->_int <= OPB->_int); break; case OP_LE_IF: OPC->_float = (float)(OPA->_int <= OPB->_float); break; case OP_LE_FI: OPC->_float = (float)(OPA->_float <= OPB->_int); break; case OP_GT: OPC->_float = OPA->_float > OPB->_float; break; case OP_GT_I: OPC->_int = (int)(OPA->_int > OPB->_int); break; case OP_GT_IF: OPC->_float = (float)(OPA->_int > OPB->_float); break; case OP_GT_FI: OPC->_float = (float)(OPA->_float > OPB->_int); break; case OP_LT: OPC->_float = OPA->_float < OPB->_float; break; case OP_LT_I: OPC->_int = (int)(OPA->_int < OPB->_int); break; case OP_LT_IF: OPC->_float = (float)(OPA->_int < OPB->_float); break; case OP_LT_FI: OPC->_float = (float)(OPA->_float < OPB->_int); 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 || !*PRVM_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 = (float)(OPA->_float == OPB->_float); break; case OP_EQ_IF: OPC->_float = (float)(OPA->_int == OPB->_float); break; case OP_EQ_FI: OPC->_float = (float)(OPA->_float == OPB->_int); 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: if (OPA->string == OPB->string) OPC->_float = true; // try fast method first else if (!OPA->string) { if (!OPB->string || !*PRVM_GetString(OPB->string)) OPC->_float = true; else OPC->_float = false; } else if (!OPB->string) { if (!OPA->string || !*PRVM_GetString(OPA->string)) OPC->_float = true; else OPC->_float = false; } else OPC->_float = !strcmp(PRVM_GetString(OPA->string), PRVM_GetString(OPB->string)); break; case OP_EQ_E: OPC->_float = (float)(OPA->_int == OPB->_int); break; case OP_EQ_FNC: OPC->_float = OPA->function == OPB->function; break; case OP_NE_F: OPC->_float = (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: if (OPA->string == OPB->string) OPC->_float = false; // try fast method first else if (!OPA->string) { if (!OPB->string || !*PRVM_GetString(OPB->string)) OPC->_float = false; else OPC->_float = true; } else if (!OPB->string) { if (!OPA->string || !*PRVM_GetString(OPA->string)) OPC->_float = false; else OPC->_float = true; } else OPC->_float = strcmp(PRVM_GetString(OPA->string), PRVM_GetString(OPB->string)); break; case OP_NE_E: OPC->_float = (float)(OPA->_int != OPB->_int); break; case OP_NE_FNC: OPC->_float = (float)(OPA->function != OPB->function); break; case OP_STORE_IF: OPB->_float = (float)OPA->_int; break; case OP_STORE_FI: OPB->_int = (int)OPA->_float; break; case OP_STORE_F: case OP_STORE_I: 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->vector[0] = OPA->vector[0]; OPB->vector[1] = OPA->vector[1]; OPB->vector[2] = OPA->vector[2]; break; case OP_STOREP_IF: PRVM_CHECK_PTR(OPB, 4); ptr = PRVM_ED_POINTER(OPB); ptr->_float = (float)OPA->_int; break; case OP_STOREP_FI: PRVM_CHECK_PTR(OPB, 4); ptr = PRVM_ED_POINTER(OPB); ptr->_int = (int)OPA->_float; break; case OP_STOREP_I: PRVM_CHECK_PTR(OPB, 4); ptr = PRVM_ED_POINTER(OPB); ptr->_int = OPA->_int; break; case OP_STOREP_F: case OP_STOREP_ENT: case OP_STOREP_FLD: // integers case OP_STOREP_S: case OP_STOREP_FNC: // pointers PRVM_CHECK_PTR(OPB, 4); ptr = PRVM_ED_POINTER(OPB); ptr->_int = OPA->_int; break; case OP_STOREP_V: PRVM_CHECK_PTR(OPB, 12); ptr = PRVM_ED_POINTER(OPB); ptr->vector[0] = OPA->vector[0]; ptr->vector[1] = OPA->vector[1]; ptr->vector[2] = OPA->vector[2]; break; case OP_STOREP_C: //store character in a string PRVM_CHECK_PTR(OPB, 4); ptr = PRVM_ED_POINTER(OPB); *(byte *)ptr = (char)OPA->_float; break; case OP_MULSTORE_F: OPB->_float *= OPA->_float; break; case OP_MULSTORE_V: OPB->vector[0] *= OPA->_float; OPB->vector[1] *= OPA->_float; OPB->vector[2] *= OPA->_float; break; case OP_MULSTOREP_F: PRVM_CHECK_PTR(OPB, 4); ptr = PRVM_ED_POINTER(OPB); OPC->_float = (ptr->_float *= OPA->_float); break; case OP_MULSTOREP_V: PRVM_CHECK_PTR(OPB, 12); ptr = PRVM_ED_POINTER(OPB); OPC->vector[0] = (ptr->vector[0] *= OPA->_float); OPC->vector[0] = (ptr->vector[1] *= OPA->_float); OPC->vector[0] = (ptr->vector[2] *= OPA->_float); break; case OP_DIVSTORE_F: OPB->_float /= OPA->_float; break; case OP_DIVSTOREP_F: PRVM_CHECK_PTR(OPB, 4); ptr = PRVM_ED_POINTER(OPB); OPC->_float = (ptr->_float /= OPA->_float); break; case OP_ADDSTORE_F: OPB->_float += OPA->_float; break; case OP_ADDSTORE_V: OPB->vector[0] += OPA->vector[0]; OPB->vector[1] += OPA->vector[1]; OPB->vector[2] += OPA->vector[2]; break; case OP_ADDSTOREP_F: PRVM_CHECK_PTR(OPB, 4); ptr = PRVM_ED_POINTER(OPB); OPC->_float = (ptr->_float += OPA->_float); break; case OP_ADDSTOREP_V: PRVM_CHECK_PTR(OPB, 12); ptr = PRVM_ED_POINTER(OPB); OPC->vector[0] = (ptr->vector[0] += OPA->vector[0]); OPC->vector[1] = (ptr->vector[1] += OPA->vector[1]); OPC->vector[2] = (ptr->vector[2] += OPA->vector[2]); break; case OP_SUBSTORE_F: OPB->_float -= OPA->_float; break; case OP_SUBSTORE_V: OPB->vector[0] -= OPA->vector[0]; OPB->vector[1] -= OPA->vector[1]; OPB->vector[2] -= OPA->vector[2]; break; case OP_SUBSTOREP_F: PRVM_CHECK_PTR(OPB, 4); ptr = PRVM_ED_POINTER(OPB); OPC->_float = (ptr->_float -= OPA->_float); break; case OP_SUBSTOREP_V: PRVM_CHECK_PTR(OPB, 12); ptr = PRVM_ED_POINTER(OPB); OPC->vector[0] = (ptr->vector[0] -= OPA->vector[0]); OPC->vector[1] = (ptr->vector[1] -= OPA->vector[1]); OPC->vector[2] = (ptr->vector[2] -= OPA->vector[2]); break; case OP_ADDRESS: if (prvm_boundscheck->value && ((uint)(OPB->_int) >= (uint)(prog->progs->entityfields))) { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR("%s attempted to address an invalid field (%i) in an edict", PRVM_NAME, OPB->_int); return; } if (OPA->edict == 0 && prog->protect_world) { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR("forbidden assignment to null/world entity in %s", PRVM_NAME); return; } ed = PRVM_PROG_TO_EDICT(OPA->edict); OPC->_int = (byte *)((int *)ed->progs.vp + OPB->_int) - (byte *)prog->edictsfields; break; case OP_LOAD_I: case OP_LOAD_F: case OP_LOAD_FLD: case OP_LOAD_ENT: case OP_LOAD_S: case OP_LOAD_FNC: if (prvm_boundscheck->value && ((uint)(OPB->_int) >= (uint)(prog->progs->entityfields))) { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR("%s attempted to read an invalid field in an edict (%i)", PRVM_NAME, OPB->_int); return; } ed = PRVM_PROG_TO_EDICT(OPA->edict); OPC->_int = ((prvm_eval_t *)((int *)ed->progs.vp + OPB->_int))->_int; break; case OP_LOAD_V: if (prvm_boundscheck->value && (OPB->_int < 0 || OPB->_int + 2 >= prog->progs->entityfields)) { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR("%s attempted to read an invalid field in an edict (%i)", PRVM_NAME, OPB->_int); return; } ed = PRVM_PROG_TO_EDICT(OPA->edict); OPC->vector[0] = ((prvm_eval_t *)((int *)ed->progs.vp + OPB->_int))->vector[0]; OPC->vector[1] = ((prvm_eval_t *)((int *)ed->progs.vp + OPB->_int))->vector[1]; OPC->vector[2] = ((prvm_eval_t *)((int *)ed->progs.vp + OPB->_int))->vector[2]; break; case OP_IFNOTS: if (!OPA->string || !*PRVM_GetString(OPA->string)) { prog->xfunction->profile += (st - startst); st += st->b - 1; // offset the s++ startst = st; PRVM_CHECK_INFINITE(); } break; case OP_IFNOT: if (!OPA->_int) { prog->xfunction->profile += (st - startst); st += st->b - 1; // offset the s++ startst = st; PRVM_CHECK_INFINITE(); } break; case OP_IFS: if (OPA->string && *PRVM_GetString(OPA->string)) { prog->xfunction->profile += (st - startst); st += st->b - 1; // offset the s++ startst = st; PRVM_CHECK_INFINITE(); } break; case OP_IF: if (OPA->_int) { prog->xfunction->profile += (st - startst); st += st->b - 1; // offset the s++ startst = st; PRVM_CHECK_INFINITE(); } break; case OP_GOTO: prog->xfunction->profile += (st - startst); st += st->a - 1; // offset the s++ startst = st; PRVM_CHECK_INFINITE(); 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 += (st - startst); startst = st; 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]; newf->callcount++; 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); startst = st; break; case OP_DONE: case OP_RETURN: prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; prog->globals.gp[OFS_RETURN+0] = prog->globals.gp[(word) st->a+0]; prog->globals.gp[OFS_RETURN+1] = prog->globals.gp[(word) st->a+1]; prog->globals.gp[OFS_RETURN+2] = prog->globals.gp[(word) st->a+2]; st = prog->statements + PRVM_LeaveFunction(); startst = st; if (prog->depth <= exitdepth) return; // all done if (prog->trace != cachedpr_trace) goto chooseexecprogram; break; case OP_STATE: if(prog->flag & PRVM_OP_STATE) { ed = PRVM_PROG_TO_EDICT(PRVM_G_INT(prog->pev->ofs)); PRVM_E_FLOAT(ed, PRVM_ED_FindField ("nextthink")->ofs) = *prog->time + 0.1; PRVM_E_FLOAT(ed, PRVM_ED_FindField ("frame")->ofs) = OPA->_float; *(func_t *)((float*)ed->progs.vp + PRVM_ED_FindField ("think")->ofs) = OPB->function; } else { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR("OP_STATE not supported by %s", PRVM_NAME); } break; case OP_ADD_I: OPC->_int = OPA->_int + OPB->_int; break; case OP_ADD_FI: OPC->_float = OPA->_float + (float)OPB->_int; break; case OP_ADD_IF: OPC->_float = (float)OPA->_int + OPB->_float; break; case OP_SUB_I: OPC->_int = OPA->_int - OPB->_int; break; case OP_SUB_FI: OPC->_float = OPA->_float - (float)OPB->_int; break; case OP_SUB_IF: OPC->_float = (float)OPA->_int - OPB->_float; break; case OP_CONV_ITOF: OPC->_float = (float)OPA->_int; break; case OP_CONV_FTOI: OPC->_int = (int)OPA->_float; break; case OP_CP_ITOF: ptr = PRVM_EV_POINTER(OPA); OPC->_float = (float)ptr->_int; break; case OP_CP_FTOI: ptr = PRVM_EV_POINTER(OPA); OPC->_int = (int)ptr->_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_MUL_I: OPC->_int = OPA->_int * OPB->_int; break; case OP_DIV_I: // don't divide by zero if (OPB->_int == 0) OPC->_int = 0; else OPC->_int = OPA->_int / OPB->_int; break; case OP_EQ_I: OPC->_int = (OPA->_int == OPB->_int); break; case OP_NE_I: OPC->_int = (OPA->_int != OPB->_int); break; case OP_GLOBAL_ADD: ed = PRVM_PROG_TO_EDICT(OPA->edict); OPC->_int = (byte *)((int *)OPB->_int) - (byte *)prog->edictsfields; break; case OP_POINTER_ADD: OPC->_int = OPA->_int + OPB->_int * 4; break; case OP_LOADA_I: case OP_LOADA_F: case OP_LOADA_FLD: case OP_LOADA_ENT: case OP_LOADA_S: case OP_LOADA_FNC: ptr = (prvm_eval_t *)(&OPA->_int + OPB->_int); OPC->_int = ptr->_int; break; case OP_LOADA_V: ptr = (prvm_eval_t *)(&OPA->_int + OPB->_int); OPC->vector[0] = ptr->vector[0]; OPC->vector[1] = ptr->vector[1]; OPC->vector[2] = ptr->vector[2]; break; case OP_ADD_SF: OPC->_int = OPA->_int + (int)OPB->_float; break; case OP_SUB_S: OPC->_int = OPA->_int - OPB->_int; break; case OP_LOADP_C: ptr = PRVM_EM_POINTER(OPA->_int + (int)OPB->_float); OPC->_float = *(unsigned char *)ptr; break; case OP_LOADP_I: case OP_LOADP_F: case OP_LOADP_FLD: case OP_LOADP_ENT: case OP_LOADP_S: case OP_LOADP_FNC: ptr = PRVM_EM_POINTER(OPA->_int + OPB->_int); OPC->_int = ptr->_int; break; case OP_LOADP_V: ptr = PRVM_EM_POINTER(OPA->_int + OPB->_int); OPC->vector[0] = ptr->vector[0]; OPC->vector[1] = ptr->vector[1]; OPC->vector[2] = ptr->vector[2]; break; case OP_POWER_I: OPC->_int = OPA->_int ^ OPB->_int; break; case OP_RSHIFT_I: OPC->_int = OPA->_int >> OPB->_int; break; case OP_LSHIFT_I: OPC->_int = OPA->_int << OPB->_int; break; case OP_FETCH_GBL_F: case OP_FETCH_GBL_S: case OP_FETCH_GBL_E: case OP_FETCH_G_FNC: i = (int)OPB->_float; if(prvm_boundscheck->value && (i < 0 || i > ((prvm_eval_t *)&prog->globals.gp[st->a-1])->_int)) { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR ("%s Progs array index out of bounds", PRVM_NAME); return; } ptr = (prvm_eval_t *)&prog->globals.gp[(word)st->a + i]; OPC->_int = ptr->_int; break; case OP_FETCH_GBL_V: i = (int)OPB->_float; if(prvm_boundscheck->value && (i < 0 || i > ((prvm_eval_t *)&prog->globals.gp[st->a-1])->_int)) { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR ("%s Progs array index out of bounds", PRVM_NAME); return; } ptr = (prvm_eval_t *)&prog->globals.gp[(word)st->a + ((int)OPB->_float)*3]; OPC->vector[0] = ptr->vector[0]; OPC->vector[1] = ptr->vector[1]; OPC->vector[2] = ptr->vector[2]; break; case OP_BITSET: OPB->_float = (float)((int)OPB->_float | (int)OPA->_float); break; case OP_BITSETP: ptr = PRVM_ED_POINTER(OPB); ptr->_float = (float)((int)ptr->_float | (int)OPA->_float); break; case OP_BITCLR: OPB->_float = (float)((int)OPB->_float & ~((int)OPA->_float)); break; case OP_BITCLRP: ptr = PRVM_ED_POINTER(OPB); ptr->_float = (float)((int)ptr->_float & ~((int)OPA->_float)); break; case OP_SWITCH_F: case OP_SWITCH_V: case OP_SWITCH_S: case OP_SWITCH_E: case OP_SWITCH_FNC: _switch = OPA; switchtype = st->op; prog->xfunction->profile += (st - startst); st += st->b - 1; // offset the s++ startst = st; PRVM_CHECK_INFINITE(); break; case OP_CASE: switch(switchtype) { case OP_SWITCH_F: if (_switch->_float == OPA->_float) { prog->xfunction->profile += (st - startst); st += st->b - 1; // offset the s++ startst = st; PRVM_CHECK_INFINITE(); } break; case OP_SWITCH_E: case OP_SWITCH_FNC: if (_switch->_int == OPA->_int) { prog->xfunction->profile += (st - startst); st += st->b - 1; // offset the s++ startst = st; PRVM_CHECK_INFINITE(); } break; case OP_SWITCH_S: if (_switch->_int == OPA->_int) { prog->xfunction->profile += (st - startst); st += st->b - 1; // offset the s++ startst = st; PRVM_CHECK_INFINITE(); } if((!_switch->_int && PRVM_GetString(OPA->string)) || (!OPA->_int && PRVM_GetString(_switch->string))) break; if(!strcmp(PRVM_GetString(_switch->string), PRVM_GetString(OPA->string))) { prog->xfunction->profile += (st - startst); st += st->b - 1; // offset the s++ startst = st; PRVM_CHECK_INFINITE(); } break; case OP_SWITCH_V: if (_switch->vector[0] == OPA->vector[0] && _switch->vector[1] == OPA->vector[1] && _switch->vector[2] == OPA->vector[2]) { prog->xfunction->profile += (st - startst); st += st->b - 1; // offset the s++ startst = st; PRVM_CHECK_INFINITE(); } break; default: PRVM_ERROR ("%s Progs OP_CASE with bad/missing OP_SWITCH", PRVM_NAME); break; } break; case OP_CASERANGE: switch(switchtype) { case OP_SWITCH_F: if (_switch->_float >= OPA->_float && _switch->_float <= OPB->_float) { prog->xfunction->profile += (st - startst); st += st->b - 1; // offset the s++ startst = st; PRVM_CHECK_INFINITE(); } break; default: PRVM_ERROR ("%s Progs OP_CASERANGE with bad/missing OP_SWITCH", PRVM_NAME); break; } 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->_int = ((int)OPA->_float & OPB->_int); break; case OP_BITOR_FI: OPC->_int = ((int)OPA->_float | OPB->_int); break; case OP_MUL_IF: OPC->_float = (OPA->_int * OPB->_float); break; case OP_MUL_FI: OPC->_float = (OPA->_float * OPB->_int); break; case OP_MUL_VI: OPC->vector[0] = OPA->vector[0] * OPB->_int; OPC->vector[1] = OPA->vector[0] * OPB->_int; OPC->vector[2] = OPA->vector[0] * OPB->_int; break; case OP_MUL_IV: OPC->vector[0] = OPB->_int * OPA->vector[0]; OPC->vector[1] = OPB->_int * OPA->vector[1]; OPC->vector[2] = OPB->_int * OPA->vector[2]; break; case OP_DIV_IF: OPC->_float = (OPA->_int / OPB->_float); break; case OP_DIV_FI: OPC->_float = (OPA->_float / OPB->_int); break; case OP_AND_I: OPC->_int = (OPA->_int && OPB->_int); break; case OP_OR_I: OPC->_int = (OPA->_int || OPB->_int); break; case OP_AND_IF: OPC->_int = (OPA->_int && OPB->_float); break; case OP_OR_IF: OPC->_int = (OPA->_int || OPB->_float); break; case OP_AND_FI: OPC->_int = (OPA->_float && OPB->_int); break; case OP_OR_FI: OPC->_int = (OPA->_float || OPB->_int); break; case OP_NOT_I: OPC->_int = !OPA->_int; break; case OP_NE_IF: OPC->_int = (OPA->_int != OPB->_float); break; case OP_NE_FI: OPC->_int = (OPA->_float != OPB->_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 (prvm_boundscheck->value && (OPB->_int < 0 || OPB->_int >= (uint)prog->progs->numglobaldefs)) { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR ("%s Progs attempted to write to an invalid indexed global", PRVM_NAME); return; } prog->globals.gp[OPB->_int] = OPA->_float; break; case OP_GSTOREP_V: if (prvm_boundscheck->value && (OPB->_int < 0 || OPB->_int + 2 >= (uint)prog->progs->numglobaldefs)) { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR ("%s Progs attempted to write to an invalid indexed global", PRVM_NAME); return; } prog->globals.gp[OPB->_int+0] = OPA->vector[0]; prog->globals.gp[OPB->_int+1] = OPA->vector[1]; prog->globals.gp[OPB->_int+2] = OPA->vector[2]; break; case OP_GADDRESS: i = OPA->_int + (int)OPB->_float; if (prvm_boundscheck->value && (i < 0 || i >= (uint)prog->progs->numglobaldefs)) { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR ("%s Progs attempted to address an out of bounds global", PRVM_NAME); return; } OPC->_float = prog->globals.gp[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 (prvm_boundscheck->value && (OPA->_int < 0 || OPA->_int >= (uint)prog->progs->numglobaldefs)) { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR ("%s Progs attempted to read an invalid indexed global", PRVM_NAME); return; } OPC->_float = prog->globals.gp[OPA->_int]; break; case OP_GLOAD_V: if (prvm_boundscheck->value && (OPA->_int < 0 || OPA->_int + 2 >= (uint)prog->progs->numglobaldefs)) { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR ("%s Progs attempted to read an invalid indexed global", PRVM_NAME); return; } OPC->vector[0] = prog->globals.gp[OPA->_int+0]; OPC->vector[1] = prog->globals.gp[OPA->_int+1]; OPC->vector[2] = prog->globals.gp[OPA->_int+2]; break; case OP_BOUNDCHECK: if (OPA->_int < 0 || OPA->_int >= st->b) { prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR ("%s Progs boundcheck failed at line number %d, value is < 0 or >= %d", PRVM_NAME, st->b, st->c); return; } break; case OP_CSTATE: case OP_CWSTATE: PRVM_ERROR("OP_CSTATE or OP_CWSTATE not supported by %s", PRVM_NAME); break; default: prog->xfunction->profile += (st - startst); prog->xstatement = st - prog->statements; PRVM_ERROR ("Bad opcode %i in %s", st->op, PRVM_NAME); } } }