#define PROGSUSED #include "progsint.h" //#include "editor.h" #if __STDC_VERSION__ >= 199901L #define fte_restrict restrict #elif defined(_MSC_VER) && _MSC_VER >= 1400 #define fte_restrict __restrict #else #define fte_restrict #endif #if defined(_WIN32) || defined(__DJGPP__) #include #elif !defined(alloca) //alloca.h isn't present on bsd (stdlib.h should define it to __builtin_alloca, and we can check for that here). #include #endif #define HunkAlloc BADGDFG sdfhhsf FHS #define Host_Error Sys_Error // I put the following here to resolve "undefined reference to `__imp__vsnprintf'" with MinGW64 ~ Moodles #if 0//def _WIN32 #if (_MSC_VER >= 1400) //with MSVC 8, use MS extensions #define snprintf linuxlike_snprintf_vc8 void VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...) LIKEPRINTF(3); #define vsnprintf(a, b, c, d) (void)(vsnprintf_s(a, b, _TRUNCATE, c, d)) #else //msvc crap #define snprintf linuxlike_snprintf void VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...) LIKEPRINTF(3); #define vsnprintf linuxlike_vsnprintf void VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr); #endif #endif //cpu clock stuff (glorified rdtsc), for profile timing only #if !defined(Sys_GetClock) && defined(_WIN32) //windows has some specific functions for this (traditionally wrapping rdtsc) //note: on some systems, you may need to force cpu affinity to a single core via task manager static prclocks_t Sys_GetClock(void) { LARGE_INTEGER li; QueryPerformanceCounter(&li); return li.QuadPart; } prclocks_t Sys_GetClockRate(void) { LARGE_INTEGER li; QueryPerformanceFrequency(&li); return li.QuadPart; } #define Sys_GetClock Sys_GetClock #endif #if 0//!defined(Sys_GetClock) && defined(__unix__) //linux/unix has some annoying abstraction and shows time in nanoseconds rather than cycles. lets hope we don't waste too much time reading it. #include #if defined(_POSIX_TIMERS) && _POSIX_TIMERS >= 0 #include #ifdef CLOCK_PROCESS_CPUTIME_ID static prclocks_t Sys_GetClock(void) { struct timespec c; clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &c); return (c.tv_sec*1000000000ull) + c.tv_nsec; } #define Sys_GetClock Sys_GetClock prclocks_t Sys_GetClockRate(void) { return 1000000000ull; } #endif #endif #endif #if !defined(Sys_GetClock) && defined(__unix__) #include #define Sys_GetClock() clock() prclocks_t Sys_GetClockRate(void) { return CLOCKS_PER_SEC; } #endif #ifndef Sys_GetClock //other systems have no choice but to omit this feature in some way. this is just for profiling, so we can get away with stubs. #define Sys_GetClock() 0 prclocks_t Sys_GetClockRate(void) { return 1; } #endif //============================================================================= /* ================= PR_PrintStatement ================= */ static void PR_PrintStatement (progfuncs_t *progfuncs, int statementnum) { unsigned int op; unsigned int arg[3]; switch(current_progstate->structtype) { default: case PST_DEFAULT: case PST_QTEST: op = ((dstatement16_t*)current_progstate->statements + statementnum)->op; arg[0] = ((dstatement16_t*)current_progstate->statements + statementnum)->a; arg[1] = ((dstatement16_t*)current_progstate->statements + statementnum)->b; arg[2] = ((dstatement16_t*)current_progstate->statements + statementnum)->c; break; case PST_KKQWSV: case PST_FTE32: op = ((dstatement32_t*)current_progstate->statements + statementnum)->op; arg[0] = ((dstatement32_t*)current_progstate->statements + statementnum)->a; arg[1] = ((dstatement32_t*)current_progstate->statements + statementnum)->b; arg[2] = ((dstatement32_t*)current_progstate->statements + statementnum)->c; break; } #if !defined(MINIMAL) && !defined(OMIT_QCC) #define TYPEHINT(a) (pr_opcodes[op].type_##a) if ( (unsigned)op < OP_NUMOPS) { int i; externs->Printf ("%s ", pr_opcodes[op].name); i = strlen(pr_opcodes[op].name); for ( ; i<10 ; i++) externs->Printf (" "); } else #endif externs->Printf ("op%3i ", op); #ifndef TYPEHINT #define TYPEHINT(a) NULL #endif if (op == OP_IF_F || op == OP_IFNOT_F) externs->Printf ("%sbranch %i",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)),arg[1]); else if (op == OP_GOTO) { externs->Printf ("branch %i",arg[0]); } else if ( (unsigned)(op - OP_STORE_F) < 6) { externs->Printf ("%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a))); externs->Printf ("%s", PR_GlobalStringNoContents(progfuncs, arg[1])); } else { if (arg[0]) externs->Printf ("%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a))); if (arg[1]) externs->Printf ("%s",PR_GlobalString(progfuncs, arg[1], TYPEHINT(b))); if (arg[2]) externs->Printf ("%s", PR_GlobalStringNoContents(progfuncs, arg[2])); } externs->Printf ("\n"); } #ifdef _WIN32 static void VARGS QC_snprintfz (char *dest, size_t size, const char *fmt, ...) { va_list args; va_start (args, fmt); _vsnprintf (dest, size-1, fmt, args); va_end (args); //make sure its terminated. dest[size-1] = 0; } #else #define QC_snprintfz snprintf #endif void PDECL PR_GenerateStatementString (pubprogfuncs_t *ppf, int statementnum, char *out, int outlen) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; unsigned int op; unsigned int arg[3]; *out = 0; outlen--; if ((unsigned)statementnum >= current_progstate->progs->numstatements) return; switch(current_progstate->structtype) { case PST_DEFAULT: case PST_QTEST: op = ((dstatement16_t*)current_progstate->statements + statementnum)->op; arg[0] = ((dstatement16_t*)current_progstate->statements + statementnum)->a; arg[1] = ((dstatement16_t*)current_progstate->statements + statementnum)->b; arg[2] = ((dstatement16_t*)current_progstate->statements + statementnum)->c; break; case PST_KKQWSV: case PST_FTE32: op = ((dstatement32_t*)current_progstate->statements + statementnum)->op; arg[0] = ((dstatement32_t*)current_progstate->statements + statementnum)->a; arg[1] = ((dstatement32_t*)current_progstate->statements + statementnum)->b; arg[2] = ((dstatement32_t*)current_progstate->statements + statementnum)->c; break; default: return; } op = op & ~0x8000; //break points. if (current_progstate->linenums) { QC_snprintfz (out, outlen, "%3i: ", current_progstate->linenums[statementnum]); outlen -= strlen(out); out += strlen(out); } else { QC_snprintfz (out, outlen, "%3i: ", statementnum); outlen -= strlen(out); out += strlen(out); } #if !defined(MINIMAL) && !defined(OMIT_QCC) if ( (unsigned)op < OP_NUMOPS) { QC_snprintfz (out, outlen, "%-12s ", pr_opcodes[op].opname); outlen -= strlen(out); out += strlen(out); } else #endif { QC_snprintfz (out, outlen, "op%3i ", op); outlen -= strlen(out); out += strlen(out); } if (op == OP_IF_F || op == OP_IFNOT_F || op == OP_IF_I || op == OP_IFNOT_I || op == OP_IF_S || op == OP_IFNOT_S) { QC_snprintfz (out, outlen, "%sbranch %i(%+i)",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)),(short)arg[1], statementnum+(short)arg[0]); outlen -= strlen(out); out += strlen(out); } else if (op == OP_GOTO) { QC_snprintfz (out, outlen, "branch %i(%+i)",(short)arg[0], statementnum+(short)arg[0]); outlen -= strlen(out); out += strlen(out); } else if ( (unsigned)(op - OP_STORE_F) < 6) { QC_snprintfz (out, outlen, "%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a))); outlen -= strlen(out); out += strlen(out); QC_snprintfz (out, outlen, "%s", PR_GlobalStringNoContents(progfuncs, arg[1])); outlen -= strlen(out); out += strlen(out); } else { if (arg[0]) { QC_snprintfz (out, outlen, "%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a))); outlen -= strlen(out); out += strlen(out); } if (arg[1]) { QC_snprintfz (out, outlen, "%s",PR_GlobalString(progfuncs, arg[1], TYPEHINT(b))); outlen -= strlen(out); out += strlen(out); } if (arg[2]) { QC_snprintfz (out, outlen, "%s", PR_GlobalStringNoContents(progfuncs, arg[2])); outlen -= strlen(out); out += strlen(out); } } QC_snprintfz (out, outlen, "\n"); outlen -= 1; out += 1; } /* ============ PR_StackTrace ============ */ static void PDECL PR_PrintRelevantLocals(progfuncs_t *progfuncs) { //scan for op_address/op_load instructions within the function int st, st2; int op; dstatement16_t *st16 = current_progstate->statements; int line; if (!current_progstate->linenums || current_progstate->structtype != PST_DEFAULT) return; line = current_progstate->linenums[prinst.pr_xstatement]; for (st = prinst.pr_xfunction->first_statement; st16[st].op != OP_DONE; st++) { if (current_progstate->linenums[st] < line - 2 || current_progstate->linenums[st] > line + 2) continue; //don't go crazy with this. op = st16[st].op & ~0x8000; if (op == OP_ADDRESS || (op >= OP_LOAD_F && op <= OP_LOAD_FNC) || op == OP_LOAD_I || op == OP_LOAD_P) { ddef16_t *ent = ED_GlobalAtOfs16(progfuncs, st16[st].a); ddef16_t *fld = ED_GlobalAtOfs16(progfuncs, st16[st].b); pbool skip = false; edictrun_t *ed; unsigned int entnum; eval_t *ptr; fdef_t *fdef; fdef_t *cnfd; const char *classname; if (!ent || !fld) continue; //all this extra code to avoid printing dupes... for (st2 = st-1; st2 >= prinst.pr_xfunction->first_statement; st2--) { if (current_progstate->linenums[st2] < line - 2 || current_progstate->linenums[st2] > line + 2) continue; op = st16[st2].op & ~0x8000; if (op == OP_ADDRESS || (op >= OP_LOAD_F && op <= OP_LOAD_FNC) || op == OP_LOAD_I || op == OP_LOAD_P) if (st16[st].a == st16[st2].a && st16[st].b == st16[st2].b) { skip = true; break; } } if (skip) continue; entnum = ((eval_t *)&pr_globals[st16[st].a])->edict; if (entnum >= sv_num_edicts) { classname = "INVALID"; continue; } else { ed = PROG_TO_EDICT_PB(progfuncs, entnum); if ((unsigned int)((eval_t *)&pr_globals[st16[st].b])->_int*4u >= ed->fieldsize) continue; else ptr = (eval_t *)(((int *)edvars(ed)) + ((eval_t *)&pr_globals[st16[st].b])->_int + progfuncs->funcs.fieldadjust); cnfd = ED_FindField(progfuncs, "classname"); if (cnfd) { string_t *v = (string_t *)((char *)edvars(ed) + cnfd->ofs*4); classname = PR_StringToNative(&progfuncs->funcs, *v); } else classname = ""; } if (*classname) fdef = ED_ClassFieldAtOfs(progfuncs, ((eval_t *)&pr_globals[st16[st].b])->_int, classname); else fdef = ED_FieldAtOfs(progfuncs, ((eval_t *)&pr_globals[st16[st].b])->_int); if (fdef) externs->Printf(" %s.%s: %s\n", PR_StringToNative(&progfuncs->funcs, ent->s_name), PR_StringToNative(&progfuncs->funcs, fld->s_name), PR_ValueString(progfuncs, fdef->type, ptr, false)); else externs->Printf(" %s.%s: BAD FIELD DEF - %#x\n", PR_StringToNative(&progfuncs->funcs, ent->s_name), PR_StringToNative(&progfuncs->funcs, fld->s_name), ptr->_int); } } } void PDECL PR_StackTrace (pubprogfuncs_t *ppf, int showlocals) { progfuncs_t *progfuncs = (progfuncs_t *)ppf; const mfunction_t *f; int prnum; int i, st; int progs; int ofs; int *globalbase; int tracing = progfuncs->funcs.debug_trace; progs = -1; if (prinst.pr_depth == 0) { externs->Printf ("\n"); return; } progfuncs->funcs.debug_trace = -10; //PR_StringToNative(+via PR_ValueString) has various error conditions that we want to mute instead of causing recursive errors. //point this to the function's locals globalbase = (int *)pr_globals + prinst.pr_xfunction->parm_start + prinst.pr_xfunction->locals; for (i=prinst.pr_depth ; i>0 ; i--) { if (i == prinst.pr_depth) { f = prinst.pr_xfunction; st = prinst.pr_xstatement; prnum = prinst.pr_typecurrent; } else { f = prinst.pr_stack[i].f; st = prinst.pr_stack[i].s; prnum = prinst.pr_stack[i].progsnum; } if (!f) { externs->Printf ("\n"); } else { globalbase -= f->locals; if (prnum != progs) { progs = prnum; externs->DPrintf ("<%s>\n", pr_progstate[progs].filename); } if (!f->s_file) externs->Printf ("unknown-file : %s\n", PR_StringToNative(ppf, f->s_name)); else { if (pr_progstate[progs].linenums) externs->Printf ("%12s:%i: %s\n", PR_StringToNative(ppf, f->s_file), pr_progstate[progs].linenums[st], PR_StringToNative(ppf, f->s_name)); else externs->Printf ("%12s : %s+%i\n", PR_StringToNative(ppf, f->s_file), PR_StringToNative(ppf, f->s_name), st-f->first_statement); } //locals:0 = no locals //locals:1 = top only //locals:2 = ALL locals. if ((i == prinst.pr_depth && showlocals == 1) || showlocals >= 2) { for (ofs = 0; ofs < f->locals; ofs++) { ddef16_t *local; local = ED_GlobalAtOfs16(progfuncs, f->parm_start+ofs); if (!local) { int arg, aofs; for (arg = 0, aofs = 0; arg < f->numparms; arg++) { if (ofs >= aofs && ofs < aofs + f->parm_size[arg]) break; aofs += f->parm_size[arg]; } if (arg < f->numparms) { if (f->parm_size[arg] == 3) { //looks like a vector. print it as such externs->Printf(" arg%i(%i): [%g, %g, %g]\n", arg, f->parm_start+ofs, *(float *)(globalbase+ofs), *(float *)(globalbase+ofs+1), *(float *)(globalbase+ofs+2)); ofs += 2; } else externs->Printf(" arg%i(%i): %g===%i\n", arg, f->parm_start+ofs, *(float *)(globalbase+ofs), *(int *)(globalbase+ofs) ); } else { externs->Printf(" unk(%i): %g===%i\n", f->parm_start+ofs, *(float *)(globalbase+ofs), *(int *)(globalbase+ofs) ); } } else { externs->Printf(" %s: %s\n", PR_StringToNative(ppf, local->s_name), PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase+ofs), false)); if (local->type == ev_vector) ofs+=2; } } } if (i == prinst.pr_depth) { //scan for op_address/op_load instructions within the function PR_PrintRelevantLocals(progfuncs); } if (i == prinst.pr_depth) globalbase = prinst.localstack + prinst.localstack_used; } } progfuncs->funcs.debug_trace = tracing; } /* ============================================================================ PR_ExecuteProgram The interpretation main loop ============================================================================ */ /* ==================== PR_EnterFunction Returns the new program statement counter ==================== */ static int ASMCALL PR_EnterFunction (progfuncs_t *progfuncs, mfunction_t *f, int progsnum) { int i, j, c, o; prstack_t *st; if (prinst.pr_depth == MAX_STACK_DEPTH) { PR_StackTrace (&progfuncs->funcs, false); externs->Printf ("stack overflow on call to %s (depth %i)\n", progfuncs->funcs.stringtable+f->s_name, prinst.pr_depth); //comment this out if you want the progs to try to continue anyway (could cause infinate loops) PR_AbortStack(&progfuncs->funcs); externs->Abort("Stack Overflow in %s\n", progfuncs->funcs.stringtable+f->s_name); return prinst.pr_xstatement; } st = &prinst.pr_stack[prinst.pr_depth++]; st->s = prinst.pr_xstatement; st->f = prinst.pr_xfunction; st->progsnum = progsnum; st->pushed = prinst.spushed; st->stepping = progfuncs->funcs.debug_trace; if (progfuncs->funcs.debug_trace == DEBUG_TRACE_OVER) progfuncs->funcs.debug_trace = DEBUG_TRACE_OFF; if (prinst.profiling) { st->timestamp = Sys_GetClock(); } prinst.localstack_used += prinst.spushed; //make sure the call doesn't hurt pushed pointers // save off any locals that the new function steps on (to a side place, fromwhere they are restored on exit) c = f->locals; if (prinst.localstack_used + c > LOCALSTACK_SIZE) { prinst.localstack_used -= prinst.spushed; prinst.pr_depth--; PR_RunError (&progfuncs->funcs, "PR_ExecuteProgram: locals stack overflow\n"); } for (i=0 ; i < c ; i++) prinst.localstack[prinst.localstack_used+i] = ((int *)pr_globals)[f->parm_start + i]; prinst.localstack_used += c; // copy parameters (set initial values) o = f->parm_start; for (i=0 ; inumparms ; i++) { for (j=0 ; jparm_size[i] ; j++) { ((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j]; o++; } } prinst.pr_xfunction = f; return f->first_statement - 1; // offset the s++ } /* ==================== PR_LeaveFunction ==================== */ static int ASMCALL PR_LeaveFunction (progfuncs_t *progfuncs) { int i, c; prstack_t *st; if (prinst.pr_depth <= 0) externs->Sys_Error ("prog stack underflow"); // up stack st = &prinst.pr_stack[--prinst.pr_depth]; // restore locals from the stack c = prinst.pr_xfunction->locals; prinst.localstack_used -= c; if (prinst.localstack_used < 0) PR_RunError (&progfuncs->funcs, "PR_ExecuteProgram: locals stack underflow\n"); for (i=0 ; i < c ; i++) ((int *)pr_globals)[prinst.pr_xfunction->parm_start + i] = prinst.localstack[prinst.localstack_used+i]; PR_SwitchProgsParms(progfuncs, st->progsnum); prinst.spushed = st->pushed; if (!progfuncs->funcs.debug_trace) progfuncs->funcs.debug_trace = st->stepping; if (prinst.profiling) { prclocks_t cycles; cycles = Sys_GetClock() - st->timestamp; if (cycles > prinst.profilingalert) externs->Printf("QC call to %s took over a second\n", PR_StringToNative(&progfuncs->funcs,prinst.pr_xfunction->s_name)); prinst.pr_xfunction->profiletime += cycles; prinst.pr_xfunction = st->f; if (prinst.pr_depth) prinst.pr_xfunction->profilechildtime += cycles; } else prinst.pr_xfunction = st->f; prinst.localstack_used -= prinst.spushed; return st->s; } ddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, const char *name, eval_t **val) { static ddef32_t def; ddef32_t *def32; ddef16_t *def16; int i; progstate_t *cp = current_progstate; if (!cp) return NULL; switch (cp->structtype) { case PST_DEFAULT: case PST_KKQWSV: //this gets parms fine, but not locals if (prinst.pr_xfunction) for (i = 0; i < prinst.pr_xfunction->locals; i++) { def16 = ED_GlobalAtOfs16(progfuncs, prinst.pr_xfunction->parm_start+i); if (!def16) continue; if (!strcmp(def16->s_name+progfuncs->funcs.stringtable, name)) { *val = (eval_t *)&cp->globals[prinst.pr_xfunction->parm_start+i]; //we need something like this for functions that are not the top layer // *val = (eval_t *)&localstack[localstack_used-pr_xfunction->numparms*4]; def.ofs = def16->ofs; def.s_name = def16->s_name; def.type = def16->type; return &def; } } def16 = ED_FindGlobal16(progfuncs, name); if (!def16) return NULL; def.ofs = def16->ofs; def.type = def16->type; def.s_name = def16->s_name; def32 = &def; break; case PST_QTEST: case PST_FTE32: //this gets parms fine, but not locals if (prinst.pr_xfunction) for (i = 0; i < prinst.pr_xfunction->numparms; i++) { def32 = ED_GlobalAtOfs32(progfuncs, prinst.pr_xfunction->parm_start+i); if (!def32) continue; if (!strcmp(def32->s_name+progfuncs->funcs.stringtable, name)) { *val = (eval_t *)&cp->globals[prinst.pr_xfunction->parm_start+i]; //we need something like this for functions that are not the top layer // *val = (eval_t *)&localstack[localstack_used-pr_xfunction->numparms*4]; return def32; } } def32 = ED_FindGlobal32(progfuncs, name); if (!def32) return NULL; break; default: externs->Sys_Error("Bad struct type in ED_FindLocalOrGlobal"); def32 = NULL; } *val = (eval_t *)&cp->globals[def32->ofs]; return &def; } static char *COM_TrimString(const char *str, char *buffer, int buffersize) { int i; while (*str <= ' ' && *str>'\0') str++; for (i = 0; i < buffersize-1; i++) { if (*str <= ' ') break; buffer[i] = *str++; } buffer[i] = '\0'; return buffer; } pbool LocateDebugTerm(progfuncs_t *progfuncs, const char *key, eval_t **result, etype_t *rettype, eval_t *store) { ddef32_t *def; fdef_t *fdef; int fofs; eval_t *val = NULL, *fval=NULL; char *c, *c2; etype_t type = ev_void; struct edictrun_s *ed; // etype_t ptrtype = ev_void; c = strchr(key, '.'); if (c) *c = '\0'; def = ED_FindLocalOrGlobal(progfuncs, key, &val); if (!def) { if (*key == '\'') { type = ev_vector; val = store; val->_vector[0] = 0; val->_vector[1] = 0; val->_vector[2] = 0; } else if (*key == '\"') { type = ev_string; val = store; val->string = 0; } else if (atoi(key)) { type = ev_entity; val = store; val->edict = atoi(key); } } else type = def->type; if (c) *c = '.'; if (!val) { return false; } //go through ent vars c = strchr(key, '.'); while(c) { c2 = c+1; c = strchr(c2, '.'); type = type &~DEF_SAVEGLOBAL; if (current_progstate && current_progstate->types) type = current_progstate->types[type].type; if (type != ev_entity) return false; if (c)*c = '\0'; fdef = ED_FindField(progfuncs, c2); if (!fdef) { char trimmed[256]; c2 = COM_TrimString(c2, trimmed, sizeof(trimmed)); def = ED_FindLocalOrGlobal(progfuncs, c2, &fval); if (def && def->type == ev_field) { fofs = fval->_int + progfuncs->funcs.fieldadjust; fdef = ED_FieldAtOfs(progfuncs, fofs); } } if (c)*c = '.'; if (!fdef) return false; fofs = fdef->ofs; type = fdef->type; if ((unsigned int)val->_int >= prinst.maxedicts) ed = NULL; else ed = PROG_TO_EDICT_PB(progfuncs, val->_int); if (!ed) return false; if (fofs < 0 || fofs >= (int)prinst.max_fields_size) return false; val = (eval_t *) (((char *)ed->fields) + fofs*4); } *rettype = type; *result = val; return true; } pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *ppf, const char *key) { progfuncs_t *progfuncs = (progfuncs_t *)ppf; eval_t *val; eval_t fakeval; etype_t type; if (!key) { free(prinst.watch_name); prinst.watch_name = NULL; prinst.watch_ptr = NULL; prinst.watch_type = ev_void; return false; } if (!LocateDebugTerm(progfuncs, key, &val, &type, &fakeval)) { externs->Printf("Unable to evaluate watch term \"%s\"\n", key); return false; } if (val == &fakeval) { externs->Printf("Do you like watching paint dry?\n"); return false; } if (type == ev_vector) { externs->Printf("Unable to watch vectors. Watching the x field instead.\n"); type = ev_float; } free(prinst.watch_name); prinst.watch_name = strdup(key); prinst.watch_ptr = val; prinst.watch_old = *prinst.watch_ptr; prinst.watch_type = type &~ DEF_SAVEGLOBAL; return true; } static const char *PR_ParseCast(const char *key, etype_t *t, pbool *isptr) { extern char *basictypenames[]; int type; *t = ev_void; *isptr = false; while(*key == ' ') key++; if (*key == '(') { key++; for (type = 0; type <= ev_variant; type++) { if (!strncmp(key, basictypenames[type], strlen(basictypenames[type]))) { key += strlen(basictypenames[type]); while(*key == ' ') key++; if (*key == '*') { *isptr = true; key++; } *t = type; break; } } if (type > ev_variant) return NULL; while(*key == ' ') key++; if (*key++ != ')') return NULL; } return key; } char *PDECL PR_EvaluateDebugString(pubprogfuncs_t *ppf, const char *key) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; static char buf[8192]; fdef_t *fdef; eval_t *val; char *assignment; etype_t type; eval_t fakeval; extern char *basictypenames[]; if (*key == '*') { int ptr; eval_t v; etype_t cast; pbool isptr; type = ev_void; key = PR_ParseCast(key+1, &cast, &isptr); if (!key || !isptr) return "(unable to evaluate)"; if (*key == '&') { if (!LocateDebugTerm(progfuncs, key+1, &val, &type, &fakeval) && val != &fakeval) return "(unable to evaluate)"; v._int = (char*)val - progfuncs->funcs.stringtable; val = &v; type = ev_pointer; } else { if (!LocateDebugTerm(progfuncs, key, &val, &type, &fakeval) && val != &fakeval) return "(unable to evaluate)"; } if (type == ev_integer || type == ev_string || type == ev_pointer) ptr = val->_int; else if (type == ev_float) ptr = val->_float; else return "(unable to evaluate)"; return PR_ValueString(progfuncs, cast, (eval_t*)(progfuncs->funcs.stringtable + ptr), true); } if (*key == '&') { if (!LocateDebugTerm(progfuncs, key+1, &val, &type, &fakeval) && val != &fakeval) return "(unable to evaluate)"; QC_snprintfz(buf, sizeof(buf), "(%s*)%#x", ((type>=10)?"???":basictypenames[type]), (unsigned int)((char*)val - progfuncs->funcs.stringtable)); return buf; } assignment = strchr(key, '='); if (assignment) *assignment = '\0'; if (!LocateDebugTerm(progfuncs, key, &val, &type, &fakeval)) return "(unable to evaluate)"; /* c = strchr(key, '.'); if (c) *c = '\0'; def = ED_FindLocalOrGlobal(progfuncs, key, &val); if (!def) { if (atoi(key)) { def = &fakedef; def->ofs = 0; def->type = ev_entity; val = &fakeval; val->edict = atoi(key); } } if (c) *c = '.'; if (!def) { return "(Bad string)"; } type = def->type; //go through ent vars c = strchr(key, '.'); while(c) { c2 = c+1; c = strchr(c2, '.'); type = type &~DEF_SAVEGLOBAL; if (current_progstate && current_progstate->types) type = current_progstate->types[type].type; if (type != ev_entity) return "'.' without entity"; if (c)*c = '\0'; fdef = ED_FindField(progfuncs, COM_TrimString(c2)); if (c)*c = '.'; if (!fdef) return "(Bad string)"; ed = PROG_TO_EDICT(progfuncs, val->_int); if (!ed) return "(Invalid Entity)"; val = (eval_t *) (((char *)ed->fields) + fdef->ofs*4); type = fdef->type; } */ if (assignment) { char *str = assignment+1; while(*str == ' ') str++; switch (type&~DEF_SAVEGLOBAL) { case ev_string: #ifdef QCGC *(string_t *)val = PR_AllocTempString(&progfuncs->funcs, str); #else *(string_t *)val = PR_StringToProgs(&progfuncs->funcs, ED_NewString (&progfuncs->funcs, assignment, 0, true)); #endif break; case ev_float: if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) *(float*)val = strtoul(str, NULL, 0); else *(float *)val = (float)atof (str); break; case ev_integer: *(int *)val = atoi (str); break; case ev_vector: { int i; if (*str == '\'') str++; for (i = 0; i < 3; i++) { while(*str == ' ' || *str == '\t') str++; ((float *)val)[i] = strtod(str, &str); } while(*str == ' ' || *str == '\t') str++; if (*str == '\'') str++; } break; case ev_entity: if (!EDICT_NUM(progfuncs, atoi (str))) return "(invalid entity)"; *(int *)val = EDICT_TO_PROG(progfuncs, EDICT_NUM(progfuncs, atoi (str))); break; case ev_field: fdef = ED_FindField (progfuncs, str); if (!fdef) { size_t l,nl = strlen(str); *assignment = '='; strcpy(buf, "Can't find field "); l = strlen(buf); if (nl > sizeof(buf)-l-2) nl = sizeof(buf)-l-2; memcpy(buf+l, str, nl); buf[l+nl+1] = 0; return buf; } *(int *)val = G_INT(fdef->ofs); break; case ev_function: { mfunction_t *func; progsnum_t i; progsnum_t progsnum = -1; if (str[0] && str[1] == ':') { progsnum = atoi(str); str+=2; } else if (str[0] && str[1] && str[2] == ':') { progsnum = atoi(str); str+=3; } func = ED_FindFunction (progfuncs, str, &i, progsnum); if (!func) { size_t l,nl = strlen(str); *assignment = '='; strcpy(buf, "Can't find field "); l = strlen(buf); if (nl > sizeof(buf)-l-2) nl = sizeof(buf)-l-2; memcpy(buf+l, str, nl); buf[l+nl+1] = 0; return buf; } *(func_t *)val = (func - pr_progstate[i].functions) | (i<<24); } break; default: break; } *assignment = '='; } QC_snprintfz(buf, sizeof(buf), "%s", PR_ValueString(progfuncs, type, val, true)); return buf; } //int EditorHighlightLine(window_t *wnd, int line); void SetExecutionToLine(progfuncs_t *progfuncs, int linenum) { int pn = prinst.pr_typecurrent; int snum; const mfunction_t *f = prinst.pr_xfunction; switch(current_progstate->structtype) { case PST_DEFAULT: case PST_QTEST: for (snum = f->first_statement; pr_progstate[pn].linenums[snum] < linenum; snum++) { if (pr_statements16[snum].op == OP_DONE) return; } break; case PST_KKQWSV: case PST_FTE32: for (snum = f->first_statement; pr_progstate[pn].linenums[snum] < linenum; snum++) { if (pr_statements32[snum].op == OP_DONE) return; } break; default: externs->Sys_Error("Bad struct type"); snum = 0; } prinst.debugstatement = snum; // EditorHighlightLine(editwnd, pr_progstate[pn].linenums[snum]); } struct sortedfunc_s { int firststatement; int firstline; }; int PDECL PR_SortBreakFunctions(const void *va, const void *vb) { const struct sortedfunc_s *a = va; const struct sortedfunc_s *b = vb; if (a->firstline == b->firstline) return 0; return a->firstline > b->firstline; } //0 clear. 1 set, 2 toggle, 3 check int PDECL PR_ToggleBreakpoint(pubprogfuncs_t *ppf, const char *filename, int linenum, int flag) //write alternate route to work by function name. { progfuncs_t *progfuncs = (progfuncs_t*)ppf; int ret=0; unsigned int fl, stline; unsigned int i, j; progstate_t *cp; mfunction_t *f; int op = 0; //warning about not being initialized before use if (!pr_progstate) return ret; for (j = 0; j < prinst.maxprogs; j++) { cp = &pr_progstate[j]; if (!cp->progs) continue; if (linenum) //linenum is set means to set the breakpoint on a file and line { struct sortedfunc_s *sortedstatements; int numfilefunctions = 0; if (!cp->linenums) continue; sortedstatements = alloca(cp->progs->numfunctions * sizeof(*sortedstatements)); //we need to use the function table in order to set breakpoints in the right file. for (f = cp->functions, fl = 0; fl < cp->progs->numfunctions; f++, fl++) { const char *fncfile = f->s_file+progfuncs->funcs.stringtable; if (fncfile[0] == '.' && fncfile[1] == '/') fncfile+=2; if (!stricmp(fncfile, filename)) { sortedstatements[numfilefunctions].firststatement = f->first_statement; if (f->first_statement < 0 || f->first_statement >= (int)cp->progs->numstatements) sortedstatements[numfilefunctions].firstline = 0; else sortedstatements[numfilefunctions].firstline = cp->linenums[f->first_statement]; numfilefunctions++; } } f = NULL; qsort(sortedstatements, numfilefunctions, sizeof(*sortedstatements), PR_SortBreakFunctions); //our functions are now in terms of ascending line numbers. for (fl = 0; fl < numfilefunctions; fl++) { for (i = sortedstatements[fl].firststatement; i < cp->progs->numstatements; i++) { if (cp->linenums[i] >= linenum) { stline = cp->linenums[i]; for (; ; i++) { if ((unsigned int)cp->linenums[i] != stline) break; switch(cp->structtype) { case PST_DEFAULT: case PST_QTEST: op = ((dstatement16_t*)cp->statements + i)->op; break; case PST_KKQWSV: case PST_FTE32: case PST_UHEXEN2: op = ((dstatement32_t*)cp->statements + i)->op; break; default: externs->Sys_Error("Bad structtype"); op = 0; } switch (flag) { default: if (op & OP_BIT_BREAKPOINT) { op &= ~OP_BIT_BREAKPOINT; ret = false; flag = 0; } else { op |= OP_BIT_BREAKPOINT; ret = true; flag = 1; } break; case 0: op &= ~OP_BIT_BREAKPOINT; ret = false; break; case 1: op |= OP_BIT_BREAKPOINT; ret = true; break; case 3: if (op & OP_BIT_BREAKPOINT) return true; } switch(cp->structtype) { case PST_DEFAULT: case PST_QTEST: ((dstatement16_t*)cp->statements + i)->op = op; break; case PST_KKQWSV: case PST_FTE32: case PST_UHEXEN2: ((dstatement32_t*)cp->statements + i)->op = op; break; default: externs->Sys_Error("Bad structtype"); op = 0; } if (ret) //if its set, only set one breakpoint statement, not all of them. break; if ((op & ~OP_BIT_BREAKPOINT) == OP_DONE) break; //give up when we see the function's done. } goto cont; //next progs } } } } else //set the breakpoint on the first statement of the function specified. { for (f = cp->functions, fl = 0; fl < cp->progs->numfunctions; f++, fl++) { if (!strcmp(f->s_name+progfuncs->funcs.stringtable, filename)) { i = f->first_statement; switch(cp->structtype) { case PST_DEFAULT: case PST_QTEST: op = ((dstatement16_t*)cp->statements + i)->op; break; case PST_KKQWSV: case PST_FTE32: case PST_UHEXEN2: op = ((dstatement32_t*)cp->statements + i)->op; break; default: externs->Sys_Error("Bad structtype"); } switch (flag) { default: if (op & 0x8000) { op &= ~0x8000; ret = false; flag = 0; } else { op |= 0x8000; ret = true; flag = 1; } break; case 0: op &= ~0x8000; ret = false; break; case 1: op |= 0x8000; ret = true; break; case 3: if (op & 0x8000) return true; } switch(cp->structtype) { case PST_DEFAULT: case PST_QTEST: ((dstatement16_t*)cp->statements + i)->op = op; break; case PST_KKQWSV: case PST_FTE32: case PST_UHEXEN2: ((dstatement32_t*)cp->statements + i)->op = op; break; default: externs->Sys_Error("Bad structtype"); } break; } } } cont: continue; } return ret; } int ShowStep(progfuncs_t *progfuncs, int statement, char *fault, pbool fatal) { //FIXME: statics are evil, but at least the lastfile pointer check _should_ isolate different vms. static unsigned int lastline = 0; static unsigned int ignorestatement = 0; static const char *lastfile = NULL; const char *file = NULL; int pn = prinst.pr_typecurrent; int i; const mfunction_t *f = prinst.pr_xfunction; int faultline; int debugaction; prinst.pr_xstatement = statement; if (!externs->useeditor) { PR_PrintStatement(progfuncs, statement); if (fatal) progfuncs->funcs.debug_trace = DEBUG_TRACE_ABORTERROR; return statement; } if (f) { for(;;) //for DEBUG_TRACE_NORESUME handling { file = PR_StringToNative(&progfuncs->funcs, f->s_file); if (pr_progstate[pn].linenums) { if (lastline == pr_progstate[pn].linenums[statement] && lastfile == file && statement == ignorestatement && !fault) { ignorestatement++; return statement; //no info/same line as last time } lastline = pr_progstate[pn].linenums[statement]; } else lastline = -1; lastfile = file; faultline = lastline; debugaction = externs->useeditor(&progfuncs->funcs, lastfile, ((lastline!=-1)?&lastline:NULL), &statement, f->first_statement, fault, fatal); // if (pn != prinst.pr_typecurrent) //if they changed the line to execute, we need to find a statement that is on that line if (lastline && faultline != lastline) if (pr_progstate[pn].linenums) { switch(pr_progstate[pn].structtype) { case PST_UHEXEN2: case PST_FTE32: case PST_KKQWSV: { dstatement32_t *st = pr_progstate[pn].statements; unsigned int *lnos = pr_progstate[pn].linenums; for (i = f->first_statement; ; i++) { if (lastline == lnos[i]) { statement = i; break; } else if (lastline <= lnos[i]) break; else if (st[i].op == OP_DONE) break; } } break; case PST_DEFAULT: case PST_QTEST: { dstatement16_t *st = pr_progstate[pn].statements; unsigned int *lnos = pr_progstate[pn].linenums; for (i = f->first_statement; ; i++) { if (lastline == lnos[i]) { statement = i; break; } else if (lastline <= lnos[i]) break; else if (st[i].op == OP_DONE) break; } } } } if (debugaction == DEBUG_TRACE_NORESUME) continue; else if(debugaction == DEBUG_TRACE_ABORTERROR) progfuncs->funcs.parms->Abort ("%s", fault?fault:"Debugger Abort"); else if (debugaction == DEBUG_TRACE_OFF) { //if we're resuming, don't hit any lingering step-over triggers progfuncs->funcs.debug_trace = DEBUG_TRACE_OFF; for (i = 0; i < prinst.pr_depth; i++) prinst.pr_stack[prinst.pr_depth-1].stepping = DEBUG_TRACE_OFF; } else if (debugaction == DEBUG_TRACE_OUT) { //clear tracing for now, but ensure that it'll be reactivated once we reach the caller (if from qc) progfuncs->funcs.debug_trace = DEBUG_TRACE_OFF; if (prinst.pr_depth) prinst.pr_stack[prinst.pr_depth-1].stepping = DEBUG_TRACE_INTO; } else //some other debug action. maybe resume. progfuncs->funcs.debug_trace = debugaction; break; } } ignorestatement = statement+1; return statement; } //called by the qcvm when executing some statement that cannot be execed. int PR_HandleFault (pubprogfuncs_t *ppf, char *error, ...) { progfuncs_t *progfuncs = (progfuncs_t *)ppf; va_list argptr; char string[1024]; int resumestatement; va_start (argptr,error); Q_vsnprintf (string,sizeof(string)-1, error,argptr); va_end (argptr); PR_StackTrace (ppf, true); ppf->parms->Printf ("%s\n", string); resumestatement = ShowStep(progfuncs, prinst.pr_xstatement, string, true); if (resumestatement == 0) { PR_AbortStack(ppf); return -1; // ppf->parms->Abort ("%s", string); } return resumestatement; } /* ============ PR_RunError Aborts the currently executing function ============ */ void VARGS PR_RunError (pubprogfuncs_t *progfuncs, const char *error, ...) { va_list argptr; char string[1024]; va_start (argptr,error); Q_vsnprintf (string,sizeof(string)-1, error,argptr); va_end (argptr); // PR_PrintStatement (pr_statements + pr_xstatement); PR_StackTrace (progfuncs, true); progfuncs->parms->Printf ("\n"); //editbadfile(pr_strings + pr_xfunction->s_file, -1); progfuncs->parms->Abort ("%s", string); } pbool PR_RunWarning (pubprogfuncs_t *ppf, char *error, ...) { progfuncs_t *progfuncs = (progfuncs_t *)ppf; va_list argptr; char string[1024]; va_start (argptr,error); Q_vsnprintf (string,sizeof(string)-1, error,argptr); va_end (argptr); progfuncs->funcs.parms->Printf ("%s", string); if (prinst.pr_depth != 0) PR_StackTrace (ppf, false); if (progfuncs->funcs.debug_trace == 0) { progfuncs->funcs.debug_trace = DEBUG_TRACE_INTO; return true; } return false; } static pbool PR_ExecRunWarning (pubprogfuncs_t *ppf, int xstatement, char *error, ...) { progfuncs_t *progfuncs = (progfuncs_t *)ppf; va_list argptr; char string[1024]; prinst.pr_xstatement = xstatement; va_start (argptr,error); Q_vsnprintf (string,sizeof(string)-1, error,argptr); va_end (argptr); progfuncs->funcs.parms->Printf ("%s", string); if (prinst.pr_depth != 0) PR_StackTrace (ppf, false); if (progfuncs->funcs.debug_trace == DEBUG_TRACE_OFF) { prinst.pr_xstatement = ShowStep(progfuncs, xstatement, string, false); if (progfuncs->funcs.debug_trace != DEBUG_TRACE_OFF) return true; } return false; } //For debugging. Assumes classname field exists. const char *PR_GetEdictClassname(progfuncs_t *progfuncs, unsigned int edict) { fdef_t *cnfd = ED_FindField(progfuncs, "classname"); if (cnfd && edict < prinst.maxedicts) { string_t *v = (string_t *)((char *)edvars(PROG_TO_EDICT_PB(progfuncs, edict)) + cnfd->ofs*4); return PR_StringToNative(&progfuncs->funcs, *v); } return ""; } static pbool casecmp_f(progfuncs_t *progfuncs, eval_t *ref, eval_t *val) {return ref->_float == val->_float;} static pbool casecmp_i(progfuncs_t *progfuncs, eval_t *ref, eval_t *val) {return ref->_int == val->_int;} static pbool casecmp_v(progfuncs_t *progfuncs, eval_t *ref, eval_t *val) {return ref->_vector[0] == val->_vector[0] && ref->_vector[1] == val->_vector[1] && ref->_vector[2] == val->_vector[2];} static pbool casecmp_s(progfuncs_t *progfuncs, eval_t *ref, eval_t *val) { const char *refs = PR_StringToNative(&progfuncs->funcs, ref->string); const char *vals = PR_StringToNative(&progfuncs->funcs, val->string); return !strcmp(refs, vals);} static pbool casecmprange_f(progfuncs_t *progfuncs, eval_t *ref, eval_t *min, eval_t *max) {return ref->_float >= min->_float && ref->_float <= max->_float;} static pbool casecmprange_i(progfuncs_t *progfuncs, eval_t *ref, eval_t *min, eval_t *max) {return ref->_int >= min->_int && ref->_int <= max->_int;} static pbool casecmprange_v(progfuncs_t *progfuncs, eval_t *ref, eval_t *min, eval_t *max) {return ref->_vector[0] >= min->_vector[0] && ref->_vector[0] <= max->_vector[0] && ref->_vector[1] >= min->_vector[1] && ref->_vector[1] <= max->_vector[1] && ref->_vector[2] >= min->_vector[2] && ref->_vector[2] <= max->_vector[2];} static pbool casecmprange_bad(progfuncs_t *progfuncs, eval_t *ref, eval_t *min, eval_t *max){ PR_RunError (&progfuncs->funcs, "OP_CASERANGE type not supported");//BUG: pr_xstatement will not be correct. return false;} typedef pbool (*casecmp_t)(progfuncs_t *progfuncs, eval_t *ref, eval_t *val); typedef pbool (*casecmprange_t)(progfuncs_t *progfuncs, eval_t *ref, eval_t *min, eval_t *max); static casecmp_t casecmp[] = { casecmp_f, //float casecmp_v, //vector casecmp_s, //string casecmp_i, //ent casecmp_i //func //pointer, field, int, etc are emulated with func or something. I dunno }; static casecmprange_t casecmprange[] = { casecmprange_f, //float casecmprange_v, //vector - I'm using a bbox, not really sure what it should be casecmprange_bad, //string - should it use stof? string ranges don't relly make sense, at all. casecmprange_i, //ent - doesn't really make sense, but as ints/pointers/fields/etc might be emulated with this, allow it anyway, as an int type. casecmprange_i //func }; #define RUNAWAYCHECK() \ if (!--*runaway) \ { \ prinst.pr_xstatement = st-pr_statements; \ PR_RunError (&progfuncs->funcs, "runaway loop error\n");\ PR_StackTrace(&progfuncs->funcs,false); \ externs->Printf ("runaway loop error\n"); \ while(prinst.pr_depth > prinst.exitdepth) \ PR_LeaveFunction(progfuncs); \ prinst.spushed = 0; \ return -1; \ } #if defined(FTE_TARGET_WEB) || defined(SIMPLE_QCVM) static int PR_NoDebugVM(progfuncs_t *fte_restrict progfuncs) { char stack[4*1024]; size_t ofs; strcpy(stack, "This platform does not support QC debugging\nStack Trace:"); ofs = strlen(stack); PR_SaveCallStack (progfuncs, stack, &ofs, sizeof(stack)); PR_RunError (&progfuncs->funcs, "%s", stack); return -1; } #endif static int PR_ExecuteCode16 (progfuncs_t *fte_restrict progfuncs, int s, int *fte_restrict runaway) { unsigned int switchcomparison = 0; const dstatement16_t *fte_restrict st; mfunction_t *fte_restrict newf; int i; edictrun_t *ed; eval_t *ptr; float *fte_restrict glob = pr_globals; float tmpf; int tmpi; unsigned short op; eval_t *switchref = (eval_t*)glob; unsigned int num_edicts = sv_num_edicts; #define OPA ((eval_t *)&glob[st->a]) #define OPB ((eval_t *)&glob[st->b]) #define OPC ((eval_t *)&glob[st->c]) #define INTSIZE 16 st = &pr_statements16[s]; while (progfuncs->funcs.debug_trace || prinst.watch_ptr || prinst.profiling) { #if defined(FTE_TARGET_WEB) || defined(SIMPLE_QCVM) reeval16: //this can generate huge functions, so disable it on systems that can't realiably cope with such things (IE initiates an unwanted denial-of-service attack when pointed our javascript, and firefox prints a warning too) prinst.pr_xstatement = st-pr_statements16; return PR_NoDebugVM(progfuncs); #else #define DEBUGABLE #ifdef SEPARATEINCLUDES #include "execloop16d.h" #else #include "execloop.h" #endif #undef DEBUGABLE #endif } while(1) { #include "execloop.h" } #undef INTSIZE } static int PR_ExecuteCode32 (progfuncs_t *fte_restrict progfuncs, int s, int *fte_restrict runaway) { #if defined(FTE_TARGET_WEB) ||defined(SIMPLE_QCVM) //this can generate huge functions, so disable it on systems that can't realiably cope with such things (IE initiates an unwanted denial-of-service attack when pointed our javascript, and firefox prints a warning too) prinst.pr_xstatement = s; PR_RunError (&progfuncs->funcs, "32bit qc statement support was disabled for this platform.\n"); PR_StackTrace(&progfuncs->funcs, false); return -1; #else unsigned int switchcomparison = 0; const dstatement32_t *fte_restrict st; mfunction_t *fte_restrict newf; int i; edictrun_t *ed; eval_t *ptr; float *fte_restrict glob = pr_globals; float tmpf; int tmpi; eval_t *switchref = (eval_t*)glob; unsigned int num_edicts = sv_num_edicts; unsigned int op; #define OPA ((eval_t *)&glob[st->a]) #define OPB ((eval_t *)&glob[st->b]) #define OPC ((eval_t *)&glob[st->c]) #define INTSIZE 32 st = &pr_statements32[s]; while (progfuncs->funcs.debug_trace || prinst.watch_ptr || prinst.profiling) { #define DEBUGABLE #ifdef SEPARATEINCLUDES #include "execloop32d.h" #else #include "execloop.h" #endif #undef DEBUGABLE } while(1) { #ifdef SEPARATEINCLUDES #include "execloop32.h" #else #include "execloop.h" #endif } #undef INTSIZE #endif } /* ==================== PR_ExecuteProgram ==================== */ static void PR_ExecuteCode (progfuncs_t *progfuncs, int s) { int runaway; if (prinst.watch_ptr && prinst.watch_ptr->_int != prinst.watch_old._int) { switch(prinst.watch_type) { case ev_float: externs->Printf("Watch point \"%s\" changed by engine from %g to %g.\n", prinst.watch_name, prinst.watch_old._float, prinst.watch_ptr->_float); break; case ev_vector: externs->Printf("Watch point \"%s\" changed by engine from '%g %g %g' to '%g %g %g'.\n", prinst.watch_name, prinst.watch_old._vector[0], prinst.watch_old._vector[1], prinst.watch_old._vector[2], prinst.watch_ptr->_vector[0], prinst.watch_ptr->_vector[1], prinst.watch_ptr->_vector[2]); break; default: externs->Printf("Watch point \"%s\" changed by engine from %i to %i.\n", prinst.watch_name, prinst.watch_old._int, prinst.watch_ptr->_int); break; case ev_entity: externs->Printf("Watch point \"%s\" changed by engine from %i(%s) to %i(%s).\n", prinst.watch_name, prinst.watch_old._int, PR_GetEdictClassname(progfuncs, prinst.watch_old._int), prinst.watch_ptr->_int, PR_GetEdictClassname(progfuncs, prinst.watch_ptr->_int)); break; case ev_function: case ev_string: externs->Printf("Watch point \"%s\" set by engine to %s.\n", prinst.watch_name, PR_ValueString(progfuncs, prinst.watch_type, prinst.watch_ptr, false)); break; } prinst.watch_old = *prinst.watch_ptr; //we can't dump stack or anything, as we don't really know the stack frame that it happened in. //stop watching // prinst->watch_ptr = NULL; } #ifdef QCJIT if (current_progstate->jit) { PR_EnterJIT(progfuncs, current_progstate->jit, s); return; } #endif runaway = 100000000; for(;;) { switch (current_progstate->structtype) { case PST_DEFAULT: case PST_QTEST: s = PR_ExecuteCode16(progfuncs, s, &runaway); if (s == -1) return; continue; case PST_KKQWSV: case PST_FTE32: case PST_UHEXEN2: s = PR_ExecuteCode32(progfuncs, s, &runaway); if (s == -1) return; continue; default: externs->Sys_Error("PR_ExecuteProgram - bad structtype"); } } } void PDECL PR_ExecuteProgram (pubprogfuncs_t *ppf, func_t fnum) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; mfunction_t *f; int i; unsigned int initial_progs; int oldexitdepth; int s; #ifndef QCGC int tempdepth; #endif unsigned int newprogs = (fnum & 0xff000000)>>24; initial_progs = prinst.pr_typecurrent; if (newprogs != initial_progs) { if (newprogs >= prinst.maxprogs || !pr_progstate[newprogs].globals) //can happen with hexen2... { externs->Printf("PR_ExecuteProgram: tried branching into invalid progs (%#"pPRIx")\n", fnum); return; } PR_SwitchProgsParms(progfuncs, newprogs); } if (!(fnum & ~0xff000000) || (signed)(fnum & ~0xff000000) >= pr_progs->numfunctions) { // if (pr_global_struct->self) // ED_Print (PROG_TO_EDICT(pr_global_struct->self)); #if defined(__GNUC__) && !defined(FTE_TARGET_WEB) && !defined(NACL) externs->Printf("PR_ExecuteProgram: NULL function from exe (address %p)\n", __builtin_return_address(0)); #else externs->Printf("PR_ExecuteProgram: NULL function from exe\n"); #endif // Host_Error ("PR_ExecuteProgram: NULL function from exe"); // PR_MoveParms(0, pr_typecurrent); PR_SwitchProgs(progfuncs, initial_progs); return; } oldexitdepth = prinst.exitdepth; f = &pr_cp_functions[fnum & ~0xff000000]; if (f->first_statement < 0) { // negative statements are built in functions i = -f->first_statement; if (i < externs->numglobalbuiltins) (*externs->globalbuiltins[i]) (&progfuncs->funcs, (struct globalvars_s *)current_progstate->globals); else { externs->Printf ("Bad builtin call number %i (from exe)\n", -f->first_statement); // PR_MoveParms(p, pr_typecurrent); PR_SwitchProgs(progfuncs, initial_progs); } PR_SwitchProgsParms(progfuncs, initial_progs); return; } //forget about any tracing if its active. control returning to the engine should not look like its calling some random function. progfuncs->funcs.debug_trace = DEBUG_TRACE_OFF; // make a stack frame prinst.exitdepth = prinst.pr_depth; s = PR_EnterFunction (progfuncs, f, initial_progs); #ifndef QCGC tempdepth = prinst.numtempstringsstack; #endif PR_ExecuteCode(progfuncs, s); PR_SwitchProgsParms(progfuncs, initial_progs); #ifndef QCGC PR_FreeTemps(progfuncs, tempdepth); prinst.numtempstringsstack = tempdepth; #else if (!prinst.pr_depth) PR_RunGC(progfuncs); #endif prinst.exitdepth = oldexitdepth; } typedef struct { int fnum; int progsnum; int statement; } qcthreadstack_t; typedef struct qcthread_s { int fstackdepth; qcthreadstack_t fstack[MAX_STACK_DEPTH]; int lstackused; int lstack[LOCALSTACK_SIZE]; int xstatement; int xfunction; progsnum_t xprogs; } qcthread_t; struct qcthread_s *PDECL PR_ForkStack(pubprogfuncs_t *ppf) { //QC code can call builtins that call qc code. //to get around the problems of restoring the builtins we simply don't save the thread over the builtin. progfuncs_t *progfuncs = (progfuncs_t*)ppf; int i, l; int ed = prinst.exitdepth; int localsoffset, baselocalsoffset; qcthread_t *thread = externs->memalloc(sizeof(qcthread_t)); const mfunction_t *f; //copy out the functions stack. for (i = 0,localsoffset=0; i < ed; i++) { if (i+1 == prinst.pr_depth) f = prinst.pr_xfunction; else f = prinst.pr_stack[i+1].f; localsoffset += f->locals; //this is where it crashes } baselocalsoffset = localsoffset; for (i = ed; i < prinst.pr_depth; i++) { thread->fstack[i-ed].fnum = prinst.pr_stack[i].f - pr_progstate[prinst.pr_stack[i].progsnum].functions; thread->fstack[i-ed].progsnum = prinst.pr_stack[i].progsnum; thread->fstack[i-ed].statement = prinst.pr_stack[i].s; if (i+1 == prinst.pr_depth) f = prinst.pr_xfunction; else f = prinst.pr_stack[i+1].f; localsoffset += f->locals; } thread->fstackdepth = prinst.pr_depth - ed; for (i = prinst.pr_depth - 1; i >= ed ; i--) { if (i+1 == prinst.pr_depth) f = prinst.pr_xfunction; else f = prinst.pr_stack[i+1].f; localsoffset -= f->locals; for (l = 0; l < f->locals; l++) { thread->lstack[localsoffset-baselocalsoffset + l ] = ((int *)pr_globals)[f->parm_start + l]; ((int *)pr_globals)[f->parm_start + l] = prinst.localstack[localsoffset+l]; //copy the old value into the globals (so the older functions have the correct locals. } } for (i = ed; i < prinst.pr_depth ; i++) //we need to get the locals back to how they were. { if (i+1 == prinst.pr_depth) f = prinst.pr_xfunction; else f = prinst.pr_stack[i+1].f; for (l = 0; l < f->locals; l++) { ((int *)pr_globals)[f->parm_start + l] = thread->lstack[localsoffset-baselocalsoffset + l]; } localsoffset += f->locals; } thread->lstackused = localsoffset - baselocalsoffset; thread->xstatement = prinst.pr_xstatement; thread->xfunction = prinst.pr_xfunction - current_progstate->functions; thread->xprogs = prinst.pr_typecurrent; return thread; } void PDECL PR_ResumeThread (pubprogfuncs_t *ppf, struct qcthread_s *thread) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; mfunction_t *f, *oldf; int i,l,ls, olds; progsnum_t initial_progs; int oldexitdepth; int *glob; int s; #ifndef QCGC int tempdepth; #endif progsnum_t prnum = thread->xprogs; int fnum = thread->xfunction; if (prinst.localstack_used + thread->lstackused > LOCALSTACK_SIZE) PR_RunError(&progfuncs->funcs, "Too many locals on resumtion of QC thread\n"); if (prinst.pr_depth + thread->fstackdepth > MAX_STACK_DEPTH) PR_RunError(&progfuncs->funcs, "Too large stack on resumtion of QC thread\n"); //do progs switching stuff as appropriate. (fteqw only) initial_progs = prinst.pr_typecurrent; PR_SwitchProgsParms(progfuncs, prnum); oldexitdepth = prinst.exitdepth; prinst.exitdepth = prinst.pr_depth; ls = 0; //add on the callstack. for (i = 0; i < thread->fstackdepth; i++) { if (prinst.pr_depth == prinst.exitdepth) { prinst.pr_stack[prinst.pr_depth].f = prinst.pr_xfunction; prinst.pr_stack[prinst.pr_depth].s = prinst.pr_xstatement; prinst.pr_stack[prinst.pr_depth].progsnum = initial_progs; } else { prinst.pr_stack[prinst.pr_depth].progsnum = thread->fstack[i].progsnum; prinst.pr_stack[prinst.pr_depth].f = pr_progstate[thread->fstack[i].progsnum].functions + thread->fstack[i].fnum; prinst.pr_stack[prinst.pr_depth].s = thread->fstack[i].statement; } if (i+1 == thread->fstackdepth) { f = &pr_cp_functions[fnum]; glob = (int*)pr_globals; } else { f = pr_progstate[thread->fstack[i+1].progsnum].functions + thread->fstack[i+1].fnum; glob = (int*)pr_progstate[thread->fstack[i+1].progsnum].globals; } for (l = 0; l < f->locals; l++) { prinst.localstack[prinst.localstack_used++] = glob[f->parm_start + l]; glob[f->parm_start + l] = thread->lstack[ls++]; } prinst.pr_depth++; } if (ls != thread->lstackused) PR_RunError(&progfuncs->funcs, "Thread stores incorrect locals count\n"); f = &pr_cp_functions[fnum]; // thread->lstackused -= f->locals; //the current function is the odd one out. //add on the locals stack memcpy(prinst.localstack+prinst.localstack_used, thread->lstack, sizeof(int)*thread->lstackused); prinst.localstack_used += thread->lstackused; //bung the locals of the current function on the stack. // for (i=0 ; i < f->locals ; i++) // ((int *)pr_globals)[f->parm_start + i] = 0xff00ff00;//thread->lstack[thread->lstackused+i]; // PR_EnterFunction (progfuncs, f, initial_progs); oldf = prinst.pr_xfunction; olds = prinst.pr_xstatement; prinst.pr_xfunction = f; s = thread->xstatement; #ifndef QCGC tempdepth = prinst.numtempstringsstack; #endif PR_ExecuteCode(progfuncs, s); PR_SwitchProgsParms(progfuncs, initial_progs); #ifndef QCGC PR_FreeTemps(progfuncs, tempdepth); prinst.numtempstringsstack = tempdepth; #endif prinst.exitdepth = oldexitdepth; prinst.pr_xfunction = oldf; prinst.pr_xstatement = olds; } void PDECL PR_AbortStack (pubprogfuncs_t *ppf) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; while(prinst.pr_depth > prinst.exitdepth) PR_LeaveFunction(progfuncs); prinst.pr_xstatement = -1; //should make the loop abort. } pbool PDECL PR_GetBuiltinCallInfo (pubprogfuncs_t *ppf, int *builtinnum, char *function, size_t sizeoffunction) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; int st = prinst.pr_xstatement; int op; int a; const char *fname; switch (current_progstate->structtype) { case PST_DEFAULT: case PST_QTEST: op = pr_statements16[st].op; a = pr_statements16[st].a; break; case PST_KKQWSV: case PST_FTE32: op = pr_statements32[st].op; a = pr_statements32[st].a; break; default: op = OP_DONE; a = 0; break; } *builtinnum = 0; *function = 0; if ((op >= OP_CALL0 && op <= OP_CALL8) || (op >= OP_CALL1H && op <= OP_CALL8H)) { a = ((eval_t *)&pr_globals[a])->function; *builtinnum = -current_progstate->functions[a].first_statement; fname = PR_StringToNative(ppf, current_progstate->functions[a].s_name); strncpy(function, fname, sizeoffunction-1); function[sizeoffunction-1] = 0; return true; } return false; }